扫雷游戏【C语言实现】【完美版】【非常详细的讲解,看完必会】

本期介绍🍖 主要介绍:如何一步一步的把整个扫雷游戏的所有功能实现,…

在这里插入图片描述

本期介绍🍖

主要介绍:如何一步一步的把整个扫雷游戏的所有功能实现,详细的讲解其中每一个功能从无到有的思考过程以及代码上的实现👀。


文章目录


一、扫雷游戏🍖

扫雷是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。这款游戏的玩法是在一个9 * 9(初级),16 * 16(中级),16 * 30(高级),或自定义大小的方块矩阵中随机布置一定量的地雷(初级为10个,中级为40个,高级为99个)。由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束。值得注意的是若翻开的格子下没有地雷,则会标记其周围一圈格子中包含雷的个数,所以若想顺利通关就必须利用好这些显示出来的数字。如下图所示(而我们今天要实现的是9×9方格的扫雷游戏,棋盘上一共放置了10个地雷)
在这里插入图片描述


二、test.c文件的实现🍖

2.1程序实现思路 🍖

首先,我们要思考一下怎样才能实现的和计算机里的扫雷游戏一样呢?进入计算机里的扫雷游戏,首先映入眼帘的是9×9的棋盘上所有的格子都被掩盖了。然后随机去翻开一些格子,你肯定能发现会出现几种情况:

1.当翻开的是“地雷”时,你会被炸死游戏结束。
2.当翻开的格子周围一圈当中存在“地雷”时,翻开的格子下面会显示周围一圈格子内存在“地雷”的个数。
3.当翻开的格子周围没有“地雷”时,会一下子展开一大片区域。

然后不断地翻开格子,直到把所有的非“地雷”的格子全部翻开后,游戏提示:扫雷成功!那我们该怎么实现这里双层结构的棋盘呢?你会发现双层结构的第一层是用来覆盖住第二层上显示的内容,一个想法就油然而生了我们是不是只要创建两个二维数组,其中一个隐藏在暗处用来存放这些地雷啊、数字的信息,用户是看不见的;另一个则是专门向用户展示的,是摆在明面上的棋盘(这期会实现:可以自己标注地雷位置的操作,还有炸金花式展开的功能)

游戏菜单:选择是否开始玩儿游戏,1:开始游戏,0:退出游戏。
游戏流程:创建两个格子棋盘(其中一个是隐与幕后存放地雷信息用的,另一个是向用户展示的)→ 初始化两个棋盘 → 随机放置地雷 → 实现打印棋盘 → 开始扫雷(用户输入要查找的位置的坐标 → 判断是否有雷 → 有则游戏结束,若没有则显示其周围一圈中地雷的个数或者炸金花式展开一片)→ 直至扫完所有非雷区 → 游戏结束。


2.2代码 🍖

代码如下:

注意:仔细看上面的代码,你会我并没有引<stdio.h>头文件而是引了"game.h"文件,而且程序中出现了很多未定义的标识符:如ROW、COL、ROWS、COLS(这里的ROW表示:行,COL表示:列)。那是因为我把这些东西有放到头文件"game.h"中去了,引用该头文件就相当于引用他们。还有就是上面的很多函数是没有定义的,只不过事先说明其用处罢了。那为什么要把源文件分开来放置呢这是很重要的一个知识点,我之前有一期就是专门讲这个点的,不懂的同学一定要了解一下再三重复真的很重要。下面让我们来一 一实现这这些函数吧!!!链接:函数声明和定义真正的用法

在这里插入图片描述


2.3神来之笔的想法 🍖

相信看到这肯定有同学问:“不是说实现的是9×9的扫雷吗,为什么这里是用11×11的二维数组来存放哒???”这就得夸夸那些牛逼的大佬想出来的方法了。现在假设我用9×9的二维数组来存放信息,当排查某个非雷的坐标时,我们知道这时是需要将该坐标周围一圈坐标逐个排查一遍的。但当你去排查最边界的那一圈坐标时,你会发现你必须得以特殊的排查法来处理这些坐标,不然你就会越界访问。就如下图所示:
在这里插入图片描述
所以要在排查的时候不越界访问你就必须给这最边缘一圈的坐标设计出专门的排查规则,这是一种解决的方法,但该方法的实现太过于麻烦了。所以一些大佬就思考:能不能从另一个角度以更加简便的方法来解决这个问题呢?其中有些人就发现,似乎只要把9×9的棋盘扩张成11×11的棋盘,然后只拿该棋盘中间的9×9的格子用作和以前一样的操作,问题不就迎刃而解了。这是为什么呢?因为我操作时只会去排查中间那9行9列的格子,且就算我去排查最边上的一圈坐标也不会出现越界访问的情况,如下图所示。你看仅仅只需要把数组的大小改一改就能解决困扰你很久的问题,完全没有必要在那限制这限制那的。所以有些时候不能硬想啊,一定要学会换一个角度来思考问题的解决之法!!!
在这里插入图片描述


三、game.c文件的实现🍖

3.1 棋盘初始化函数🍖

该函数做到了可以任意对棋盘进行初始化,想初始化什么就传参传进来就行了。


3.2 设置地雷函数🍖

该函数是通过rand()随机数函数来设置地雷的如果我之前有一期是专门讲解了如何去创建随机数,如果这里感觉不是很明确,建议先了解一下随机数再来看下去。链接:如何创建随机数


3.3 打印棋盘函数🍖

该函数可以实时的打印出你想要的几行几列的棋盘,只需更改头文件中的常量ROW和COL就行。

演示一下效果,就如下图所示。mine棋盘其中字符1代表设置的地雷,字符0表示该地没有地雷,而show棋盘中字符 ‘ * ’ 表示方格还没有被翻开,是未知的意思。
在这里插入图片描述


