扫雷游戏的实现

1.前言:扫雷游戏作为一款历史悠久的游戏,是我们很多人第一次接触电脑是玩的第一款游戏,作为一款简单,益智,逻辑性强的游戏,我们如何利用C语言去自己制作一款扫雷游戏呢?

2.大致思路:1.任何游戏,都需要包含几个方面:1.菜单栏目 2.游戏内容的构建 3.游戏进一步优化 ,我们一个一个来。

A.菜单栏:游戏菜单栏的实现,让我们思考一下我们玩游戏时的菜单栏是长什么样的,大致是以下的样子,我们可以看到。主要包含play开始和结束游戏以及更多的功能,但作为初学者,我们构建一个可以开始和结束的菜单栏即可

 

 如图,我们首先构建三个文件,分别包括:test.c源文件(用来进行游戏整体的进程),hanshu.c源文件(用来构建一些游戏逻辑的函数),shengming.h头文件(用来进行函数和各种定义的声明方便两个源文件使用以及联系)

创建好后,我们就在我们的主文件test.c文件进行菜单的构建,注意,test.c文件就是整个游戏实现的主文件,起到指挥的作用,我们大致的思路就是把各种函数加入到主文件当中。

扫雷游戏的实现_第1张图片

 首先常规的构建C语言基本框架,由于我们要实现一个与菜单的交互,所以我这里采用了输入数字的形式,即为,我输入1开始游戏,输入0结束游戏,由分支循环的switch语句的特点,输入常量即可进入相对应的分支语句,完美契合了我们的要求,所以我们采用switch语句,同时,为了满足游戏可以不限次进行,我们也要在switch语句之外去嵌套一个循环语句,考虑到for,while循环的特点,我们的要求是无条件开始游戏,所以我们采用do while循环的方式去进行书写,这样的话第一次直接执行循环语句更加方便,如上图所示(注意,每一条分支结束后都要加break,这是switch的特点,否则会一直进行。同时,为什么要用0来结束游戏呢?这里也是利用了循环遇到0判定为假自动结束的特点,你若想用别的数字,只需要特殊赋值一下即可,同时,我这里是采用了一个枚举体来书写,初学的同学只要用1 0这些数字写即可。扫雷游戏的实现_第2张图片

 下一步我们来构建一个菜单,菜单需要窗口,我这里用*来构建,首先我们在do while循环外侧调扫雷游戏的实现_第3张图片

 用我们的菜单函数,由于不需要输入参数即为空括号,返回值为void,然后如上上图所示,构建菜单函数即可,菜单函数书写完毕后我们进一步执行,让我们对应输入一个数字1或0从而选择进入游戏或者退出游戏,利用scanf函数即可完成输入,完成后我们的效果如图扫雷游戏的实现_第4张图片

扫雷游戏的实现_第5张图片

扫雷游戏的实现_第6张图片

 这一步完成后,我们的菜单就宣告构建完成了!下一步就是最关键的游戏部分的构建了!

2.游戏内容的构建:

首先先思考我们的菜单的构建,我们的思路就是输入1就开始游戏,输入0就结束游戏,假如我们进行游戏并胜利或者失败都会循环重新选择开始游戏还是结束游戏,所以,我们在case PALY这个分扫雷游戏的实现_第7张图片

 支下进行游戏的书写,如上图,然后我们开始书写我们的game函数内容扫雷游戏的实现_第8张图片

 如上图所示,我们可以清楚的看到,正如我前面所说,我们游戏的具体内容的实现是依靠一个又一个函数的按次序排列来实现的,这么多函数的使用,如果放在主程序test.c里就非常混乱,倘若我们以后想要优化或者修改的话效率很低,所以这个时候我们就要使用另外两个文件,hanshu.c源文件和shengming.h头文件,在hanshu源文件里,我们主要是书写游戏实现的一些函数内容,而在头文件里,我们则需要书写各种声明和定义,这里,我们首先说明一下我们的头文件。扫雷游戏的实现_第9张图片

 如上图,这是我为扫雷游戏构建的头文件的整体内容,我们可以很清楚的看到,这里面都是定义内容和函数声明,到这里,我想头文件的作用就显而易见了,它相当于一个记录表,对应着各种函数和定义的门牌号,想要使用函数就需要这个记录表,所以,在我们的两个源文件里就要声明这个“记录表”,这样,就像头文件一样,我们的源文件就可以使用头文件里的全部内容,不报错,如图

 我在两个源文件都引用了头文件,对于我们自己创建的头文件,在引用的时候不会像C语言提供的库函数声明一样,我们是本地创建的头文件,所以使用双引号" "来引用头文件名称,声明好后,我们不但可以使用头文件里面的#define定义的量,也可以直接调用我们在hanshu文件里的各种函数到我们主程序test.c中,就如同我最开始的图那样。好了,现在让我们正式进入游戏部分的构建吧

扫雷游戏的实现_第10张图片

 再次看这张图,注释部分已经为我们书写了这个游戏我们需要进行哪些方面的构建

1.棋盘的创建

在扫雷游戏中,我们首先需要一个限定范围的棋盘,我们这里就以最简单的9*9棋盘为例子,怎样创建棋盘呢?首先思考,棋盘的本质用我们学到的知识可以理解为什么呢?我认为利用二维数组再好不过,二维数组可以清楚的锁定9*9范围内的任意一个格子,所以,我们首先就是创建二维数组来保证掌控所有坐标。如上图1.注释即可,构建二维数组我们也要同时输入行列的数量,但输入行和列的时候我们又要思考一个问题:假如我点开一个位置可以扫描到周围8个格子,也就是3*3的范围,那么假如我输入边界只能扫描到6个格子怎么办?我当时的思考是,那就加一个if语句就好啊,但是如果加if语句的话,要另外书写一套程序,包括判定程序和显示周围有几个雷,这样太麻烦了,所以我们不妨在9*9的格子外在额外提供一圈,这一圈全设为空位没雷,这样的话,即使是我输入边界的坐标,它也能以3*3的范围扫描,直接节省了很多书写步骤,因此,我们构建一个11*11的二维数组,那输入行为11,列为11就好了?

你先等一等,那如果我要改为100*100呢?或者我要改为行列不相等的呢?你可能会说那就到时候再改不就可以了么?但那样的话操作起来很麻烦,扫雷游戏还好,倘若是更大的项目,你岂不是要一个数值一个数值去改?所以这里我们就要使用头文件来定义变量了,如图,打开头文件扫雷游戏的实现_第11张图片

 由于我们的数组构建是11*11,但我们后续的布雷和排查用的是9*9,所以不妨利用#define首先定义行列都为9,这样我们以后使用ROW或者COL的地方即代表着数字9,同时,我们再定义ROWS COLS为ROW COL分别加2即为11,在后续的修改中,我们只要对头文件的定义进行修改,即可完成数值的修改,十分利于后续的维护。

扫雷游戏的实现_第12张图片

 如图这样,我们就完成了二维数组,这里你可能疑问为何要构建两个数组呢?由于我们是简易版本的扫雷,倘若都放在一张图里的话,棋盘会很乱,所以我们分开成两张棋盘,在第一张我们负责记录你要输入的位置以及周围有几颗雷,在第二张我们负责查看雷的布置,如下两张图所示:

扫雷游戏的实现_第13张图片

 我们的第二张棋盘是不会显示给你的,我们在游戏时只利用第一张棋盘去进行游戏的内容,只有在我们踩雷或游戏结束时才显示第二张棋盘。

2.棋盘的初始化

二维数组创立好后,我们首先要对两张棋盘分别进行初始化,这里我们就要构建一个初始化函数,我命名为defineboard,函数要传入二维数组本身,以及我们的RWOS和COLS,我们既然初始化就要对整体的11*11初始化,在前面也提到这个事情,这里我们定义第一张游戏棋盘的空格全盘由*来初始化,第二张则是由0来定义,用1表示雷,注意,我们这里的’1‘,’0‘ ’*‘都是字符,这个要记住,后面很重要,先留一个悬念。函数内部的写法如下:

扫雷游戏的实现_第14张图片

 注意,我们要在头文件内部也声明一下

注意我们传参的对象是,数组,行列的整数以及一个字符,这里这个字符很巧妙,因为这样我们就可以灵活传入0 1或*,只需要写一个函数即可实现 。

这些都写好后,我们游戏函数就写好了创建的过程扫雷游戏的实现_第15张图片

再次拿出这张图,我们接下来就开始打印棋盘

3.打印棋盘,在我们创建好并初始化好棋盘之后,我们就要打印棋盘了,打印之前,我在棋盘创建的结尾说到我们玩游戏时要利用的是第一张*棋盘而不是带有数字的棋盘,所以,我们这里构建好打印函数后首先要打印*棋盘 ,我这里构建了一个printfboard函数用来打印棋盘,我们输入的参数还是二位数组showboard以及行和列,值得注意的是我们这里输入的就是9*9了,因为我们打印出来的结果就是9*9,函数内部主题如下

扫雷游戏的实现_第16张图片

 首先我们要在函数开始和结束打印出两行分割线,,然后利用双循环来依次打印出我们的棋盘的所有位置的字符,注意我这里是从1开始到row结束,同时你要注意我传入二维数组的行数和列数,是ROWS和COLS,这就为我提供了最外层一个空行,空行除了用来放字符0,也可以用来标识行号和列号,比如我的第一个循环i从0开始,与后文的循环i,j从1开始,这样就完美的不仅避开了串行也实现了对应标识,效果如下:

扫雷游戏的实现_第17张图片

 扫雷游戏的实现_第18张图片

 打印完第一个棋盘,我们就进入下一步,布雷。

4.布雷:

在扫雷游戏中,布雷是随机的,也就是说,我们需要构建一个随机的行和列的生成的程序,使雷可以在9*9的棋盘上随机散步,联想到之前学过的猜数字游戏生成的数字的方式,我们这里也采用相同的方式:如图:

         

 利用srand函数,传入一个时间戳的随机参数,从而构建一个随机数的种子,然后:

扫雷游戏的实现_第19张图片

 我们创建一个布雷的函数setboard,同样传入二维数组和行列,在这里我们打算布置10颗雷,如同ROW和COL一样,我们也在头文件中将其10定义为BOOM(在这里我不演示了,就像图中显示的一样,我们这里利用while循环,同样利用count从BOOM到0自动跳出循环的特点,在循环内部生成10颗雷,用rand()%row+1和rand()%col+1来随机一个1到10的随机数,这样,我们的布雷函数就写好了。

在布雷的后面,我在这里加上了一个打印雷区布局棋盘的函数(其实没必要,我主要是为了测试游戏的运行逻辑,你实际上不用写这个函数,只打印*棋盘即可)

扫雷游戏的实现_第20张图片

 在布雷结束后,我们就即将进入最后一个步骤,即排查雷和判定游戏结果。

5.排查雷和判定游戏输赢:

我将排查雷的函数命名为cleanboard函数,在这个函数内部,我们就需要两个棋盘都要打印了,因为游戏结束时无论输赢都会将布雷棋盘显示出来告诉玩家你踩雷了。函数内容如下:

扫雷游戏的实现_第21张图片

由于这里要求我们输入坐标,所以我们默认scanf输入x和y作为行和列,那么对于输入者,我们除了输入规定的坐标外,还有可能输入坐标范围外的坐标,所以我们这里首先考虑两个大的分支,即在坐标范围内和在坐标范围外,就如同我写的else语句情况,首先确认如果输入到坐标范围外,即输出无效的输入,倘若输入坐标范围内,我们就考虑踩雷和不踩雷两个分支情况:1.假设踩雷,即你选的坐标对应的为字符’1‘,那么输出踩雷语句同时引用打印棋盘函数将雷区棋盘打印出来,游戏结束下一轮开始。2.假设没踩雷,那么我们就要扫描这个坐标为中心的3*3范围内有几颗雷并将数目以字符串的形式显示在你选择的坐标对应的位置上,比如有3颗雷,则这个位置对应为3,如果你71颗空位都选出来,则判定游戏结束,输出成功语句,进行下一局游戏,对于这个71个空位,我们采用循环的形式,也就是定义一个win变量初始化为0,每成功一次就加一,直到加到等于总格子数减去雷数即判定玩家成功,并显示雷区棋盘。

以上就是我们这个函数执行起来的逻辑,下面我们来详解。

a.对于如何将字符变为对应的3*3范围内对应的雷区个数的字符,这里我们提供一种方法,首先,我们要明确一个概念,C语言中,每一个字符都对应一个ASCII值,相应的字符串相减得到的结果是其ASCII值相减的结果,例如’1‘-’0‘就等于1,因为’1‘与’0‘ASCII值差1,同理,一个字符加上对应的整型也相当于ASCII值去参与运算,从而得出一个ASCII值对应的字符,有了这个概念,我们就这样实现雷区的显示,首先在没踩雷的分支语句内构建一个函数research,我们输入参数为雷区棋盘和我们输入的坐标x和y,然后进入函数内部,如图:

 我们在这个函数内部,让它返回一个整型,其目的是与’0‘相加得出一个对应的字符来显示雷的个数,我们让这个函数返回以x和y为中心的周围3*3格子内的对应的字符分别于’0‘相减从而得出一个总的ASCII值,这样我们就成功的实现了显示雷区的操作。

最后一个函数构建完毕后,我们的扫雷游戏就可以正常运行了!很简单是不是?但这个小游戏依旧有很多值得优化的点,比如插旗,如何让空白区展开,如何设置不同难度的棋盘,如何实现积分表,这些东西都值得大家探索,希望大家可以自行去实现。

游戏运行实测:

扫雷游戏的实现_第22张图片

扫雷游戏的实现_第23张图片

扫雷游戏的实现_第24张图片

扫雷游戏的实现_第25张图片

            

你可能感兴趣的:(游戏专栏,游戏)