游戏介绍:
《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
正文:
今天我们就带来一期扫雷项目讲解
首先想一个问题我们要写一片大幅度代码我们是一大片连着一起写的吗?
对于这个问题我们可以去联想一下生活,对于一件复杂的事情你该怎么去处理,在编程界有一个很有名的问题,把大象塞进冰箱要几步?嘿嘿,对了就三部,把冰箱门打开,把大象塞进去,然后把冰箱门关上。同样的我们在编程中遇见的问题同样也可以把大问题划分为无数个小问题。那么写一个扫雷游戏我们要划分为多少个部分呢?
首先,一个游戏我们需要有一个菜单,这是必须的,不然怎么开始和结束呢?
其次:我们要有一个游戏体
最后我们需要将两者衔接起来才算是一个游戏
菜单介绍:在菜单中我们要去实现提醒用户的输入
对于开始和结束的选择我们可以写一个打印函数来提醒用户输入选项来使得游戏进行与结束。但是怎么实现游戏的运行与结束呢?这里我们就需要另一个函数test()函数来帮助我们完成
我们对于游戏的选择我们可以用c语言中的选择结构来完成,这里我们通过switch来实现
注释:game()为游戏体
首先我们玩游戏可能玩几盘不一定只玩一盘啊可能是玩好几盘这里我们就要想到它放到循环里去写,循环我们都知道最重要的就是循环变量和条件啦,我们可以定义一个变量作为循环条件,我们上面的菜单中说明过了输入0我们就退出游玩,输入1我们就进行游戏我们可以把这个输入的内容放到循环变量中去是不是就可以实现多次游玩了呢?但是这里我们要先完成游戏的导入与退出,退出的话,我们首先想到我们是在循环中去完成这个游戏的我们跳出这个循环是不是就算是结束游戏了?我们不妨按这个思路去试试讲input换成0,while中的条件就为假了,我们就跳出了这个循环也就真正的结束了这个游戏
其次我们要了解我们这个选择部分的导向除了退出和游戏体意外是不是还有其他可能,我们输入的格式多种多样但是我们只有输入1才是游戏0才是退出,那么怎么处理其他输入呢?
我们这里就需要用到switch语句里面的default,也就是说除了0和1的其他输入我们都运行default中的语句break,我们设想输入的input为2(以2为例),我们的input值就为2,不为0,先提示重新输入,其次while中的条件也就为真也就再次执行循环体部分重新执行scanf部分输入input。
游戏体介绍:在游戏体中我们要完成扫雷的棋盘基础化,雷区的布置,雷区的初始化以及游戏的基础算法
布置棋盘:
我们玩扫雷是不是都有行和列的设定?那么我们可以先定义两个变量来分别作为这个棋盘的行和列
对于扫雷我们都知道扫雷有一个棋盘的呈现(以下为9*9的扫雷棋盘)
而棋盘的作用是用来存储所有的雷以及展现给玩家看,那么我们可以将这两个部分分开来完成,我们可以定义两个棋盘分别来实现存储雷的位置以及展示给玩家看
这时候有的小伙伴就要问了,不是说两个棋盘吗,这里这么只有一个啊?为什么上面的行和列定义为11这下面的展示又都为9呢?
别着急这时候我们接着往下看
对于为什么只有一个棋盘:
其实是因为雷的存储棋盘我们是不能让玩家看见的,假设让玩家看见了那么我们做这个游戏也就失去了他的意义,所以我们在执行的时候我们就不给玩家看我们的存储雷的棋盘(以下简称jichu棋盘)所以在这里我就只给大家展现我们的展现棋盘(以下简称show棋盘)了。
对于为什么行和列和展现的不一样
大家可以设想一下,我们要完成一个扫雷的游戏算法设计上是不是要执行多套算法?因为我们的可能会遇见雷在边界的情况,看下图
假设我们检测的位置在(9,9)坐标,我们检测这种边界是不是就和我们在(5,5)坐标处检测不一样?那么我们就需要两套甚至多套算法去实现游戏算法,但是如果都是在(5,5)这种周围都有字符的区域我们就更方便检测,这时候我们可以在我们的棋盘上加上这么一圈一定不是雷的区域
像这样,他是不是就可以使用一套算法去完成所有的在9*9棋盘内的检测了?不妨试试,我们在(9,9)坐标处检测是不是和在(5,5)座标处检测的效果一样?那么这时候我们的jichu棋盘和show棋盘就是一个11*11的棋盘了,但是我们展示的还是要是9*9的棋盘,那么我们就可以定义另外两个变量来作为展示棋盘的行和列以及雷的设置棋盘(因为我们只需要在9*9的范围内设置雷就够)的行和列了。
那么在这个游戏中我们还要去完成对这两个棋盘的初始化,那么我们就需要一个初始化函数来帮助我们对棋盘初始化。
这里呢我们完成了一个初始化函数,我们这个函数只需要去把我们的jichu数组和show数组给初始化为特定数组就行,但是我们对于show棋盘只需要全部给他赋值成字符*就可以了,在jichu数组当中我们先把他全部设置为字符0,所以我们不需要它给我们一个返回值我们只需要初始化就可以了,所以在我们的初始化这里我们在函数名前写的void,我们这个函数要做到数组的初始化但是我们首先需要一个数组才能够对他进行初始化,所以我们将jichu数组和show数组分两次传递给我们的初始化函数,而我们要展示的show数组不能包含任何雷的信息所以我们给他全部初始化为字符*就可以了,而对于jichu数组我们则先将它全部赋值为字符0就行了。
布置雷区:
在我们设置雷前首先要确定一件事情,那就是雷的个数。在这我们将游戏的简单模式定义为10个雷。
在确定了雷的个数之后,布置雷区之前,我想先向小伙伴们介绍一个c语言的库函数:srand函数。srand函数是一个生成随机数的库函数在使用时我们使用srand函数里面给他一个种子他就能够利用一套特殊的算法给你一个随机数,而我们给他time函数(另一个c语言库函数,可以随时间改变值,可以去csdn搜索c语言time函数查看用法),我们给srand函数传递time时每时每刻time的值都在变化所以给的种子是每时每刻都在变化的也就可以得到一个真正的随机数。在声明了srand函数之后,rand函数(srand的配合函数)的值也就成为了一个随机值。在文件头我们使用srand函数和rand函数时要包含头文件stdlib.h,在使用time函数时我们也要包含头文件time.h。
在这里我们定义两个变量一个为h一个为l,其中h等于rand()% hang + 1,l等于rand() % lie + 1。h为随机行数,我们可以设想一下为什么是%hang+1呢?我们给set传递的hang和lie分别为hangs和lies也就是9和9。我们设置雷肯定是在9*9的棋盘内设置的,我们可以设想一下雷的下标范围,谁不是x坐标范围为1~9,y坐标范围也为1~9。所以我们只需要得到1~9的两个随机数分别代横坐标和纵坐标就行了。而我们rand()% 9的取值是不是在0~8之间,那么我们rand%9+1是不是就是1~9内取值了。所以我们这样就可以获得随机的横纵坐标,然后再利用横纵坐标对其赋值,将原来的字符0改为字符1就行了。所以我们就可以利用循环对其进行布置雷区,那么我们该循环几次呢?我们一共要布置10个雷,那么我们是不是就可以利用雷的个数来作为循环条件,每成功布置一个雷我们救减一。这样是不是就可以控制雷的布置。但是,我们有没有可能在布置过雷区的地方重新布置一次雷呢?我的h和l变量是随机的啊是不是有可能两个变量生成两次都为一样的数?
答案是肯定的,那么我们就要避免这些位置去布置雷,我们可以想到雷区和无雷区的区别就是一个为字符1一个为字符0,那么我们布置过的地方就是字符1,那么我们加一个if语句,如果我们对应的arr[h][l]内存储的是字符0那么就count–,否则不执行任何语句就行了。那么我们的循环始终都是执行10+次,也就可以真正的实现布置雷的过程。那么到这我们的设置雷步骤也就完成了。
输入检测:
我们的雷区和棋盘都设置好了,那么接下来就给大家带来游戏算法实现。
一个游戏他的关键核心就在于算法实现也就是游戏玩法的实现,那么我们怎么样做到扫雷算法实现呢?首先大家可以去思考一下,我们的扫雷是怎么玩的呢?是不是点了排查了一个不是雷的地方,他会在这个位置显示周围雷的个数,例如输入5 5,会有下图检测提示。
那么这里我们要做什么操作呢?首先我们第一个要做的就是实现伪代码思路,首先我们有两个数组,一个为jichu数组,另一个则是我们的show数组,jichu数组的作用是存储雷以及辨别雷,show数组的作用是展示给玩家观看以及做到排查雷个数的接收,我们在实现这一过程时我们到底该怎么去实现呢。我们有多种方法以下为大家列举两种。
利用ASCII码值:
我们已知我们每个字符都有自己特定的ASCII码值
这里我们可以观察到我们字符0与字符1之间ASCII码值只相差1,并且0~9的ASCII码值按照0~9顺序依次加一,那么我们就可以利用这个规律去用来实现算法。
如图我们检测(6,3)坐标,那么我们只需要将(5,2);(7,2);(7,3);(7,4);(5,4);(5,3);(6,2);(6,4)的ASCII码值加起来减去8*0的ASCII码就能够得到周围雷的个数。那么我们就可以根据这个原理去写一个函数我们得到这八个位置的ASCII码值减去8*字符0的ASCII码值后的和,然后将这个和返回就可以得到周围雷的个数了(记住这个函数名,下面要考)。
这样我们的大致算法也就算是完成了,那么接下来我们就要考虑该怎么去使用这个函数。
首先我们需要输入我们要检测的部位的x坐标以及y坐标,然后我们还要做到检测了之后我们要打印棋盘,我们知道了这些条件后可以写到以下函数。
但是我们再去对这个函数思路走一遍,我们发现我们这个游戏是不是只能走一遍并且还有胜利条件,输入范围,重复输入这些问题没有去解决,那么对于这些问题我们又该怎么去更改呢?
对于胜利条件(循环条件):
我们应该分析一下我们怎么样才算是获胜?其实我们胜利的条件就只是要检测次数等于刚刚好就是我们的行数乘上列数再减去雷的个数,那么我们就可以定义一个变量为0,每检测一次让它做到自增就行了。
对于输入范围:
我们输入的范围应该对应着数组的下标,如果超出了下标范围那么就要提示重新输入,那么我们的输入范围是多少呢?我们输入的范围应该和雷区的分布范围一致,只有这样才能确保用户每次输入都可能踩雷,那么我们的x范围就应该是[1,9],y的范围就也是[1,9]。那么我们就可以加上一个if语句去判断输入的x与y的范围在不在这个我们要的范围内,如果在则下一步,如果不在则提示用户重新输入
对于重复输入:
我们检测一个地方时我们就有可能会重复检测那么对于这种情况我们该怎么办呢?其实这个问题很简单,我们只需要加上一个if语句判断检测的地方的show数组对应位置的值是不是字符*如果是那就说明没有被检测,如果否则提示用户已检测。
当我们把这些问题都解决后可以得到以下函数
那么我们这些部分就算是完成了,我们重新理一下思路,是不是就是->布置棋盘->布置雷->打印一遍棋盘->提醒用户输入->开始输入->开始检测->开始更改->打印棋盘
那么我们就可以得到最后的程序文本。
代码放在这大家感兴趣的可以看一下。
23_12_25/23_12_25 · 江尘cg/saolei – 码云 – 开源中国 (gitee.com)
那么到此我们的扫雷项目的讲解也就到此为止了。感谢大家的观看,请各位小伙伴多点点关注啊。