扫雷
1. 介绍
扫雷游戏想必小伙伴们都玩过,本质就是在一片由空地和雷组成的区域上,(当然我们看不到哪里是否有雷,在玩家看来,都是空地),玩家一个一个的排查坐标,倘如该坐标是雷,则游戏失败。倘若该坐标不是雷,则在在坐标显示其上下左右以及对角区域(加起来八个坐标)含有的雷的个数。
2. 设计思路
我的设计是这样的:
游戏开始之时,打印一个方格区域,当然在该区域上玩家只能看到全部都是空白,然后玩家输入要排查的雷的坐标,该坐标是雷则游戏失败,该区域不是类则显示在该坐标附近的雷的个数,附近没有则显示0个。
那接下来就以9*9的扫雷游戏为例,首先创建两个二维字符数组。一个用于埋雷(不展示给玩家看),另一个一个用于显示给玩家看(即在玩家要排查的坐标上附近的雷的个数)。那就把埋雷的数组取名为Mine吧,展示给玩家看的数组取名为Show。
我们初始化棋盘时,MIne数组用字符0进行初始化,也就是用字符0表示该坐标没有雷,后面设置雷的时候用字符1表示。Show数组用*进行初始化,即未被排查的坐标都用*表示。被排查的坐标显示该坐标周围的雷的个数。
注意:
在玩家排雷时,假设玩家输入的坐标是(假设设计的是9*9的棋盘)9行9列,那么在统计其周围的坐标时按理来讲应该要统计其上下左右以及对角处的坐标是雷与否,在9*9坐标是在数组的边界处,要将其周围8个位置全部访问必定会发生越界问题。所以干脆将数组的行增加两行,列也增加两列,变成11*11的棋盘,这样就可以避免越界问题了。虽然变成11*11的棋盘,但是我们还是使用中间的9*9的部分。
3. 逻辑实现
3.1 棋盘的相关操作
【创建棋盘】
首先创建两个二维数组,这里仍然使用define定义的常量来控制数组的行数和列数。
#define EASY_COUNT 10//定义要埋的雷的个数 #define ROW 9//数组实际使用的行数 #define COL 9//数组实际使用的列数 #define ROWS ROW+2//数组实际的行数 #define COLS COL+2//数组实际的列数 //Mine数组用于记录实际的雷的情况 char Mine[ROWS][COLS] = { 0 }; //Show数组用于显示每个位置附近有多少个雷 char Show[ROWS][COLS] = { 0 };
3.2 埋雷
Mine数组用于埋雷,首先将其全部设置为字符0(即’0’),埋雷的地方设置为字符1(即’1’)
InitBoard(Mine,ROWS,COLS,'0');//‘0’代表该位置没有雷 InitBoard(Show,ROWS,COLS,'*');// ‘*’为了保持神秘感 //初始化时将字符串全部设置为set字符。 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < cols; j++) { board[i][j] = set; } } }
埋雷:埋雷首先设置雷的个数,再随机设置雷。
void SetMine(char board[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int count = EASY_COUNT;//要埋10颗雷 while (count > 0) { x = rand() % row + 1;//坐标范围是1-9 y = rand() % col + 1; if (board[x][y] == '0'); { board[x][y] = '1'; count--; } } }
3.3 打印棋盘
设置了雷之后就将棋盘打印出来看是否满足要求。
打印棋盘的时候可以顺便将行号和列号打印出来,方便玩家输入坐标。
void DisplayBoard(char board[ROWS][COLS], int row, int col) { printf("-----------------------------------\n"); int i = 0; for (i = 0; i <= col; i++) { printf("[%d] ", i);//提示当前是第几列 } printf("\n"); for (i = 1; i <= row; i++) { printf("[%d] ", i);//提示当前是第几行 int j = 0; for (j = 1; j <= col; j++) { printf(" %c ", board[i][j]); } printf("\n"); } }
3.4 排雷
排雷操作:
注意:
排雷函数在传参的时候传入的是实际使用的行数和列数
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)//排雷操作 { int x = 0; int y = 0; int win = 0;//记录排查的次数 while (row*col-EASY_COUNT > win) { printf("请输入你要排雷区域的坐标>:\n"); scanf("%d %d", &x, &y); //检测输入的坐标是否满足要求 if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) { if (Mine[x][y] == '1') { printf("你被炸死啦!哈哈哈哈\n"); DisplayBoard(Mine, row, col); break; } else { //搜索该坐标附近有多少雷 int count = GetMineCount(Mine, x, y); Show[x][y] = count + '0';//这里很细节 DisplayBoard(Show, ROW, COL); //每成功排一次雷 win就++ win++; } } else { printf("你tm眼瞎?cnm重新输!\n"); } } if (win == row * col - EASY_COUNT)//假如是个9*9的棋盘,埋了10颗雷。当win等于71的时候游戏胜利 { printf("恭喜你赢啦!\n"); } }
这里注意代码的第23行,这里一定要注意加上一个字符0,count数组算出的只是雷的个数,倘若将该count存放进入Show数组对应的坐标,就代表着将该ASCll码对应的字符存入其中,这显然不符合我们的需求。由前文提到,Show数组存放的字符,通过查阅可知字符0的ascll值是48,向后推理,字符1的ascll码就是49,会发现,0的ascll值加上一个数,那么得到的数字对应的字符就是加上的那个数(当然加上的这个数不能超过10),所以代码的第23行这样处理就比较妥当。
【小结】
将整个埋雷之后的逻辑设置成一个循环就可以达到扫雷游戏的效果
4. 代码展示
4.1 test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void game() { //Mine数组用于记录实际的雷的情况 char Mine[ROWS][COLS] = { 0 }; //Show数组用于显示每个位置附近有多少个雷 char Show[ROWS][COLS] = { 0 }; //分别对两个数组进行初始化 InitBoard(Mine,ROWS,COLS,'0');//‘0’代表该位置没有雷 InitBoard(Show,ROWS,COLS,'*');// ‘*’为了保持神秘感 //打印棋盘 //DisplayBoard(Show, ROW, COL); //DisplayBoard(Mine, ROW, COL); //开始布置雷 SetMine(Mine, ROW, COL); DisplayBoard(Mine, ROW, COL); //排雷 DisplayBoard(Show, ROW, COL); FindMine(Mine,Show,ROW,COL); //DisplayBoard(Mine, ROW, COL); } void menu() { printf("****************\n"); printf("****1. Play ****\n"); printf("****2. Exit ****\n"); printf("****************\n"); } int main() { srand((unsigned int)time(NULL)); int input = 0; do { menu(); printf("请输入你的选项>:\n"); scanf("%d", &input); switch (input) { case 1: //printf("玩游戏!\n"); game(); break; case 0: printf("退出游戏!\n"); break; default: printf("你输入的选择有误,请重新输入!\n"); break; } } while (input); return 0; }
4.2 game.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < cols; j++) { board[i][j] = set; } } } void DisplayBoard(char board[ROWS][COLS], int row, int col) { printf("-----------------------------------\n"); int i = 0; for (i = 0; i <= col; i++) { printf("[%d] ", i);//提示当前是第几列 } printf("\n"); for (i = 1; i <= row; i++) { printf("[%d] ", i);//提示当前是第几行 int j = 0; for (j = 1; j <= col; j++) { printf(" %c ", board[i][j]); } printf("\n"); } } void SetMine(char board[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int count = EASY_COUNT;//要埋10颗雷 while (count > 0) { x = rand() % row + 1;//坐标范围是1-9 y = rand() % col + 1; if (board[x][y] == '0'); { board[x][y] = '1'; count--; } } } void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0;//记录排查的次数 while (row*col-EASY_COUNT > win) { printf("请输入你要排雷区域的坐标>:\n"); scanf("%d %d", &x, &y); //检测输入的坐标是否满足要求 if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) { if (Mine[x][y] == '1') { printf("你被炸死啦!哈哈哈哈\n"); DisplayBoard(Mine, row, col); break; } else { //搜索该坐标附近有多少雷 int count = GetMineCount(Mine, x, y); Show[x][y] = count + '0'; DisplayBoard(Show, ROW, COL); //每成功排一次雷 win就++ win++; } } else { printf("你tm眼瞎?cnm重新输!\n"); } } if (win == row * col - EASY_COUNT) { printf("恭喜你赢啦!\n"); } } int GetMineCount(char Mine[ROWS][COLS], int x, int y) { return (Mine[x-1][y] + Mine[x-1][y-1] + Mine[x][y-1] + Mine[x+1][y-1] + Mine[x+1][y] + Mine[x+1][y+1] + Mine[x][y+1] + Mine[x-1][y+1] - 8* '0'); }
4.3 game.h
#pragma once #include<stdlib.h> #include<stdio.h> #include<time.h> #define EASY_COUNT 10 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 //初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set); //打印棋盘 void DisplayBoard(char board[ROWS][COLS], int row, int col); //布置雷 void SetMine(char board[ROWS][COLS], int row, int col); //排雷 void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col); //搜索该坐标附近有多少雷 int GetMineCount(char Mine[ROWS][COLS], int x, int y);
5. 完结
本章的内容就到这里啦,若有不足,欢迎评论区指正,最后,希望大佬们多多三连吧,下期见!