前言:
如果是第一次看,可以去看博主的上一篇基础版本的扫雷渡劫篇:用c语言简易实现扫雷游戏,超详细!附图解!包看包会!看完你也能写!✿-CSDN博客
二者是紧密连在一起的~
扫雷拓展
在上一篇中,我们已经完成了简易版的扫雷,但是感觉和我们幼时玩的似乎不太一样哈~
从图中我们可以发现,在我们输入一个坐标的时候,如果当前位置九宫格方位都没有雷,它只会显示出0这个数字,但我们游玩正常的扫雷的时候,倘若该地方无雷,会显示出一篇无雷的区域,就像这样。
那这个是怎么实现的呢?这就需要用到递归的知识,今天采花贼就给大家详细介绍如何实现这种扫雷。
代码实现以及思路
在上一篇的代码中,我们已经实现了可以返会以一个位置为中心,再以九宫格扫描的方式去获取到雷的个数的getminecount函数,如果有雷就返回雷的个数,没有就返回0。
那聪明的宝子肯定能意识到今们的递归函数肯定也要使用我们getminecount函数,这里我们首先先定义一个递归查找函数refindmine函数。
注意:1.为了游戏更具有观赏性,这里博主会将show图没有雷的位置赋值成空格,也就是将0变成空格。
2.扫描时以当前位置为中心进行九宫格的方式进行扫描
传入参数如下
厉害的小伙伴注意到了我们这里传了一个int* win,这里为了不影响大家的思路,不做解释,往后看便知
1.递归思路
前提:在递归中我们会使用getminecount函数,倘若getminecount函数的返回值是0,也就代表该以该点为中心进行九宫格扫描时没有发现雷,这时候我们就要进行递归探索。
当然在我们进行递归的时候,我们首先要判断递归结束的条件,不然的话,嘿嘿嘿……
递归结束条件
这里我们递归结束的条件有两个
1.当我们的递归的过程中碰到了空格,停止递归
原因:空格的地方代表我们已经扫描过了,没有必要再去扫描。
2.当我们递归的时候扫描出来了雷,停止递归。
原因:这里我们是让每个格子进行递归扫描,在递归的途中我们会调用getminecount函数,去寻找以当前格子位置为中心的九宫格方位是否存在雷,如果存在雷,我们就会返回一个值给当前格子,这个位置也就不再是空格,是有数字的,不符合我们递归进入的条件。
知道了这些我们就可以进行代码的编写了。
递归代码
void refindmine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, int x, int y,int* win)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断边界
{
if (show[x][y] == ' ')
{
return;//遇到了空格,结束递归
}
else if (getminecount(mine, x, y) != 0)//扫描到了雷,结束递归
{
show[x][y] = getminecount(mine, x, y) + '0';
(*win)++;///遇到数字了,加加一次
return;
}
show[x][y] = ' ';//将未扫描到雷的地方赋值成空格
(*win)++;
refindmine(show, mine, row, col, x - 1, y - 1,win);//九宫格扫描有八个方向,所以我们进行八次递归
refindmine(show, mine, row, col, x - 1, y,win);
refindmine(show, mine, row, col, x - 1, y + 1,win);
refindmine(show, mine, row, col, x, y - 1,win);
refindmine(show, mine, row, col, x, y + 1,win);
refindmine(show, mine, row, col, x + 1, y - 1,win);
refindmine(show, mine, row, col, x + 1, y,win);
refindmine(show, mine, row, col, x + 1, y + 1,win);
}
}
递归效果
如果你在运行的时候出现了问题,请在评论区提出,博主会收集起来,然后去解决,去完善扫雷。
1.判断胜利的思路
注意:之前我们的win代表的是我们每输入一个不是雷的坐标,win就会加加一次,但这次我们使用了递归,这种方法就无法使用。
那我们怎么判断胜利的条件呢?
其实这里与之前一样,同样是win < row * col - mine_count,但是!我们这里的win代表的是我们所有非雷的地方,也就是我们的win的数量等于我们show图中空白格的数量加上我们数字格的数量。
这时候就可以解答refindmine函数中为什么传入了int* win的问题了,如图所示
代码实现
void findmine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int win = 0;
int x, y = 0;///初始化要排查的坐标
while (win < row * col - mine_count)
{
printf("请输入您要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)///控制排查的位置在图内
{
if (mine[x][y] == '1')
{
printf("你被炸死了");
displayboard(mine, ROW, COL);///打印出我们布置雷的图,让玩家失败后知道雷的位置
break;
}
else///该坐标不是雷,所以要统计周围有几个雷
{
refindmine(show, mine, ROW, COL, x, y,&win);
displayboard(show, ROW, COL);//要再次打印出我们的show图,因为我们排查的位置的数字已经发生变化。
if (win == row * col - mine_count)
{
printf("恭喜您扫雷成功");
break;
}
}
}
else
{
printf("坐标错误,请重新输入\n");
}
}
}
注意:这里我们运用了指针的相关知识,通过指针使我们的win能够肆无忌惮飞驰在其他函数中
(偷偷插一嘴,有一说一,指针是真的)
这里还有个地方要注意
我们要将win++,写成(*win)++,不然传入的win就没有用了
完整代码
#include"game.h"
void Initboard(char arr[ROWS][COLS], int rows, int cols, char ret)///初始化地图
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = ret;
}
}
}
void displayboard(char arr[ROWS][COLS], int row, int col)///打印地图
{
printf("---------------------------\n");
for (int m = 0; m <= col; m++)///打印列
{
printf("%2d ", m);///这里改成%2d,%2d是按照两个字符对齐
///原因是当我们在修改ROW和COL的值的时候,可以避免行和列无法对齐方格的情况
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%2d ", i);///打印行
for (int j = 1; j <= col; j++)
{
printf("%2c ", arr[i][j]);
}
printf("\n");
}
}
void Setmine(char arr[ROWS][COLS], int row, int col)///放置雷
{
int count = mine_count;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
void findmine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int win = 0;
int x, y = 0;///初始化要排查的坐标
while (win < row * col - mine_count)
{
printf("请输入您要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)///控制排查的位置在图内
{
if (mine[x][y] == '1')
{
printf("你被炸死了");
displayboard(mine, ROW, COL);///打印出我们布置雷的图,让玩家失败后知道雷的位置
break;
}
else///该坐标不是雷,所以要统计周围有几个雷
{
refindmine(show, mine, ROW, COL, x, y,&win);
displayboard(show, ROW, COL);//要再次打印出我们的show图,因为我们排查的位置的数字已经发生变化。
if (win == row * col - mine_count)
{
printf("恭喜您扫雷成功");
break;
}
}
}
else
{
printf("坐标错误,请重新输入\n");
}
}
}
void refindmine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, int x, int y,int* win)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断边界
{
if (show[x][y] == ' ')
{
return;//遇到了空格,结束递归
(*win)++;
}
else if (getminecount(mine, x, y) != 0)//扫描到了雷,结束递归
{
(*win++);
show[x][y] = getminecount(mine, x, y) + '0';
return;
}
show[x][y] = ' ';//将未扫描到雷的地方赋值成空格
(*win)++;
refindmine(show, mine, row, col, x - 1, y - 1,win);//九宫格扫描有八个方向,所以我们进行八次递归
refindmine(show, mine, row, col, x - 1, y,win);
refindmine(show, mine, row, col, x - 1, y + 1,win);
refindmine(show, mine, row, col, x, y - 1,win);
refindmine(show, mine, row, col, x, y + 1,win);
refindmine(show, mine, row, col, x + 1, y - 1,win);
refindmine(show, mine, row, col, x + 1, y,win);
refindmine(show, mine, row, col, x + 1, y + 1,win);
}
}
int getminecount(char mine[ROWS][COLS], int x, int y)
{
int res = mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
+ mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y]
+ mine[x + 1][y + 1];
res = res - 8 * '0';
return res;
}
总结
如果大家在运行中出现错误或者建议,请大家在讨论区下面留言,博主会去收集起来,再统一更新。
这里多运用了递归的指针的知识,如果对您有用的话,不妨给博主一个点赞,关注加收藏三连哦~阿里嘎多,美羊羊桑~