打开vs,新建两个c++文件,分别是test.c和game.c,再新建一个头文件game.h。
其中test.c实现主函数,game.h放函数的声明和一些宏定义,game.h放函数的实现
让我们开始吧!
先把源码拿出来供大家参考,详细解释在下面
test.c
#include"game.h"
void menu()
{
printf("************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("************************\n");
}
void game()
{
//创建棋盘对应的数组
char mine[ROWS][COLS];//存放布置好的雷的信息
char show[ROWS][COLS];//存放排查出的雷的信息
//初始化棋盘
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS,'*');
//打印棋盘
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL, EASY_CONUT);
//DisplayBoard(mine, ROW, COL);
//排查雷
FineMine(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请选择:>\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
game.c
#include"game.h"
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;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("-------------扫雷游戏-------------\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-------------扫雷游戏-------------\n");
}
void SetMine(char mine[ROWS][COLS], int row, int col, int count)
{
while (count)
{
//1 - 9
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
//x,y坐标处没有雷
mine[x][y] = '1';
count--;
}
}
}
//统计mine数组的x,y周围有几个雷
int GetMine(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';
}
//排雷
void FineMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
int win = 0;
while (win<row*col - EASY_CONUT)
{
printf("请输入要排查的坐标:>");
int x = 0;
int y = 0;
scanf("%d%d", &x, &y);
//1.坐标合法性
if (x >= 1 && x <= row&&y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, row, col);
break;
}
else
{
//2.该坐标是不是雷?不是雷,统计雷的个数
int count = GetMine(mine, x, y);
show[x][y] = count + '0';//存放数字字符
DisplayBoard(show, row, col);
win++;
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
if (win == row*col - EASY_CONUT)
{
printf("恭喜你,排雷成功!\n");
DisplayBoard(show, row, col);
}
}
game.h
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define EASY_CONUT 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);
//mine - 存放雷的信息
//count - 布置的雷的个数
void SetMine(char mine[ROWS][COLS],int row,int col,int count);
//排雷
void FineMine(char mine[ROWS][COLS],
char show[ROWS][COLS],
int row,
int col);
统计mine数组的x,y周围有几个雷
int GetMine(char mine[ROWS][COLS], int x, int y);
-----------------------------------------
游戏玩一次不够可以一直玩,所以首先我们要在主函数里面写一个do-while循环,使我们有个入口进入游戏,完整代码如下
int input = 0;
do
{
menu();
printf("请选择:>\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
这里我们将游戏菜单封装成一个menu函数,menu函数的代码如下
void menu()
{
printf("************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("************************\n");
}
选择1后我们可以进入游戏,定义一个game()函数,而最开始我们应该打印两个扫雷棋盘,一个是全是*的游戏棋盘,是我们之后用来玩的,另一个是由字符0组成的棋盘,是方便我们观察雷是否布置成功和雷是如何布置的。
这里遇到了一个问题,当我们在排棋盘边界上的雷的时侯,例如排左上角第一个雷,只能扫描出周围三个格子的情况,对于边界外的情况其实是未知的,因此我们在边上还需要一圈格子
我们以9×9的扫雷举例,我们应该创建一个11×11的棋盘,我们初始化11×11的棋盘,但我们显示在程序中是一个9×9的棋盘,这就完美地解决了上面的问题,当我们创建扫雷棋盘的时候,我们需要定义两个字符二维数组,一个用来玩,一个用来布置雷
用来玩的棋盘为show,布置雷的棋盘为mine
//创建棋盘对应的数组
char mine[ROWS][COLS];//存放布置好的雷的信息
char show[ROWS][COLS];//存放排查出的雷的信息
为了方便以后改动棋盘的尺寸而不仅仅是玩9×9的扫雷,我们先在game.h里写一下宏定义
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
回到game函数里,此时要初始化棋盘,为了方便之后的操作,我们把我们mine棋盘初始化为字符0,之后布置雷的时候把有雷的地方改成字符1
而show棋盘就是要玩的棋盘,我们玩的时候不知道雷的具体布置情况,所以我们把show棋盘初始化为字符*
具体代码如下
//初始化棋盘
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS,'*');
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(),注意我们打印的时候应该传9进去而不是11.
具体代码如下(为了方便看出棋盘坐标,我在以下的代码加入了一个简易的二维坐标系)
//打印棋盘
DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("-------------扫雷游戏-------------\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-------------扫雷游戏-------------\n");
}
完成打印棋盘的函数后,我们可以编译运行一下看看效果,效果如下图
完成棋盘的打印后,我们开始布置雷,定义一个SetMine()函数,EASY_COUNT为我们设置的雷的个数,在头文件中定义
//布置雷
SetMine(mine, ROW, COL, EASY_CONUT);
函数代码如下
void SetMine(char mine[ROWS][COLS], int row, int col, int count)
{
while (count)
{
//1 - 9
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
//x,y坐标处没有雷
mine[x][y] = '1';
count--;
}
}
}
其中x,y是设置的布置的雷坐标,布置的雷是随机布置,因此我们这里用了库函数rand(),但它的返回值是一个伪随机值,不够随机,因此我们应在main()函数的最开头加上这么一句,以实现真正的随机,
srand((unsigned int)time(NULL));
在头文件中我们可以修改宏定义来调整布置的雷的参数,这里以布置10个雷为例,布置雷的时候我们还要进行一下判断,该格子不是雷(字符0)才能布置雷,
设置一个while循环,当成功布置一个雷,count--;
当雷布置完后,count=0,跳出循环
随机的布置完雷后我们可以再调用一下DisplayBoard函数来看看布置雷完后的效果
//布置雷
SetMine(mine, ROW, COL, EASY_CONUT);
DisplayBoard(mine, ROW, COL);
可以看到其中的1就是我们随机布置的雷
布置完雷之后的下一步就要开始排雷了,这里我们再定义一个排雷函数FineMine函数
//排查雷
FineMine(mine, show, ROW, COL);
//排雷
void FineMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
int win = 0;
while (win<row*col - EASY_CONUT)
{
printf("请输入要排查的坐标:>");
int x = 0;
int y = 0;
scanf("%d%d", &x, &y);
//1.坐标合法性
if (x >= 1 && x <= row&&y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, row, col);
break;
}
else
{
//2.该坐标是不是雷?不是雷,统计雷的个数
int count = GetMine(mine, x, y);
show[x][y] = count + '0';//存放数字字符
DisplayBoard(show, row, col);
win++;
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
if (win == row*col - EASY_CONUT)
{
printf("恭喜你,排雷成功!\n");
DisplayBoard(show, row, col);
}
}
思路:输入你认为不是雷的坐标,输入坐标后,要进行判断,
判断该坐标的合法性(以9×9为例,判断输入的坐标范围是否在9×9的范围内),若坐标非法,则提示重新输入,然后循环,输入。
若坐标合法,则判断该坐标是否是雷,若是雷,则游戏结束,展示整个雷盘并跳出循环。
若该坐标不是雷,则需要统计雷的个数,并将周围雷的个数展示在该坐标上
这里再定义一个用来统计雷的函数GetMine()
//统计mine数组的x,y周围有几个雷
int GetMine(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';
}
思路:在前面初始化棋盘和布置棋盘的时候,我们采用的方法是此坐标不是雷,则为字符0,此坐标是雷,则为字符1
这种方法的好处在于方便我们这一步的统计雷,我们可以计算坐标x,y周围8个格子里的字符数字之和,然后减掉8×字符0,返回的值便是该坐标周围8个格子的藏雷数
但是这是棋盘是一个字符数组,我们再把返回的值加上字符0,便得到该值的数字字符,然后我们可以调用DisplayBoard函数来展示棋盘,每输入一次坐标展示一次棋盘,形成一个循环。
那么问题又来了,循环什么时候停止呢,
我写的这个扫雷程序,赢的规则是把棋盘上所有不是雷的坐标都以输入的方式排查出来,换句话说,就是当你输入的坐标个数等于布置的雷的个数,就说明排雷成功,
因此这里我们可以初始化一个变量win=0,每成功输入一个非雷的坐标,win++,
而循环的判断条件便是win的值和(棋盘格子总数-布置的雷的个数)的值进行比较,当win<总数-雷的个数时,说明雷还没排完,循环继续,
当最后一次完成排雷,两值相等,排雷完毕,跳出循环,进行一次两个值的比较判断(因为跳出循环也有可能是被雷炸死,而被雷炸死时两值不相等,所以有必要进行值的比较判断),
若两值相等,便显示排雷成功,然后再调用DisplayBoard函数展示一下你成功排完雷的棋盘。
-----------------------------分割线-----------------------------
一切就绪后,我们再把game函数中的DisplayBoard(mine, ROW, COL)和注释掉,如下:
void game()
{
//创建棋盘对应的数组
char mine[ROWS][COLS];//存放布置好的雷的信息
char show[ROWS][COLS];//存放排查出的雷的信息
//初始化棋盘
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS,'*');
//打印棋盘
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL, EASY_CONUT);
//DisplayBoard(mine, ROW, COL);
//排查雷
FineMine(mine, show, ROW, COL);
}
这样布置雷的棋盘就不会显示出来了,我们就可以开始玩了
为了方便演示效果,我把雷的个数改成了80个
编译运行结果如下
我们可以看到,81个格子中只有坐标(6,5)不是雷,这便是扫雷游戏啦!
新人创作,若有意见和建议可在评论区提出,有不明白的地方也可以私信我探讨