最近学习了如何编程最基础的扫雷,算是我接触的第一个项目,虽然没有页面美化,功能上的拓展,但我已经在做成的那一刻已经感觉到很爽了。这是理科才能带来的成就感。
一、整体思路
作为一个项目,即使十分基础,但扫雷依然有它的傲气,就是比任何oj都要更多的代码量和思考量。所以我们必须将整个项目拆解成可执行的步骤,才能在我们脑中成功编译。
即将展开一场以结果为导向的倒推所需条件。
核心部分
扫雷,扫雷,顾名思义有雷,有不是雷的地方,我们才会去“扫”。
所以我们需要做的事其中最重要的就是,布置雷,然后检测雷的位置。
如何布置雷?
我们形容扫雷的时候,都是形容为9*9的格子里布置着十个雷,而不是81个格子里放十个雷,所以自然而然地,我们想到了二维数组,因为二者是极度接近的。
而扫雷每次开始,所布置的雷都有所不同,这就意味着我们需要用到随机数函数——rand。
注意事项
rand函数实际上是伪随机函数,在我们曾经写猜数字游戏的时候,我们就知道,rand函数实质上是以一个“种子”进行运算得到的随机数,不变种子,就不变随机值。
而rand函数的“种子”,默认为1。
这个时候,我们就需要掏出大杀器——srand函数
程序在调用rand函数的时候,会先调用srand函数,我们通过srand改变rand函数的种子,即达到了每次真随机的目的。
但是seed如果是给定值,只是改变一次种子,这个时候我们就可以用到时间这一恒定改变量,作为我们的seed。
time函数的本质是返回1970年1月1日0分0秒到现在的时间差,但是这个实时变化的值可以让我们得到随机的效果。
于是这样,我们就能得到一个随机种子了。
于是我们可以得到布置雷的思路:(其中srand函数已经在前面使用,避免反复随机,浪费不必要内存资源)
如何检查雷?
前面提到了如何布置雷,那扫雷游戏的获胜逻辑在于,当我们点出一个不是雷的方块时,方块出现的数字告诉我们周围一圈方块存在的雷的个数,让我们得以通过逻辑判断,某几个格子可能存在雷。
之前我们将雷布在了二维数组中,而二维数组最方便的地方在于,我们可以定位!
我先前将雷的值设为了1,那么全加起来就得到了这个方块上该填的数字。
如果这个没想到,也可以用两层循环来代替,这实质上是一样的。
我们应该设置11*11的二维数组,而不是9*9的数组
如何检查雷的思路实质上没有问题,但是我们没有考虑到数组越界。
当我们取到arr[0][0]的时候,程序无法检查arr[-1][0],arr[-1][-1],arr[0][-1],会直接报错。
所以我们可以考虑在外面加一圈(因为arr[8][8]同样会面临越界的问题)
这个时候思路甚至更顺,我们设置雷的地方就是1~9,不用想着数组下标从0开始的问题。
我们应该设置两个二维数组,而不是一个数组从头用到尾
在写着写着,我们觉得不对劲了,扫雷之前都有一些“掩盖”数字的形式,否则还需要我们扫吗?
这个时候,将一个数组变来变去,已经满足不了我们的需求了,甚至还会搞乱我们的思路。
所以我们设置两个二维数组,一个埋雷、检查雷,一个展示出来,到时候用值代替就可以了。
非核心部分
我们将扫雷的核心思路已经理顺了,接下来就是一些界面、功能的设置了。
比如,一个开始界面。
比如难度选择。难度在扫雷游戏里就是多设置一些雷,盘改大一点,只是改几个数字的事情,十分容易。
比如失败界面
当然你完全可以更华丽一些。
二、扫雷游戏的一些拓展
回想一下微软游戏里的扫雷,我们可以留心到一些功能。
1、第一下必不是雷。
这个我们可以通过先输入一遍,然后再随机数埋雷,即在rand函数前先写scanf,然后在后续的循环里再补scanf。
2、排查到周围雷为0个的格子,会将同为数字0的数字,以及一圈有数字的格子同时点亮。
博主的个人想法是,再创建第三个数组,将所有无雷方格的数字填充到这第三个数组中。
然后通过循环的方式来检索相连的0方格(即找行或列相同另一个差1的0方格)。
或者可以检索到0,就把周围一圈的值填充到打印的数组上来,发现上下左右四格里有0的,再把对应的值填上去,直到没有0为止。(感觉可能是这个思路)
3、旗子
在我们没有办法检测鼠标左键/右键的时候,我们可以检测输入的值是0还是1,从而实现放旗子还是检测值的功能,就是用户使用的方式实在感觉有些麻烦......如果是9*9还好,我们可以选择放10个旗子,就检测一次,如果放准了,直接通关。但是我很难想象99*99的那个专家级扫雷,实在有些折磨。
4、用时
可以使用先前提到time函数。time函数记录的实质上是从1970年1月1日到现在的时间差,单位为秒,所以我们可以在游戏开始的时候设置一个变量记录,再设置另一个变量,记录通关/失败的时刻,两者相减就是用时。
5、尽可能避免需要靠运气的死局出现
试想一下,当你玩专家99*99的难度的时候,你辛辛苦苦排的只剩下18个格子(2个3*3),结果发现还有17个雷没有被旗子标出来,这个时候你悲惨地发现,雷的摆放应该是一个3*3空心,一个3*3实心的时候,相信你的心态崩溃了。
这个时候建议增加:
如果发现某个格子周围均为雷,则将周围的某两个雷布置在另一个不为雷的地方。
或者:
不允许将雷相连布置(但这可能就失去游戏的难度了)
这个是开发者需要自己去权衡的。
三、写扫雷游戏有感
扫雷游戏是一个极为基础的游戏,但我发现我的脑子已经不够用了,即使学习到了可以通过函数将代码分块,但函数之间的传输数据时不时让我崩溃,char和int两者同样也让我难绷。
本来以为在已经学习过一遍思路的情况下,我通过三个小时即可复刻。但最后发现还是用了三天,整合起来大约8个小时的样子。
在这期间我学习了调试的技巧,但是这对于我这个新手来说无异于还是无用,因为我的函数分的太多了,导致我自己都不知道调试的地方在哪里了。
代码还是需要多练习,不论是思路难想,还是代码冗长的,多练总没错。