扫雷“邂逅”了递归,会发生什么了?

扫雷“邂逅”了递归,会发生什么了?


前言

小编最近学习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不也是挺好的吗?想要整个项目的可以到小编的码云上获取哦。

gitee

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WindFall1314

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值