目录
简单地使用C语言实现扫雷小游戏,更多内容持续更新中~
首先,进入游戏不都会有菜单界面嘛,虽然我们现在用的还是“黑框框”,但是好歹也是在写游戏嘛,那就写个菜单函数吧。
目前也就游玩和退出两个选项。
- void Menu()
- {
- printf("********************\n");
- printf("***** 1.play *****\n");
- printf("***** 0.exit *****\n");
- printf("********************\n");
- }
主函数里面的大体框架如下:
- int main()
- {
- int input = 0;
- do
- {
- printf("------扫雷游戏------\n");
- Menu();
- printf("请选择:->");
- scanf("%d", &input);
- switch (input)
- {
- case 1:
- Game();
- break;
- case 0:
- break;
- default:
- printf("输入非法,请重新输入!\n");
- break;
- };
- printf("------扫雷游戏------\n");
-
- } while (input);
-
- return 0;
- }
使用do…while结构,input接收用户的选择,switch实现分支。
然后具体的游戏逻辑的实现交由函数game实现,实际上是将逻辑细分后封装成若干个函数后再在game函数中组织。
扫雷中点开一个格子后会“炸开一片”没有雷的区域,并且附近一圈内有雷的话会显示雷的个数,我们这里先不考虑“炸开一片”没有雷的区域的功能,改成点开一个格子若不是雷就显示附近一圈的雷的个数。
我们这里采用两个数组来存储数据,扫雷面板大小设置成9X9,一个数组用来存放布置好的雷的信息,而另外一个存放排查出雷的信息。
你说有没有一种可能,我是说可能,在排查点开的格子周围有几个雷的时候会发生数组越界。
为了便捷地解决问题,我们直接把数组变“肥”一圈,变成11X11,但是用和打印出来的的还是9X9大小的面板。
game.h中
- #define ROW 9
- #define COL 9
- #define ROWS ROW + 2
- #define COLS COL + 2
test.c中
- void Game()
- {
- //使用二维字符数组,用ROWS和COLS是为了防止越界
- char show[ROWS][COLS];//用于存放展示面板数据的数组
- char mine[ROWS][COLS];//用于存放雷的分布情况的数组
- }
对两个数组的要求有所不同
mine数组在没有布置雷的时候,放的都是'0'。
show数组在没有排查雷的时候,放的都是'*'。
那在设计初始化函数的时候就可以多设计一个参数用来传递要放入的元素。
- void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
- {
- int i = 0;
- int j = 0;
- for (i = 0; i <= rows; i++)
- {
- for (j = 0; j <= cols; j++)
- {
- board[i][j] = set;
- }
- }
- }
打印面板
写一个函数把想要打印的数组打印出来,要注意我们只需要打印9X9大小就可以了,因此数组下标也是从1开始使用而非0。
我们再给打印出来的面板带上坐标,这里我们用的坐标系和之前的有所不同。
- void DisplayBoard(char board[ROWS][COLS], int row, int col)
- {
- int i = 0;
- int j = 0;
- for (i = 0; i <= row; i++)
- {
- printf("%d", i);//打印出Y坐标
-
- for (j = 1; j <= col; j++)//注意j=1
- {
- //注意打印每一行时要在前面放一个空格
- //使得排版美观
- if (i == 0)
- printf(" %d", j);//打印出X坐标
- else
- printf(" %c", board[i][j]);
- }
- printf("\n");
- }
- }
打印出来的效果大概就是这样
mine数组一般不会打印出来,要是打印出来不就知道雷在哪了嘛,我们打印的是show数组。
这雷埋哪呢?埋在mine数组中。
雷的个数用宏常量表示,便于修改。
#define EASY_MINE 10
用循环布雷,每布下一个雷就让计数的变量-1,直到减为0为止。
其中雷的坐标用随机函数来指定,随后判断一下这个位置是否已经埋过雷,如果没埋过的话就埋雷,不然再判断下一个坐标。
- void SetMine(char board[ROWS][COLS], int row, int col)
- {
- int count = EASY_MINE;
- while (count)
- {
- int x = rand() % row + 1;
- int y = rand() % col + 1;
- //一定一定要注意这里是字符而不是数字
- if (board[x][y] == '0')
- {
- board[x][y] = '1';
- count--;
- }
- }
- }
让生成的随机坐标范围:x在[1,row],y在[1,col]之间
- int x = rand() % row + 1;
- int y = rand() % col + 1;
已排查的非雷的数目emptyCount小于总的非雷的数目我们就一直排雷,要是踩到雷了就打印一下雷的分布情况,然后游戏失败,而要是没踩到雷就显示周围一圈雷的个数并emptyCount+1。注意要对输入的坐标进行范围判断,还要防止重复排查同一坐标。跳出循环后,如果emptyCount的数量和总的非雷的数量相等的话就说明玩家获胜。
对一个坐标,在mine面板判断周围雷数,并把雷数放在show面板的对应坐标处显示。
- void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
- {
- int x = 0;
- int y = 0;
- int mineCount = 0;
- int emptyCount = 0;//已排查的非雷的数目
-
- while (emptyCount < row * col - EASY_MINE)
- {
- printf("请输入排查坐标:");
- scanf("%d %d", &x, &y);
- //对输入坐标进行越界判断
- if (x >= 1 && x <= row && y >= 1 && y <= col)
- {
- //防止重复排查同一坐标
- if (show[x][y] == '*')
- {
- if (mine[x][y] == '1')//踩到雷了
- {
- printf("这里是雷,你寄了!\n");
- printf("本局的布雷情况:\n");
- //输了以后打印一下雷的情况
- DisplayBoard(mine, ROW, COL);
- break;
- }
- else//未踩到雷,看看周围的雷数
- {
- mineCount = GetMineCount(mine, x, y);//得到周围雷数
- show[x][y] = mineCount + '0';//数字注意要变字符
- DisplayBoard(show, ROW, COL);//每次排完显示一次当前面板
- emptyCount++;
- }
- }
- else
- {
- printf("此处已排查过,请输入其他坐标!\n");
- }
- }
- else
- {
- printf("输入非法,请重新输入!\n");
- }
- }
- if (emptyCount == row * col - EASY_MINE)
- printf("赢了!\n");
- }
选中的格子周围一圈的雷数如何计算?mine数组里'1'为雷,'0'为空,那么只要把周围八个格子的数组值减去'0'加在一起不就得出了雷的个数嘛。表示数字的字符想要转成对应的数字,比如'5'要转成5,只需要减去一个'0'即可,因为数字字符对应的值其实与数字不一样,'5'的ASCII码值为53而'0'的ASCII码值,相减就可以得到对应数字。
- int GetMineCount(char board[ROWS][COLS], int x, int y)
- {
- int i = 0;
- int j = 0;
- int mineCount = 0;
- for (i = -1; i <= 1; i++)
- {
- for (j = -1; j <= 1; j++)
- {
- //注意减去'0',字符变数字
- mineCount += board[x + i][y + j] - '0';
- }
- }
- return mineCount;
- }
- #include"game.h"
- /*
- 函数名:Menu
- 功能:打印菜单
- 参数:无
- 返回值:无
- */
-
- void Menu()
- {
- printf("********************\n");
- printf("***** 1.play *****\n");
- printf("***** 0.exit *****\n");
- printf("********************\n");
- }
-
- /*
- 函数名:Game
- 功能:运行一轮游戏
- 参数:无
- 返回值:无
- */
-
- void Game()
- {
- //使用二维字符数组,用ROWS和COLS是为了防止越界
- char show[ROWS][COLS];//用于存放展示面板数据的数组
- char mine[ROWS][COLS];//用于存放雷的分布情况的数组
-
- //初始化数组信息
- InitBoard(mine, ROWS, COLS, '0');
- InitBoard(show, ROWS, COLS, '*');
-
- //打印扫雷界面
- DisplayBoard(show, ROW, COL);
-
- //向面板布置雷
- SetMine(mine, ROW, COL);
-
- //用户排查雷
- FineMine(mine, show, ROW, COL);
-
-
- }
-
- int main()
- {
- srand((unsigned)time(NULL));//设置随机数种子
- int input = 0;
- do
- {
- printf("------扫雷游戏------\n");
- Menu();
- printf("请选择:->");
- scanf("%d", &input);
- switch (input)
- {
- case 1:
- Game();
- break;
- case 0:
- break;
- default:
- printf("输入非法,请重新输入!\n");
- break;
- };
- printf("------扫雷游戏------\n");
-
-
- } while (input);
-
- return 0;
- }
- #include<stdio.h>
- #include<stdlib.h>
- #include<time.h>
-
- #define ROW 9
- #define COL 9
- #define ROWS ROW + 2
- #define COLS COL + 2
- #define EASY_MINE 10
-
- 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 FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
- #include"game.h"
-
- /*
- 函数名:InitBoard
- 功能:初始化二维字符数组全部元素为某一字符
- 参数:(char [][])目标二维数组board[ROWS][COLS],(int)要初始化范围的行数row和列数col, (char)要设置的字符set
- 返回值:无
- */
-
- void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
- {
- int i = 0;
- int j = 0;
- for (i = 0; i <= rows; i++)
- {
- for (j = 0; j <= cols; j++)
- {
- board[i][j] = set;
- }
-
- }
- }
-
- /*
- 函数名:DisplayBoard
- 功能:打印二维数组
- 参数:(char [][])目标二维数组board[ROWS][COLS],(int)行数row和列数col
- 返回值:无
- */
-
- void DisplayBoard(char board[ROWS][COLS], int row, int col)
- {
-
- int i = 0;
- int j = 0;
- for (i = 0; i <= row; i++)
- {
- printf("%d", i);//打印出Y坐标
- for (j = 1; j <= col; j++)//注意j=1
- {
- //注意打印每一行时要在前面放一个空格
- //使得排版美观
- if (i == 0)
- printf(" %d", j);//打印出X坐标
- else
- printf(" %c", board[i][j]);
- }
- printf("\n");
- }
- }
-
- /*
- 函数名:SetMine
- 功能:在棋盘布置雷
- 参数:(char [][])目标二维数组board[ROWS][COLS],(int)行数row和列数col
- 返回值:无
- */
-
- void SetMine(char board[ROWS][COLS], int row, int col)
- {
- int count = EASY_MINE;
- while (count)
- {
- int x = rand() % row + 1;
- int y = rand() % col + 1;
- //一定一定要注意这里是字符而不是数字
- if (board[x][y] == '0')
- {
- board[x][y] = '1';
- count--;
- }
- }
- }
-
- /*
- 函数名:GetMineCount
- 功能:在mine棋盘中对坐标周围八格进行雷数统计并返回对应值
- 参数:(char [][])目标二维数组mine[ROWS][COLS]和show[ROWS][COLS],(int)X坐标x和Y坐标y
- 返回值:(int)坐标周围八格的雷数
- */
-
- int GetMineCount(char board[ROWS][COLS], int x, int y)
- {
- int i = 0;
- int j = 0;
- int mineCount = 0;
- for (i = -1; i <= 1; i++)
- {
- for (j = -1; j <= 1; j++)
- { //注意减去'0',字符变数字
- mineCount += board[x + i][y + j] - '0';
- }
- }
- return mineCount;
- }
-
- /*
- 函数名:FineMine
- 功能:在mine棋盘排雷,把周围八格的雷数显示在show面板对应坐标处
- 参数:(char [][])目标二维数组mine[ROWS][COLS]和show[ROWS][COLS],(int)行数row和列数col
- 返回值:无
- */
-
- void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
- {
- int x = 0;
- int y = 0;
- int mineCount = 0;
- int emptyCount = 0;//已排查的非雷的数目
-
- while (emptyCount < row * col - EASY_MINE)
- {
- printf("请输入排查坐标:");
- scanf("%d %d", &x, &y);
- //对输入坐标进行越界判断
- if (x >= 1 && x <= row && y >= 1 && y <= col)
- {
- //防止重复排查同一坐标
- if (show[x][y] == '*')
- {
- if (mine[x][y] == '1')//踩到雷了
- {
- printf("这里是雷,你寄了!\n");
- printf("本局的布雷情况:\n");
- //输了以后打印一下雷的情况
- DisplayBoard(mine, ROW, COL);
- break;
- }
- else//看看周围的雷数
- {
- mineCount = GetMineCount(mine, x, y);
- show[x][y] = mineCount + '0';//数字注意要变字符
- DisplayBoard(show, ROW, COL);//每次排完显示一次当前面板
- emptyCount++;
- }
- }
- else
- {
- printf("此处已排查过,请输入其他坐标!\n");
-
- }
- }
- else
- {
- printf("输入非法,请重新输入!\n");
- }
- }
- if (emptyCount == row * col - EASY_MINE)
- printf("赢了!\n");
- }