目录
我已经把我的代码上传到了我的Gitee的仓库中了,需要的可自取
仓库 - 流云luoxiao (liuyun-luo) - Gitee.com
准备工作
第一步:打印菜单
第二步:定义数组和初始化
第三步:布雷
第三步:打印show和mine数组
第四步:标记和取消标记
标记
取消标记
第五步:排雷
1.判断输入的坐标是否合法
2.判断坐标是否被排除过和该坐标是否是雷
3.递归排雷
递归结束的条件
递归函数参数的设计
递归函数的递归方式(我用的是十字递归(即递归该坐标的上下左右))
第六步:用switch语句将标记,取消标记和排雷放在一起
第七步:判断是否胜利
最后一步:将所有函数放在game函数中(就可以玩了)
玩一玩
首先为了便于代码的维护与管理,我们将扫雷的所有代码分成三个部分
①game.h:用来存放扫雷函数声明、库函数的头文件、以及#define定义的宏常量等
②game.c:用来存放扫雷函数的实现的代码
③test.c:用来存放main函数,以及对扫雷函数的测试
包含在game.h中包含头文件stdio.h,并在game.c和test.c中包函我们自己的头文件game.h(注意包含自己的头文件的时候头文件不用<>,而用“”,即#include“game.h”)
①分装一个函数menu,用以下的代码(加这么多*是为了让菜单更好看一点捏)
②实现对菜单选项的响应,定义一个变量,接收从键盘传来的值,因为菜单至少得打印一次所以用do while循环,因为选项可能出现多种情况,所以使用switch语句
③根据菜单的情况,在switch语句中写对应的代码
①定义两个二维数组,一个mine,一个show(为什么定义两个数组?因为扫雷的时候,总得给别人看一个要扫雷的区域吧,只有一个数组+printf会很麻烦)mine用来存放雷,show用来存储标记和一个坐标周围雷的个数。
②数组的行列,扫雷的时候,扫一个位置,该位置就会显示它周围一圏雷的个数,既然是周围的一圏,那么处理角/边缘上的位置扫的时候就要分类讨论,这样太麻烦,不如直接再给数组大一圏(例9x9的扫雷区域,就建11x11的数组[注意10X10的数组不能包围9x9的区域一圏])这样只要控制布雷的区域为9x9就可以。
扫雷区域是9x9
mine[11][11]
show[9][9](show也可以建11x11的行列,根据个人习惯)
③数组的行列都要用#define来定义,并把定义放在game.h中,这样以后的时候想要改变扫雷的区域的大小,就只需要改变#define后面的行,列的值就可以了
④mine数组全部出初始化为0/字符0/其他(可以根据自己的喜好。)
代码
void MineInit(char mine[NLIL][NCOL])
{
int i = 0, j = 0;
for (i = 0; i < NLIL; i++)
for (j = 0; j < NCOL; j++)
mine[i][j] = 0;
}
show数组全部初始化为*(或者自己的喜欢任意字符)
//初始化无雷数组
void ShowInit(char show[LIL][COL])
{
int i = 0, j = 0;
for (i = 0; i < LIL; i++)
for (j = 0; j < COL; j++)
show[i][j] = '*';
}
利用srand函数,rand函数,time函数制造随机数并限制随机数的范围,把随机数赋给X(二维数组的行)y(二维数组的列)
1.将srand函数放在main函数的最开头(一般这样)设置随机数的种子(代码为srand((unsigned int)time(NULL),利用时间戳随时间变化而变化的特点,让随机数的种子不断变化,以达到真随机的效果。
srand的()中的参数为随机数的种子,参数类型为unsigned int,如果种子固定为一个值(不论是任何一个unsigned int的值)那么rand生成的随机数只会生成一份,之后无论再运行程序多少次,rand生成的随机数还是和第一次生成的随机数相同.
2.把rand生成的随机数赋给X(二维数组的行)y(二维数组的列)
//布雷
void Mine(char mine[NLIL][NCOL])
{
int count = 0;
while (count < MINE)
{
int x = rand() % (NLIL - 2) + 1;
int y = rand() % (NCOL - 2) + 1;
if(mine[x][y]==0)//无雷的地方才布雷
{
mine[x][y] = 1;
count++;
}
}
}
mine数组的打印用于测试代码/玩家失败和胜利时让玩家知道雷在哪。
为了便于玩家的游玩,我们可以给show数组加上行号和列号。
show数组的打印
void ShowPrint(char show[LIL][COL])
{
printf("—————————扫雷——————————————\n");
int z = 0;
for (z = 0; z <= COL; z++)//打印列号
printf("%d|", z);
printf("\n");
int i = 0, j = 0;
for (i = 0; i < LIL; i++)
{
for (j = 0; j < COL; j++)
{
if (j == 0)
{
printf("%d|", i + 1);//打印行号
printf("%c ", show[i][j]);
}
else
{
printf("%c ", show[i][j]);
}
}
printf("\n");
}
printf("—————————扫雷——————————————\n");
}
效果
mine数组的打印
//打印有雷数组
void MinePrint(char mine[NLIL][NCOL])
{
int i = 0, j = 0;
for (i = 1; i < NLIL - 1; i++)
{
for (j = 1; j < NCOL - 1; j++)
printf("%d ", mine[i][j]);
printf("\n");
}
1.判断输入的坐标是否合法(可以在排雷的函数中限制,也可以在排雷函数外,game函数中限制,我这里是在game函数中限制的)
2.判断坐标是否被排除过/被标记过
void mark(char show[LIL][COL],int lil,int col)
{
int x = lil, y = col;
while (show[x - 1][y - 1] == 'X')
{
printf("该坐标已经被标记过了,请换一个坐标吧:");
scanf("%d %d", &x, &y);
}
while (show[x - 1][y - 1] != 'X'&&show[x - 1][y - 1] != '*')
{
printf("该坐标已经被排除过了,请换一个坐标吧:");
scanf("%d %d", &x, &y);
}
show[x - 1][y - 1] = 'X';
}
判断输入的坐标是否合法(可以在排雷的函数中限制,也可以在排雷函数外,game函数中限制,我这里是在game函数中限制的)
void unmark(char show[LIL][COL],int lil,int col)
{
int x = lil, y = col;
while (show[x - 1][y - 1] != 'X')
{
printf("该坐标没有被标记过,请换一个坐标吧:");
scanf("%d %d", &x, &y);
}
show[x - 1][y - 1] = '*';
}
(可以在排雷的函数中限制,也可以在排雷函数外,game函数中限制,我这里是在game函数中限制的)
如果踩到雷就返回1,被函数外的game函数的flag接受,结束游戏
int MineClearance(char mine[NLIL][NCOL], char show[LIL][COL],int lil,int col)
{
int x = lil, y = col;
while (show[x - 1][y - 1] != '*'&&show[x - 1][y - 1] != 'X')
{
printf("该坐标已经被排除过了,请换一个坐标吧:");
scanf("%d %d", &x, &y);
}
if (mine[x][y] == 1)
{
printf("你踩到雷了,你失败了!\n");
MinePrint(mine);
return 1;
}
_MineClearance(mine, show, x, y,0);
return 0;
}
1.递归到的坐标周围有雷
2.递归到的坐标已经被排除过了
void _MineClearance(char mine[NLIL][NCOL], char show[LIL][COL], int lil, int col,int flag)
{
我是这样设计的lil是行,col是列,flag是根据递归到的坐标是否被排除过,而是否递归
如果被排除过就要结束递归,flag的值为1;果没被排除过,就不需要递归,flag的值为0;
//排雷的递归函数
void _MineClearance(char mine[NLIL][NCOL], char show[LIL][COL], int lil, int col,int flag)
{
if (flag)//该坐标如果已经被排除过,就结束递归
return;
int flag1 = 0;
int x = lil, y = col;
int count = 0;
int i = 0, j = 0;
if (x - 1 < LIL && y - 1 < COL && x - 1 >= 0 && y - 1 >= 0)//防止坐标越界
{
for (i = x - 1; i <= x + 1; i++)//计算该坐标周围有多少个雷
{
for (j = y - 1; j <= y + 1; j++)
{
if (mine[i][j] == 1)
{
count++;
flag1 = 1;//该坐标如果周围有雷就标记一下
}
}
}
if(flag1==1)
show[x - 1][y - 1] = count + '0';
else
show[x - 1][y - 1] = ' ';//如果没雷就放空格
}
if (flag1)//该坐标如果周围有雷就结束递归
{
return;
}
if (x - 1 < LIL && y - 1 < COL&&x-1>=0&&y-1>=0) //防止坐标越界
{
if (('0'< show[x - 1-1][y-1] && show[x - 1-1][y-1] < '9') || show[x - 1-1][y-1] == ' ')//坐标已经被排除过了也结束递归
_MineClearance(mine, show, x - 1, y,1); //结束递归
else
_MineClearance(mine, show, x - 1, y,0); //递归该坐标的上
if (('0' < show[x-1][y - 1-1] && show[x-1][y - 1-1] < '9') || show[x-1][y - 1-1] == ' ')
_MineClearance(mine, show, x, y - 1,1);
else
_MineClearance(mine, show, x, y - 1,0); //递归该坐标的左
if (('0' < show[x-1][y + 1-1] && show[x-1][y + 1-1] < '9') || show[x-1][y + 1-1] == ' ')
_MineClearance(mine, show, x, y + 1,1);
else
_MineClearance(mine, show, x, y+1,0); //递归该坐标的右
if (('0' < show[x + 1-1][y-1] && show[x + 1-1][y-1] < '9') || show[x + 1-1][y-1] == ' ')
_MineClearance(mine, show, x + 1, y,1);
else
_MineClearance(mine, show, x+1, y,0); //递归该坐标的下
}
else //坐标越界就结束递归
{
return;
}
}
while (1)
{
int flag = 0;
int a = 0;
ShowPrint(show);
do
{
printf("1:标记|2:取消标记|3:排雷\n");
scanf("%d", &a);
switch (a)
{
case 1:
printf("请输入你标记的坐标:");
scanf("%d %d", &lil, &col);
while (lil > LIL || lil<1 || col>COL || col < 1)
{
printf("坐标输入错误,请重新输入:");
scanf("%d %d", &lil, &col);
}
mark(show, lil, col);
ShowPrint(show);
break;
case 2:
printf("请输入取消标记的坐标:");
scanf("%d %d", &lil, &col);
while (lil > LIL || lil<1 || col>COL || col < 1)
{
printf("坐标输入错误,请重新输入:");
scanf("%d %d", &lil, &col);
}
unmark(show, lil, col);
ShowPrint(show);
break;
case 3:
printf("请输入您要排的坐标:");
scanf("%d %d", &lil, &col);
while (lil > LIL || lil<1 || col>COL || col < 1)
{
printf("坐标输入错误,请重新输入:");
scanf("%d %d", &lil, &col);
}
flag = MineClearance(mine, show, lil, col);
if (flag)
{
return;
}
ShowPrint(show);
break;
default:
printf("选择错误\n");
break;
}
if (iswin(show))
{
printf("\n恭喜你,你已经排除了所有雷!\n");
MinePrint(mine);
return;
}
} while (a);
}
}
利用剩余的标记和*的个数之和是否等于雷的个数判断是否胜利,如果等于就胜利。
int iswin(char show[LIL][COL])
{
int i = 0, j = 0;
int count = 0;
for (i = 0; i < LIL; i++)
{
for (j = 0; j < COL; j++)
{
if (show[i][j] != '*' &&show[i][j]!='X')
count++;
}
}
if (count==LIL*COL-MINE)
{
return 1;
}
else
{
return 0;
}
}
以上就是C语言实现扫雷的全部内容了,如果对您有帮助的话,希望您给我点点赞!