3.4 扫雷函数🍖

该函数是由很多子函数嵌套调用而成,总体实现的功能是:先创建一个循环,然后要求用户输入要排查的下标,接着判断用户输入下标是否有效,再判断是否排查到雷。若排到地雷则直接退出循环游戏结束,否则直接进入炸金花式展开的递归调用,直到排查完所有的非雷方格则游戏胜利(炸金花式展开是啥,下面会仔细说明,不要着急)。


3.4.1 计算四周地雷个数函数🍖

该函数是实现:当我们在排查某个位置的时候,会计算该位置周围一圈存在地雷的个数,并返回给这个值。如下图中的数字那样:


3.4.2 显示地雷函数🍖

该函数若放到调用它的扫雷函数中去执行,会实现下面这种情况:若我不小心扫到了地雷或扫雷成功时将会显示曝光所有的地雷在用户界面上向你展示。如下图所示:
在这里插入图片描述


3.4.3 炸金花式展开函数🍖

这是一个递归函数(对于新手来水非常难,我估计光看我写的代码是很难理解透彻这个函数的,所以我会逐步的来分析这个代码),该函数实现的功能是:若排查的位置周围没有雷则向四周爆炸式展开,直至遇到周围有地雷的坐标时停下来。如下图所示:

在这里插入图片描述
那该怎么实现该递归函数呢?当排查的位置没有雷且该位置周围没有雷时,就展开其周围一圈的8个坐标,然后看这8个坐标是否可以再逐个向其自身周围接着展开,这样一次就递归调用函数自身8次的展开速度就像爆炸了一样,所以称其为:炸金花式展开


(1)难题1🍖

如果按照这个思路去编写代码一定会出现一种情况“ 程序陷入死递归 ”。这个问题当时也时困扰了我很久,代码调试一直走不下去,直到我动手画了张图慢慢分析后才发现了问题的所在。看下图所示:
在这里插入图片描述
基于上面的所思所想:我们认为当排查某个周围没有雷的坐标时,该坐标会向周围一圈展开,然后展开的这些坐标会继续再向外围展开,继而把所有周围没有地雷的坐标统统排查出来。可事实真是如此吗?你会发现不管你先展开哪一个位置你都将陷入死递归当中。就拿上图所示的这个坐标为例,当要展开(x,y)坐标周围的一圈坐标时,假如其首先会从(x-1,y+1)位置开始,然后又由该坐标向其自身周围的一圈展开时,你会发现坐标(x,y)也在需要展开的范围内。这样一来不就会重复要求再次展开坐标(x,y),然后在由(x,y)要求展开(x-1,y+1),两个坐标在那相互疯狂调用,你不死递归谁死递归???
解题思路:那该怎么解决呢,说难不难当也不简单,要看你自己在思考的过程中能不能灵光一现。你看,现在我们完全是站在存放地雷这个棋盘(mine_board)的角度在思考解决问题的办法。这样必然局限了我们的思维,不妨跳出来看看,换一个角度去寻求解题之法。不知道大家有没有发现我们似乎略了用户棋盘(show_board)了呀,可不能只认为用户棋盘只是用来展示的,如若这两个棋盘配合的够好,你会发现此法可以完美的解决上面的难题
如何实现若每次判断一个坐标上没有雷且该坐标周围一圈同样没有雷时,则将向我们展示的棋盘(show_board)上表示未知的字符 ‘ * ’ 改成空格。然后在之后的递归调用前给一个判断:如果这个将要被递归调用的坐标在(show_board)棋盘上存放的是字符 ‘ * ’ 时才能进行下一步,否则将直接跳过此次递归。如此就可以限制住坐标在那相互疯狂调用,因为程序这样设计后每个坐标向外展开的次数就只有一次了


(2)难题2🍖

解决了上面的难题后你还会遇到一种情况,程序调试时出现错误,编译器给的理由是:非法访问内存空间。其实在很多情况下会导致这里出现“ 非法访问内存空间” ,但该处出现的原因是:数组越界访问了肯定有同学会问:怎么又会越界访问呢?上面不是已经解决这个问题了吗?那就只能说明你思考问题的时候太片面。上面的确解决了这个问题,但你要知道这里可是出现了向外扩张的递归啊,你那拓展出来最外围的一圈坐标完全无法起到限制的作用,无法阻止炸金花式的向外展开。所以当然会出现越界访问啦!!!
在这里插入图片描述

那该怎么解决呢?其实很简单只要能想到上面那层,就很容易得出:每次进入递归函数后加一条限制不就行了具体怎么做看下面的代码自己领悟吧


(3)代码实现🍖


3.4.4 手动标记地雷坐标🍖

该功能实现的是在每次排查后都有一次选择的机会,是否要标记地雷的位置。若要则输入**‘ Y ’** ,之后需要输入想要标注的坐标位置,只有当方格还是未知符号 ‘ * ’ 时才能用字符 ‘ !’ 作为警告标记的;若不要则输入 ‘ N ’ ,程序接着执行下一步。


四、完整的程序🍖

头文件<game.h>:

源文件<test.c>:

源文件<game.c>:


五、最后实现效果🍖

在这里插入图片描述
这就是我今天这一期所带来的扫雷游戏的实现,希望多多关注我的其他博客,我想你会收获很多你完全不了解的新知识!!!再次感谢你的捧场


在这里插入图片描述

这份博客👍如果对你有帮助,给博主一个免费的点赞以示鼓励欢迎各位🔎点赞👍评论收藏⭐️,谢谢!!!
如果有什么疑问或不同的见解,欢迎评论区留言欧👀。

本文来自网络,不代表软粉网立场,转载请注明出处:https://www.rfff.net/p/3513.html

作者: HUI

发表评论

您的电子邮箱地址不会被公开。

返回顶部