前言扫雷的所有代码可以看看我的gitee--扫雷游戏 · 憨色第一棒/112期比特 - 码云 - 开源中国 (gitee.com)
1.写代码前总体框架。
我们需要用代码实现的是棋盘的初始化,雷的布置,雷的排查,周围雷的显示,以及棋盘的打印等代码操作因此我们可以设置四个函数分别实现这些功能,此时我们注意到扫雷代码不包括主函数内容就有五个函数,代码量之多也可想而知,所以为了提高代码的可阅读性,我们创建三个文件
game.h文件包括了我们所有我们调用函数的头文件,函数声明,以及我们的宏定义。(这里为什么要设置宏定义,我们后续慢慢道来)
game.c文件包括扫雷游戏中四个函数的定义。(少了一个函数的声明是因为雷的显示我们只要在雷的排查中实现就行了)
test.c文件是代码的主体部分main 函数所在,各种函数的调用。
接着首先我们观察扫雷游戏,不难发现是一个类似平面坐标的格式,因此我们联想到了二维数组,我们可以用二维数组来包含雷的信息,以及棋盘打印载体。
2.菜单的打印
我们知道主函数是必不可少的,为了有更好的游戏体验,一个游戏初始的菜单是不可少的(在菜单中我们可以选择游戏是否游玩),为了让代码看起来更简洁,这里菜单我们可以用一个自定义函数menu();来实现具体
此函数只是为了打印菜单无返回值,所以函数类型是void,也不需要传参进来。
打印完菜单后我们需要选择是否游玩吧,
然后这里我们用input来接受我们所选择的选项,接着我们就可以用switch语句来实现。
当我们选择1时进入case 1:即我们选择开始游戏,进入game();这个函数中(将游戏的实现放到一个函数中也是为了代码的可读性)选择0我们就退出。(为什么1是玩游戏,0是退出游戏。这么设置后面也会提到)
3.初始化棋盘,打印棋盘,布置雷,排查雷函数(以及显示雷函数)的实现。
当然四个函数也是有先后顺序的,因为我们都知道代码在编译器中是自上而下逐步实现的所以,我们是依次要实现棋盘的初始化,打印棋盘,布置雷,排查雷(以及显示雷)。在函数实现前,我们需要2个数组show和mine数组,分别是棋盘的表面,棋盘的内面。我们用'0'表示非雷'1'表示雷,在棋盘表面用‘*’表示没有排查的位置。这里我们选择的是9x9的大小,所以我们写成以下格式
假设后期我们玩10x10的棋盘话,我们得把代码中的所有的9改成10,变得十分繁琐,所以为了方便更改,我们在game.h文件中宏定义
这样一来以后就只需要改变宏定义就行了,十分方便。
接着我们就开始着手棋盘的初始化
开始前,我们需要思考一下雷的显示的该坐标四周雷的多少,所以如果初始化的棋盘大小与显示雷棋盘的大小一样的话,在显示边上的雷时,就必会出现越界的情况。为了解决这一个问题,我们可以将初始化的棋盘在四周在外加一圈如下:
也就是初始化的棋盘大小是11x11,同样我们在game.h中宏定义11
接着我们在game.c中定义一个函数Initboard来实现棋盘的初始化,所需要的形参有(数组,棋盘行,棋盘列)
在Initboard中我们用嵌套for循环的方式初始化棋mine数组的初始化的函数可以表示为
同样函数无返回值所以为void,
同样show数组的初始化为
我们发现两个数组初始化十分相似,这里我们思考可以不可以将两个数组结合起来呢,我们不难发现两个函数中只有初始化的字符不同,所以我们可以用一个新的参数ret来接受
所以优化后的函数为
只要在test.c中传不同的字符用ret来接受就行了,具体如下:
写完后,我们还需要在game.h中声明一下(我们用game.h包含了所有函数的声明以及头文件。)
接着就是棋盘的打印了
这里我们设置DispalyBoard来实现(函数名可以任意起,不过为了可读性,用英文来解释函数目的起的函数名更加容易理解这是什么函数。),这里我们思考数组的大小是9x9还是11x11呢。初始化棋盘设置为11x11是为了显示雷时防止越界,特别留出的外面一圈空白部分是不存在雷的布置的,而棋盘的打印只要显示雷出现的部分,即我们游玩时见到的,所以这里我们只要1~9的部分所以我们在test.c中这样调用
注:游玩时我们所见的是棋盘的表面,所以传show数组。
接着我们在game.c中实现,同理我们用嵌套for循环的方式
运行起来就是这样
确实是打印出来了,但是我们还可以加一点东西使得代码运行结果更加美观。例如:我们可以把行和列加上。打印行数我们只需要在打印棋盘前加一个for循环打印0~9的数字即可,打印列数时,不难发现列数与i有关所以我们只需在内层for循环中加上个
即可,此外我们还可以设置扫雷的边界,整体如下
运行起来就变成了这样
变得一目了然。这样棋盘的打印也完成了,同样我们要在game.h中声明一下.
再接着就雷的布置了
雷的布置肯定是随机的,那么随机的雷我们要如何实现呢。对这里我们想到了rand函数(对rand函数不熟悉的观众可以看看我写的《rand函数真的是随机的吗?》这篇博客),我们可以设置int x;与int y;这两个变量,我们可以将x看作数组的横坐标(数组的行),将y看作纵坐标(数组的列),接着将rand给的“随机数”赋给横坐标(数组的行),与纵坐标(数组的列),这样我们就可以将雷’1‘随机给到数组的某行某列了。对于9x9的棋盘,一般设为10个雷,这里我们增加游戏难度设置15个雷,15个雷肯定不是一次性设完的所以我们要用到循环,这里我们知道了雷的个数也就是条件所以我们放到while循环里,接着我们可以用count来计数,每次设置完一个雷,count--,具体代码如下:
这里我们要布置雷的范围是1~9所以我们写成这种形式
%上一个9得到0~8的数再加上1,最后得到1~9的数。同理函数写完后,在game.h中声明一下。
最后就是我们雷的排查了
这里我们用FindMine来实现,这时我们就要将两个数组全部传进去,mine数组用来判断是否踩雷,show数组是输完第一次坐标后,下一次棋盘的打印。所以初步分析我们可以知道输入坐标与下一次棋盘的打印肯定是放在一个循环中的,那么循环结束的条件想必就是剩余的雷数量了,当雷剩余0时循环跳出,这里我们用win间接表示当win>9X9-10时也就是跳出循环(每次进来win++),接着当我们踩雷时,我们就打印棋盘,当我们没踩雷时,我们就要显示那个位置周围的雷的个数,因此我们还需要一个ShowMnie函数来实现这一目标.这里我直接上函数:
(对于雷的显示还有第二种用循环的方法有兴趣的可以挑战一下)
mine数组里的是字符为了转换成数字,有如下方法:
我们可以用这种方式将字符转换成数字
所以综上FindMine的函数如下
同理我们要在game.h中声明函数。
最后我们玩完一把后还行继续完怎么办,我们可以在主函数中套一层do while循环具体如下
4.结尾总结
至此扫雷游戏的代码之旅就完成了,其实初听扫雷代码,不知何处下手。写完后不能发现其实就是将代码细化,用一个一个的代码块去实现,所以写代码前,我们不妨可以先将代码的总体框架构思出来,再逐步攻破。希望能帮助到你,感谢观看!