扫雷这个游戏大家一定不陌生,但是这个游戏的底层运行逻辑大家肯定没有去想过,那本章就带着大家去实现这样的一个扫雷游戏。
![](https://img-blog.csdnimg.cn/direct/570fe9c8194a4ab5a73fb786fb351a55.png)
1.扫雷游戏分析和设计
扫雷游戏底层代码并不是我们想象中的很难,它其实很简单,知道分支,循环,函数,数组这几个知识点就可以去实现它,接下来我们来看看如何实现。
首先,我们要先了解扫雷游戏的功能
• 使⽤控制台实现经典的扫雷游戏
• 游戏可以通过菜单实现继续玩或者退出游戏
• 扫雷的棋盘是9*9的格⼦
• 默认随机布置10个雷
• 可以排查雷
◦ 如果位置不是雷,就显⽰周围有⼏个雷
◦ 如果位置是雷,就炸死游戏结束
◦ 把除10个雷之外的所有⾮雷都找出来,排雷成功,游戏结束
2.菜单处理
根据以上扫雷游戏的功能,我们首先可以先打印出菜单,在菜单中,我们要说明开始游戏和退出,这一步很简单,不过中实现菜单之前我们要了解一个常识,在实现一个项目的时候,我们会封装多个文件进行实现,这样既可以方便调试,也可以让别人更好的去观察你的代码。
test.c
//
⽂件中写游戏的测试逻辑
game.c
//
⽂件中写游戏中函数的实现等
game.h
//
⽂件中写游戏需
要的数据类型和函数声明等
打印菜单和选择游戏开始与退出,我们就可以在test.c中实现
当我们选择1是就可以进入游戏,我们把游戏实现封装在一个game函数里,可以更好去管理自己的代码,选择0就是退出,在这个过程中,我们可以利用,switch语句实现,如果我们想在玩一次,就可以利用do—while循环来处理
有人可能发现了,为什么你的头文件是#include"game.h"呢,不应该是#include<stdio.h>,我们知道.h文件是头文件的意思,而由上述知道我们创建了一个game.h的文件,而这个文件所包含的内容就是我
们程序中所需要的头文件和数据类型与函数声明
我们在.c文件中怎么引用我们创建的.h文件呢,其是就是把<>号换成""号就可以了
有人不明白为什么要搞这么麻烦呢,就如为上述所说
可以方便调试,也可以让别人更好的去观察你的代码。
3.扫雷界面处理
这一步我们就来实现我们的游戏代码了,同样,写扫雷底层运行逻辑之前,我们首先要打印出扫雷的界面,例如我们要一个9x9的界面,如下图所示
![](https://img-blog.csdnimg.cn/direct/7a50b6be23164480b40b474bdc5af971.png)
我们发现,扫雷的布局是由多行多列组成的,这一步我们是不是就可以用二维数组来解决,
不过别忘了打印二维数组之前,要对它进行初始化,因此我们需要两个函数,一个表示二位数组的初始化,一个代表二维数组的打印。
![](https://img-blog.csdnimg.cn/direct/d5949405af2c4414a1eb65792ee978de.png)
board表示二维数组,row,cols/col表示二维数组行与列,set表示对二维数组初始化的字符。
有人可能发现,ROWS和COLS是啥,其实这两个代表的是数字,下面为大家解释
![](https://img-blog.csdnimg.cn/direct/8a6b9de4cc6e4d03896fc9b598267a0d.png)
#define的用途是替换,也就是说ROWS其实就是11,为什么要弄成这个样子,这样可以更好的让我们开创多行多列的数组,因为我们在数组的初始化中,我们只需要改变ROWS和COLS后面的数据就行,不用去一个一个的改数据,不仅容易漏改,还可能改错,因此,#define很好的解决了这个问题,同样#define我们可以写到头文件中去
![](https://img-blog.csdnimg.cn/direct/97c2bead770640c88f339c1ab00dce09.png)
有人觉得奇怪为什么初始化期盼与打印棋盘所输入的数据不同,为什么初始化行列都是11,打印却是9呢?
这一步就很有讲究了,我们知道我们要打印的是9x9的界面,但是初始化为什么是11呢,首先就是我们玩的棋盘是9x9,但是,我们还需要展示每一列和每一行的数字,更好的方便我们要扫的点位,有人会说,这个我知道了,那给10不就行了,为什么要给11呢,我们知道当我们扫的点位不是雷时,就会有数字
![](https://img-blog.csdnimg.cn/direct/c18ab159b33d4ff3a8ab81d5433f9897.png)
这个数字就代表周围八个格子中有多少个雷,
当我们扫最底下时,如果只给10的话,最下⾯的三个坐标就会越界,为了防⽌越界,我们在设计的时候,给数组扩⼤⼀圈,雷还是布置在中间的9*9的坐标上,周围⼀圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将数组创建成11*11是⽐较合适。
![](https://img-blog.csdnimg.cn/direct/a7358b9ffa0946259ad3e72e3b698daf.png)
因此我们所创建的如下的数组
![](https://img-blog.csdnimg.cn/direct/f03fddade0224df994125da97bbb8ed2.png)
初始化代码如下:
![](https://img-blog.csdnimg.cn/direct/9f098e6a379d4ca69df14ed6931c3f82.png)
这个代码代表游戏实现,写在game.c中
打印代码如下:
![](https://img-blog.csdnimg.cn/direct/73fb8f87231a4ff596173d3d0d5b3f11.png)
不知道大家注意到没有,为什么我打印行列时,从1开始而不是从0开始呢
![](https://img-blog.csdnimg.cn/direct/1d583d2e95934c42a3588c547ac9c555.png)
![](https://img-blog.csdnimg.cn/direct/878080550585428fb0a9e1c77aa769d7.png)
大家知道这一步是打印列的标记
![](https://img-blog.csdnimg.cn/direct/77198fc0869c48fc90bb72a67618698e.png)
我们先看看从0开始打印会发生什么
![](https://img-blog.csdnimg.cn/direct/92a5364d723348a3bc98b7b9520d3188.png)
![](https://img-blog.csdnimg.cn/direct/3002b7b445fe4a5d86e65947ba14fbf4.png)
为什么行标多个零,*号为什么多一列和多一行,其实很好理解,
![](https://img-blog.csdnimg.cn/direct/97f501014bf4432a9ae3a799178684dc.png)
这一部分我们由于是从零开始的,外层和内层都循环了10次,因此就会发生这样一个状况,我们从1开始的话,标记对的上,*号行列也对的上。
我们发现仅仅一个菜单就有这么多坑,因此我们更不应该疏忽大意。
4.布置雷
知道了扫雷界面后,我们就可以布置雷了。
我们在棋盘上布置了雷,棋盘上雷的信息(1)和⾮雷的信息(0),假设我们排查了某⼀个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录
存储,并打印出来。那这个雷的个数信息存放在哪⾥呢?如果存放在布置雷的数组中,这样雷的信息和雷的个数信息就可能或产⽣混淆和打印上的困难。
这⾥我们肯定有办法解决,⽐如:雷和⾮雷的信息不要使⽤数字,使⽤某些字符就⾏,这样就避免冲突了,但是这样做棋盘上有雷和⾮雷的信息,还有排查出的雷的个数信息,就⽐较混杂,不够⽅便。
这⾥我们采⽤另外⼀种⽅案,我们专⻔给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不⼲扰了,把雷布置mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息。
同时为了保持神秘,show数组开始时初始化为字符 '*',为了保持两个数组的类型⼀致,可以使⽤同⼀套函数处理,mine数组最开始也初始化为字符'0',布置雷改成'1'。
因此对于我们上述打印出扫雷界面,则是,存放排查出的雷的信息,而布置雷的数组,则不打印出来。
首先我们就要再创建一个二维数组,代表雷的信息,同时我们不把它打印出来
后面就开始对mine数组布置雷
与前面一样,我们首先在game.h中定义
在game.c中实现函数
对于布置雷,我们要明白首先几个条件就是:
1.雷的坐标要随机
2.雷的坐标在9x9内
3.雷的个数要适量
对于随机我们可以用随srand函数解决,如果大家不知道srand函数,可以去翻翻我的
《猜数字详解》这篇文章,里面详细说明了srand函数的使用,在这里我就不再赘述了。
同时要使雷的坐标在9x9内可以用rand函数,详细也可以参考我的
《猜数字详解》这篇文章。
雷的个数很简单,就是布雷循环多少次,就代表几个雷。
5.排查雷
排查雷的话,首先就是要输入坐标,如果输入的坐标是雷的话,结束,如果不是,就返回,周围雷的信息,结束条件还有一个就是所有雷完全被排除。
输入坐标很容易,怎么判断这个坐标是不是雷,我们知道mine数组里面存储的是雷的数据,而‘1’代表就是雷,因此,我们可以把mine数组传过去,把坐标代入,判断就行了,循环条件的话,棋盘大小--雷的个数。
![](https://img-blog.csdnimg.cn/direct/89811851205447ea9507b7bbcd1f1f19.png)
当这个坐标不为雷,该怎么进行,我们知道当这个坐标不为雷时,会检查周围有几个雷,并把数字打印在屏幕上,我们可以对坐标周围进行相加减去周围不为雷时候,就可以得出周围有几个雷,由于,雷代表的是字符,因此我们需要+‘0’,表示数字,对于周围有几个雷的个数,我们也可以封装成一个函数,代码如下
![](https://img-blog.csdnimg.cn/direct/237bd68dabe0482ca56abcaad10465e5.png)
![](https://img-blog.csdnimg.cn/direct/8212c11d832a47b3a7b1839f71674244.png)
到此我们简易的扫雷游戏就完成了。
下面是扫雷游戏的扩展,有兴趣可以去尝试一下
•
是否可以选择游戏难度
◦
简单 9*9 棋盘,10个雷
◦
中等 16*16棋盘,40个雷
◦
困难 30*16棋盘,99个雷
•
如果排查位置不是雷,周围也没有雷,可以展开周围的⼀⽚
•
是否可以标记雷
•
是否可以加上排雷的时间显⽰
6.结语
看完这篇文章以后,你是否对前面的知识有了一个更深刻的理解,对于扫雷,没必要强求一下就学会,不过到后面还是要自己写出来,加油!!!
![](https://img-blog.csdnimg.cn/direct/7123fe92a3804b859b9e833b022400c7.png)