扫雷“邂逅”了递归,会发生什么了?
文章目录
前言
小编最近学习c语言到了数组了,但死板的知识终究只是无情的理论,我更倾向与形象化思维,于是便找到了几个经典的小游戏来练练手,前面更新了三子棋,今天我们来了解一下另外的游戏——扫雷。相信大家都玩过吧,今天我们便用c语言来一起探索一下这其中的秘密吧!
1.扫雷的基本实现
1.1扫雷的基本思路
第一步:设置棋盘
这是很关键的一个步骤,需要我们理解清楚实际的棋盘空间,以及我们实际看到的空间,他们之间有什么关系,为什么要这样设置,这几个问题,聪明的你知道答案了吗?心中有数在往下面看哦!
来看一下具体的代码实现:
game.h
#define ROW 9//定义展示的行
#define COL 9//定义展示的列
#define ROWS ROW+2//定义实际的行
#define COLS COL+2//定义实际的列
#define EASYDEMO 10//定义雷的个数
这是定义了几个宏,将来可以便于使用和更改。
game.c
void game2() {
char mine[ROWS][COLS];//定义实际的数组
char show[ROWS][COLS];//定义展示的数组
begin:
init(mine, ROWS, COLS,'0');
init(show, ROWS, COLS, '*');
display(show, ROW, COL);
setmine(mine, ROW, COL);
findmine(mine, show, ROW, COL);
printf("需要再来一把吗?(1/0)\n");
int input = 0;
scanf("%d", &input);
if (input) {
goto begin;
}
else {
return;
}
}
我们能看到在进入扫雷游戏内部后,首先定义了两个数组 mine,show;接下来,再来一起看一下基本的实现;
1.2.数组的初始化
第一步定义了数组,我们没有初始化,定义一个函数对两个数组的进行初始化,因为初始化的内容是不同的,所以初始化的符号我们也得设置为一个参数来进行区分。
game.c
void init(char arr[ROWS][COLS], int rows, int cols, char set) {
for (int i = 0;i <rows;i++) {
for (int j = 0;j <=cols;j ++) {
arr[i][j] = set;
}
}
}
1.3设置雷的坐标
雷的存储信息我们是需要在mine数组中去设置的,参数的设置必然有mine,还需要把行,列传过去,在函数内部循环产生随机数进行设置雷区.
void setmine(char arr[ROWS][COLS], int row, int col) {
int x = 0;
int y = 0;
int count = EASYDEMO;
while (count) {
x = rand() % row + 1;
y = rand() % col + 1;
if (arr[x][y] == '0') {
arr[x][y] = '1';
count--;
}
}
}
1.4简单的遍历排查
这里我就简单说一下思路吧,当我们需要排查一个坐标时,我们首先判断是不是雷,如果是,则游戏失败!不是,我们需要判断该坐标处周围有几个雷,并把该值存到show数组中去,在下一次排查时打印出来,来给玩家提供游戏思路!
那么问题又来了,怎样实现遍历8个坐标了?你可能直接写出来,但是还有一种较为简单的方法,使用两个for循环就可以了,这里就不演示代码了,后面结合递归会详解。
1.5整体游戏逻辑的实现
整体的游戏逻辑:
2.递归的基本定义及理解
2.1递归的基本定义及理解
递归,就是在运行的过程中调用自己。构成递归需具备的条件:1. 子问题须与原始问题为同样的事,且更为简单;2. 不能无限制地调用本身,须有个出口,化简为非递归状况处理。
脑瓜子嗡嗡的,这是个啥意思了?通俗的讲,就是函数在自己的内部又调用了自己,在调用了一定次数之后,不满足了调用递归的条件(再调用一次递归后,不再递归的期望增加),就会按照我们一路调用递归的路线层层返回!
递归与循环的区别在于,前者代码简单,但是运行效率底,并且每一次递归都会调用函数,就会再栈中为他开辟空间,深度递归可能会造成栈溢出的现象!
3.扫雷结合递归实现“智能”筛选功能
3.1基本思路
玩过扫雷的同学因该都知道,扫雷会有展开一片的功能,那么这个功能是怎么实现的了?我们来看一下:
第一步:判断x,y坐标出是不是雷,如果是雷,则游戏结束,如果不是,则判断该坐标周围处的坐标有没有雷,如果没有,这时候我们把show [x][y]设置为‘ ’;
第二步:产生该坐标周围的坐标(采用两个for循环实现),如果该坐标没有被排查过,递归调用该函数,到达最深层时,我们需要设置该坐标处的show数组值,然后一路返回,就能实现该功能了。
3.2核心代码实现
void to_smart(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col,int x,int y) {
if (get_all(mine, x, y) == 0) {
show[x][y] = ' ';
for (int i = -1;i <= 1;i++) {
for (int j = -1;j <= 1;j++) {
if (show[x + i][y + j] == '*') {
to_smart(mine, show, row, col, x + i, y + j);//递归出去
}
}
}
}
else {
show[x][y] = get_all(mine, x, y) + '0';
}
}
int get_all(char mine[ROWS][COLS], int x, int y) {//求x,y坐标处周围的雷的数量
int sum = 0;
for (int i = -1;i <= 1;i ++) {
for (int j = -1;j <= 1;j++) {
sum += mine[x + i][y + j];
}
}
int ret = sum - 9 * 48;
return ret;
}
递归这小东西,还真别致啊!
4.总结
扫雷的基本思路实现就到这了了,着实写的一般,但还是欢迎大家一起来玩玩的。天天玩游戏玩多了,玩玩bug不也是挺好的吗?想要整个项目的可以到小编的码云上获取哦。