扫雷游戏C语言代码实现(下)
文章目录
1.6 getmine()函数——判断位置周围3*3范围内雷的数量
三、分步实现过程
3. game函数——游戏过程函数
3.1 数组的创建和初始化
首先,进入游戏内部,我们需要先完成数据结构的构建,即上面我们提到了两个数组
分别是对内存储棋盘数据的mine数组和对外展示的show数组
出于对安全的考虑,我们创建11*11的数组,实际使用的范围是9*9的数组
并且,为了方便以后修改代码不需要修改太多的地方,我们在创建数组的时候不直接使用常量,而是在game.h中使用define定义常量,这样以后修改棋盘大小的时候只需要改动此处即可
接下来 ,创建数组
需要注意的是,因为我们初始的时候想要用'*'来对外展示棋盘,所以数组类型是char类型,为了分别操作,最好使两个数组的类型相同
并且字符数组没办法使用一个值进行初始化,所以我们还需要一个函数对数组进行初始化
3.2 initarr函数——数组的初始化
要实现数组的初始化,最简单的办法就是使用两个for循环嵌套,依次对每一行每一列进行赋值
下面我们来实现这个函数
关于参数的说明——
- 第一个参数为数组,因为函数传参如果是数组的话,传过去的是一个地址,所以在函数内可以真实改变参数的值
- 关于二维数组形式的参数的写法,声明和定义时可以省略行数,但是不可以省略列数。不过为了保险起见,我们在进行数组传参的时候还是写上完整的行数和列数最好
- 二维数组在函数调用作为参数时,只需要写上数组名字即可
- 第二个参数和第三个参数,分别是我们创建的数组的行数和列数
- 第四个参数——char类型的set需要着重强调一下。这个参数是作为我们的数组的每一行每一列的初始化的内容来赋值的,如果这里不是使用的char类型变量,而是直接使用了字符'*'或者字符'0',那就需要写两个基本完全一样的函数来做相同的事情,造成了极大的浪费。所以多传递一个字符参数,可以极大提高代码的复用程度
记得别忘记在头文件中声明这个函数
在game函数中的调用
实现了数组的初始化函数之后,接下来就是数组的打印函数了
3.3 printarr函数——数组的打印
这个函数的时候和上面初始化函数的实现类似,同样也是用一个函数来实现两个数组的打印
代码如下
关于参数的说明——
这里第一个参数数组参数和上面初始化函数的功能和规则都是相同的,第二个和第三个参数与初始化函数中使用的参数是不同的,这是因为初始化的时候,为了方便,我们是直接将申请的所有数组空间都初始化了的。而在打印的时候,则完全不同,因为我们想对外展示的只是9*9范围的数组,所以打印的时候也是9*9范围的数组,这一点是需要注意的 这样在for循环中控制变量的循环范围就是1-9这个中间范围
(别忘记在打印完每一行的时候换行哦)
完成了初始化函数和打印函数之后,就可以先把打印函数放在game函数里测试一下看看效果了
这里呢,可以看到已经达到我们想要的效果了,但是多考虑一点的话就会发现,游戏实现之后我们需要输入棋盘的坐标来排雷,如果这样每个都要数的话岂不是太麻烦了,不妨打印的时候我们在每一行每一列都加上对应的行号和列号
代码修改如下
修改的思路——
- 首先在每一行打印开始的时候,加上行号的打印
- 列号的打印需要额外增加一个循环在打印内容开始之前,而循环范围内是从0到9,是个数字,比打印的内容从1到9正好多一个,这是因为左边多出了一列行号,所以左边多加上一位数字才能让列号和内容对齐
- 还有一点小小的可以改进的地方——在每一次排雷重新打印时,前后两次的结果是紧挨在一起的,为了视觉上更好看一点,可以在打印内容开始之前,加上一条分界线
效果如下——
这一步完成之后呢,接下来回想一下主题,下一步来完成布置10个雷,并且是随机的10个雷,这里呢我们将雷表示为字符'1',非雷表示为字符'0',这个设置保存在存放后台数据的数组mine中
3.4 setmine()函数——随机布置10个雷
关于随机数生成的实现在这篇文章有详细的介绍,这里介于篇幅所限,就不再展开长篇大论了
(文章中关于rand函数 srand函数 和time函数都有详细介绍)
先上代码再解释每一步细节
代码解释说明——
- count——在这一版本中雷的数量是10个,所以设置一个整型变量,来记录雷的数量。同时为了方便后期的更改,这次也使用了define定义常量,所以在头文件中加上一句#define SET_COUNT 10 当然常量的名字由你自己决定
- 为了完成随机数的生成——需要在main函数加上一句 srand((unsigned int)time(NULL)); 并在头文件包含time.h 和 stdlib.h 头文件 这个原理可以参考上述文章详细介绍
- 接下来使用一个while循环——控制变量为count,初始值为10,每次成功布置一个雷,count--,直到count减为0 循环结束
- 进入循环内部——创建两个整型变量x和y来存储和表示数组的下标,同时为了实现随机的效果,x和y的取值是调用rand函数的结果。而rand函数的返回结果是一个较大的整型值,为了使其取值范围在我们想要的范围,将rand函数的返回结果%9范围在0到8,再+1,范围就是1到9,正好是棋盘的数据范围
- 判断随机生成的下标是否已经被布置过雷——使用一个if语句判断,只有当该下标不是雷的时候,再布置雷,改为字符'1',并且count--
完成setmine函数的定义之后,在头文件加上函数声明
然后在game函数中加上setmine函数的调用
这里我们先在setmine函数后面加上一条打印mine数组的数据
来测试几次看看
可以看到每次布置雷的结果都是不同的,这样我们想要达到的随机效果就实现了
接下来,最后一步,也是最关键的一步,就是实现扫雷过程中输入下标排查雷并获得反馈的过程
3.5 findmine()函数——扫雷过程实现的函数
回到最初,在这个阶段我们想要实现的效果是
- 如果位置不是雷,该坐标就会显示周围的雷的数量
- 如果位置是雷,就炸死游戏结束
- 把除10个雷之外的所有非雷坐标都找出来,排雷成功,游戏结束
现在,我们来一步一步完善findmine函数的框架
第一,这个函数的参数——这次需要把两个数组都作为参数传进去,因为这里需要对mine内的数据进行判断是不是雷,并在show数组改变数据进行反馈打印出来,然后我们再需要两个参数,分别是我们需要操作的9*9的数组的行数和列数
第二,屏幕打印一句提示,提醒输入两个坐标,并读取两个输入的值
第三,得到输入的两个值之后作为数组下标去进行判断——首先再最外层判断输入的值是不是合法的值,有没有越界,如果不是合法的值,提醒重新输入;否则,进行下一步判断。
第四,如果输入的是合法的值——判断该位置是否是雷,如果是雷的话,屏幕打印游戏失败(此时最好能把答案打印出来了结玩家的疑惑),并退出;如果该位置不是雷,根据游戏的要求,需要打印出来该位置周围3*3范围内存在的雷数量
第五,判断位置周围存在的雷数量——为了防止该函数内部变得过于繁琐,最好是把判断和计算数量的部分单独拿出来作为一个函数实现。
到这一步的代码实现如下
3.6 getmine()函数——判断位置周围3*3范围内雷的数量
为了实现该函数,最简单的办法就是用一个for循环来实现——因为该位置是一个3*3的范围,行号是从x-1到x+1,列号是从y-1到y+1,只要创建一个变量来记录,每次判断该位置是不是雷,如果是雷的话,该值+1,最终就可以得到雷的数量
该代码实现如下
现在,我们再回过头来补充findmine函数内的代码
3.7 findmine()函数代码的完善
上面我们用getmine函数得到了排查位置周围雷的数量,但是,不要忘记了,该值是一个整型值,而我们mine数组和show数组都是char类型的数组。
初始的时候,我们使用的是字符'0',所以在此处得到数量的一个整型值之后,也应该变为一个字符类型表示。比如getmine函数计算后得到一个数字3,如何才能转换为字符'3'呢
参照ASCII表中,字符0的ASCII值是48,而想要获得后面的数字的ASCII值,比如3,只要在字符0的基础上+3就可以了
再将得到的这个值赋值给show数组对应位置下标的值,并打印在屏幕上
findmine函数完善如下
然后在game()内加上findmine函数的调用
但是还有一个问题,上面的代码实现只是一次,但在实际过程中,不可能一次就能猜中结果,所以排雷的过程中应该放在一个循环中来实现
那循环结束的条件呢,到此为止,还有一个非常重要的事情没有完成,那就是排雷的结束条件
仔细想想,在9*9的棋盘中,布置10个雷,那么剩余的71个数量就是安全的,如此,只要把所有安全的位置都排查出来,那就算游戏胜利
这里,我们再用一个变量win来记录并作为循环结束的条件
并且当循环结束退出的时候,再使用一个if语句判断循环是否是因为游戏胜利而结束的
findmine函数完整代码如下
至此,整个游戏的代码已经全部完成,快去运行来尝试一下吧
四、源代码
game.h文件 ——项目头文件
game.c文件——项目函数封装
test.c文件——主函数
效果展示
- 点赞
- 收藏
- 关注作者
评论(0)