前言
扫雷游戏想必大家都听说过,或者上手玩过,没有接触过的话,就让我先简单的介绍扫雷的基本玩法:
游戏目标:
尽快找到所有地雷的位置,并标记出来,同时避免点击到地雷。当所有非雷的方块都被打开时,游戏胜利;如果误点到地雷,则游戏失败。
游戏操作:
如果点击到的方块下面没有地雷,方块会显示一个数字或者空白。数字代表该方块周围的 8 个相邻方块中地雷的数量;如果显示为空白,表示周围 8 格内没有地雷;如果不幸点击到有地雷的方块,地雷就会 “爆炸”,游戏结束。
介绍完毕之后,现在我们可不可以自己来编写一段程序,来实现简易的扫雷游戏呢?下面我们开始动手实践:
我使用的编译器是VS 2022
1.扫雷游戏的实现和分析
1.1.使用控制台实现简易的扫雷游戏,所以我们要打印出棋盘,9*9格式或者13*13格式;
1.2.可以通过菜单实现继续游玩或者退出游戏;
1.3.游戏中的雷的布置是随机的;
1.4.排查雷
现在,先来看看我们要实现的效果:
2.棋盘的实现
本次设计一个9*9的棋盘,在扫雷的过程中,雷的布置和雷的排查,它们的信息都需要被存储,所以我们需要一定的数据结构来存储这些信息,因为是9*9的棋盘,所以首先容易想到的就是创建一个9*9的二维数组来存放信息:
扫雷游戏中肯定有雷与非雷的区别呀,那么,我们该怎么来表示雷与非雷之间的区别呢?这样,我们用字符‘1’来表示雷,字符‘0’来表示非雷。好,雷与非雷之间已经区别开来,但是,我们又遇到一个问题,我们要扫雷呀,怎么实现向扫雷游戏那样点击到的方块下面没有地雷,方块会显示一个数字或者空白呢?并且当显示你所点击的区域范围内存在一个雷时,要与雷区别开来,因为雷是用字符‘1’来表示的,存在1个雷和点击处就是雷,这两者之间无法区别,那么,怎么解决这个问题呢?我们可以创建两个数组,一个用来布置雷,一个用来显示玩家游玩时的棋盘,并且棋盘上用字符‘*’来初始化棋盘,这样问题就解决了。然而,我们怎么统计点击处附近存在多少雷呢?
如上图,当我排查内部时,很容易就统计出了周围存在几个雷,但是,当我排查棋盘的边缘时,该怎么统计呢?有的人就说,直接像内部统计雷那样统计就行了呀!但是,有没有想过你怎么知道在数组范围外,所存储的数据是什么,这可是越界访问了呀。所以,该怎么解决这个问题呢,或者说,怎么让它不越界访问呢?只要9*9范围外的一圈都是数组的就行了呗,也就是创建一个11*11的数组,打印棋盘时就打印9*9范围就可以了:
3.用代码来实现
1.创建一个菜单
菜单的功能有进入游戏和退出游戏两种:
怎么实现选择功能呢,可以使用switch语句,然而为了实现多次游玩的效果,可以再使用do while循环,并且编写一个函数game(),之后所有的函数都从它那里调用:
当输入的数字既不是1,也不是0时,给予提示。
2.初始化棋盘
由之前的分析可知要创建两个字符二维数组,且行列都为11,但是为了之后方便改写棋盘的大小,我们可以用#define来定义几个常量
为什么还要定义两个常量来表示9呢,因为之后我们打印的棋盘是9*9的格式的,之后想要加大难度,改为50*50的就更方便了。
数组如图所创建。boom数组是用来存放雷的,show数组是用来给玩家展示的
下面开始初始化棋盘,雷棋盘和展示棋盘都要初始化,所以有
将整个棋盘都要设计成自己想要的样子,所以要传ROWS,COLS作为参数;
二维数组用双层for循环来初始化,由上文提到,雷棋盘先全用字符‘0’来初始化,之后在用字符‘1’来填充,作为雷;展示棋盘用字符‘*’来初始化。那么问题来了,光标所指的地方该写什么?是字符‘0’,还是字符‘*’,显然无论是哪个都不行,否则要创建两个InitBoard函数,一个用‘0’,一个用‘*’,如何解决这个问题呢?只要在传参时,将自己要初始化的字符作为参数传给函数就可以啦
3.打印棋盘
初始化棋盘完成后,来打印以下棋盘来看看初始化是否成功,使用两层for循环就可以将二维数组中的每个元素打印出来,因为我们要打印的是9*9的棋盘,所以传参时,传ROW,COL:
打印出的结果如下图:
(“展示棋盘”和“雷棋盘”,这串字读者可以不用打印出来,这里打印是为了更好的观看)
但是,大家有没有注意到这样打印不方便我们输入坐标来排雷,每次要输入坐标的时候都要自己用肉眼数,太过于麻烦,怎么使它变得容易呢?只要将坐标打印出来即可:
按照图示来编写代码,首先我们要先打印行,0~9一个for循环就可以解决,注意这里打印完毕后,需要换行,避免使棋盘错位:
接着打印列,由图可知,列的标号是先于棋盘打印的,所以代码可以如下编写:
我们要打印9*9的棋盘,所以下标都从1开始。打印每一行后,记得换行。打印的0和*都是字符,所以使用占位符%c。
4.布置雷
雷是布置在雷棋盘中的,并且是在9*9的范围内布置,所以函数传参为:
布置雷最重要的就是随机布置,也就是生成随机坐标,坐标随机了,按坐标所布置的雷也就随机了。那么,如何产生随机数,C语言为我们提供了两个函数:rand(),srand()可以生成随机数。但是,随机数生成的范围太大了,而我们只需要生成1~9之间的数字,要解决这个问题,只需要rand()%r + 1,rand()%c+1,二者分别生成横坐标和纵坐标的随机数,若想要了解两者函数,可以根据网址了解:rand - C++ Reference,srand - C++ Reference,了解函数后,我们知道,srand要生成随机数需要一个可以变化的种子,依赖这个种子就可以,那么什么是随时变化的呢,那就是时间。由此,我们需要借助time函数来实现我们想要的功能,想了解time函数,可以访问:time - C++ Reference。
随机数解决完毕后,现在可以定义雷的数目,我们可以像前文那样,使用#define来定义常量
雷是由‘1’来表示的,并且布置点还没有雷;要设置完我们预定的雷的数量,我们可以使用while循环,所以我们可以如下编写程序:
每当布置完一个雷后数量就减1,if的判断,就避免了雷布置在同一块位置。下面,我们将布置雷后的棋盘打印出来:
由两图比较可知,雷的分布是随机的,并且每一局都是如此。
5.扫雷
现在来到最后一个环节---扫雷。
(1).现在来分析扫雷,首先,我们肯定要自己输入坐标,所以要创建2个变量x,y;
其次还要判断我们输入的坐标,是否合法,也就是说是否在大于等于1,小于等于9这个范围 内;即便在这个范围内,还要判断输入的坐标在棋盘上是否是雷的坐标,也就是判断 boom[x][y]是否等于‘1’,等于就炸死了,游戏结束,并且打印出雷棋盘;
(2).倘若不等于‘1’,那就要开始统计该坐标八个方位存在几个雷,为了方便,我们可以创建函数SumBoom来统计,并且将函数的返回值赋值给show[x][y],毕竟我们扫雷的时候看到的棋盘是show数组,如此以来,我们便可知道扫雷函数的参数是哪些了;
(3).要达到反复扫雷的效果,可使用while函数,那么循环的终止条件是什么?我们可以定义一个变量sum来统计我们扫雷正确的次数,也就是在雷有10颗的情况下,sum小于71时,就一直循环,直到达到71,若达到71,可视为游戏胜利。
分析完毕,开始实现:
注意:在创建函数SumBoom时,返回的是整数,但是数组所存储的元素都是字符类型,要转换为对应的整型只需要在它基础上减去一个字符‘0’即可;其次,show数组所存放的数据类型都是char类型,所以返回值要加上字符‘0’,来转换成对应的字符型。
4.总结
现在将所有的代码展示出来,供大家参考:
运行游戏:
其实,这个代码之所以叫做简易扫雷游戏,是因为它没有向网页版的一样,一点开就扫出一大片,要想达到此效果,可以使用递归来实现,篇幅很长感谢你的耐心看完。