扫雷简易版(提供思路和源代码)
2. 逐个实现每一步(忘了步骤可以直接跳到目录,每个步骤会标明应该回到哪安排问题,上次的strcpy放在后面介绍完,这次我们讲点稍微有意思的,用C语言来写扫雷游戏(只是低配版的,可以优化的部分以问题形式提出来,能力有限,我也没有想出来,欢迎评论区讨论,我们共同进步)如果遇到不会的知识点,请尽量自己去补,有些我会说明(看视频或者我的博客有相关介绍),主要思路,而不是对某个语法的介绍1. 明确整体框架
安排问题,上次的strcpy放在后面介绍完,这次我们讲点稍微有意思的,用C语言来写扫雷游戏(只是低配版的,可以优化的部分以问题形式提出来,能力有限,我也没有想出来,欢迎评论区讨论,我们共同进步)
如果遇到不会的知识点,请尽量自己去补,有些我会说明(看视频或者我的博客有相关介绍),主要思路,而不是对某个语法的介绍
———————————————————————————————————————————
1. 明确整体框架(想要呈现的效果)
1.1 游戏开始,我们需要一个游戏界面:根据指令选择进入游戏或者退出游戏,这里可以创建一个函数,打印游戏界面(这里的游戏界面两个作用:设计封面和给玩家提供指示来操作游戏)
返回步骤一
1.2 如何进入或退出游戏?根据游戏界面的指示进行选择
返回步骤二
1.3 前面是进入游戏的指示,游戏的实施过程我们另创一个函数(避免冗长,让代码看起来舒适)
返回步骤三
1.4 现在是游戏的实施过程,我们明确扫雷游戏需要什么,现在讨论可能遇到的问题
返回步骤四
问题一:雷如何放置?选择点的周围有多少个雷?
返回步骤四1
由于我们的扫雷类似一个棋盘(n * n),想要呈现出来,我们首先想到创建一个二位数组,但是回到问题,仅仅一个二位数组真的能把两个过程都实现吗?显然不可能,所以我们需要创建两个二维数组
问题二:数组创建好了,还需要做什么?
返回步骤四2
这里我们需要初始化一下,由于每个数组的元素我都要涉及到,并且有两个数组,这里就需要创建一个函数,专用于初始化
问题三:创建好两个数组 我们如何呈现呢?
返回步骤四3
这里明显我们需要再创建一个函数,专用来打印我们创建好的二维数组(之前那个函数我们只是初始化,并没有打印)
问题四 : 呈现效果有了,但是我们的雷还没放进去,怎么办呢? 应该都能猜到我要说什么了吧
返回步骤四4
是的,我们还需要创建一个函数,专用于放置雷
问题五:雷放好了,就是轮到我们去找一个选择点,让程序判断我们是否被雷扎到了,那么,怎么判断呢?
返回步骤四5
再再再创建一个函数,专门判断是否成立
整体框架我说完了,现在就是一步步来实现
2. 逐个实现每一步(忘了步骤可以直接跳到目录,每个步骤会标明应该回到哪)
步骤一
回到目录1.1 我们创建函数menu()[没有要传递的参数,我们不传参]
想好要打印的游戏界面,这里放一下我的界面:
应该很容易想到menu是如何实现打印界面的过程吧
我就直接放代码,不过多讲解了
步骤二
回到目录1.2 很明显,根据菜单指示,我们可以看到选择游戏开始或结束的方式就是输入数字1或0(可能有人会问,为什么没有数字2,不要急,这点我们后面解释),为了使游戏持续下去,而不是玩一把就停止,这里要用到循环,在三个循环中,我们要用到哪个呢?(不能区分三个循环的一些差别的,可以往前翻 分支与循环(下) 我有提出过),为了使游戏至少执行一次(无论开始或结束),我们用 do,while循环,do里面放执行步骤,while里面放判断条件,每次打开游戏,我们都需要游戏界面的指导,这里把前面刚刚创建好的menu()放入do中,刚刚说了,根据游戏指令,需要玩家输入1或0,需要用到scanf输入的值,我们可以根据输入的值来给出相应的提示(如:输入1,得到游戏开始的提示,输入0,得到游戏退出的提示,我知道又有人要问了,如果我输入其它的数字怎么办,这个我们后面来解释),现在我们输入了值,怎么对输入的值进行判断呢?用if语句判断?当然也可以,但这里我教的是用switch语句判断(个人觉得比这里用起来if语句更方便)[不会用的,翻前面的 分支与循环(上)],这里可以解释上一个问题,用 default ,给出输入错误提示,这里do的执行部分说完了,回到while的判断部分,又可以回到第一个问题,我们用1,0来决定游戏是否进入,因为这里的判断部分,判断是否循环就是根据这个输入的值来判断(非零为真,零为假)
这里讲完了,直接上代码(忽略srand函数(这是改变时间戳,和随机数有关)和game()那两行,和这一步没有关系,是我们后期要讲的步骤,需要后期补上去)
步骤三
回到目录1.3游戏内部的实施,我们需要创建1个函数game(),细心的通过上个图片就能看出,我们把函数放到case 1 窗口后面(很明显嘛,输入1,我们才能调动game函数,执行游戏)现在是内部的实施的具体操作
步骤四
回到目录1.4
步骤四1
问题一 我们提出来了,需要创建两个二位数组,这里我们创建 mine[][](放置雷的棋盘)和 show[][](展示选择点周围雷个数的棋盘) (稍微提一下,括号里面放置行和列,二维数组我没讲,推荐自己先学一下),至于什么类型的二位数组需要我们创造呢?是int 类型的还是 char 类型的呢?
由于我们判断雷是根据 1 和 0 来知道这个地方是否有雷,可能会有人认为这个是数字,我们用 int 类型,但是这里我想说,第二个二位数组(也就是判断选择点周围雷的个数)开始创建时,我们需要隐藏信息,不能让玩家知道雷的个数,我们通常会想到用字符(如#,*等)来初始化这个棋盘,这里得用 char 类型,为了统一我们就两个数组都用 char 类型
呈上图:(至于ROWS,COLS,ROW,COL是什么,后面来说)
步骤四2
问题二 初始化我们创建一个函数InitBoard(),想要让两个数组通过这个函数初始化,首先你得把两个数组作为参数传过去,传过去函数内部怎么实施呢?两个for循环嵌套使用就可以初始化所有元素(这点不懂,嗯,需要多练点循环,具体不能展开,我会讲不完的),在循环过程中,不可避免我们要知道原数组的行和列,所以行和列也就我们传递过去的参数,但是我们在循环的是否会遇到问题,回归到这个函数设计初心(初始化每个元素),我怎么知道这个元素初始化成什么,现在有两个数组,我需要初始化的字符不一样怎么办?把字符当参数传递过去
现在看看我们创建好的函数
内部执行原理
回到刚刚遗留的问题,ROW,COL等等是什么(代表行和列,需要使用去define定义)
格式如下:
(放在首位)
这里先说明:我准备打印9*9的棋盘,应该会有人提问我为什么初始化传入参数的是11*11的(这个在检测雷个数的那一步详细说明)
步骤四3
问题三 这里我们创建一个打印棋盘的函数DispalyBoard,不用想嘛,需要二维数组传入,行和列也要传,但是这里我们打印9*9的棋盘,行和列就是ROW和COL,函数内部实施同样两个for循环嵌套
上图:(个别点单独说明)
步骤四4
问题四 创建函数SetMine(),放置雷,我们得知道雷的个数(自己设定),为了方便改动,我们依旧用define定义(刚刚没讲,为什么不直接写常量,这个是为了后期想随意改动数值时可以方便点,否则后面用到这个常量次数多了,一个个改很麻烦)
放置雷,我们肯定需要把mine[][]这个二位数组传过去作为参数,行和列作为参数(只在9*9的棋盘生成雷),再说具体函数的实行:需要生成两个随机数x,y作为坐标,把雷放进去(有雷为‘1’,无雷为‘0’),(生成随机数的部分自行解决,我没讲过,嗯,找视频看看怎么生成随机数),生成好了之后要注意几点,明确范围,两个随机数的范围应该是1- 9,所以记得%10 + 1(不具体讲),我需要多次放入雷,用到循环,这里我用了 while (for循环应该也可以),最关键的一步,一定要判断是否这个位置是‘0’,否则会造成生成的坐标会有重复的,导致没有放满相应的雷的个数
思路说完了,直接上代码:
步骤四5
问题五 我们来到最后一步啦,坚持就是胜利,现在创建一个函数FindMine(),查找雷嘛,mine[][]数组要作为参数,行和列要作为参数(重点:这里我们终于要提到上个问题,为什么初始化函数放的是11 * 11,这里传参是9 * 9),show[][]数组也要作为参数传入(每判断一个位置,反馈雷的个数)
先放一张图:
这是打印show[][]数组的棋盘
看到这个点,如果我想检测它周围几个雷怎么办?
是不是发现一个问题,判断时我们会发生越界访问(说白了有些位置你无法检测),回到我们的解决方案,我们讲整个棋盘外围扩大了一圈(在初始化的时候),就避免越界访问的情况
放上图:
现在回到如何判断这个部分:
明显就是把这个选择点位置的周围的数加起来(雷是‘1’,非雷是‘0’),所以我们创建一个函数(返回类型是int)去统计数值,这是创建函数MineCount()去统计,进行判断,需要mine[][]这个数组作为参数传入,判断的位置的坐标(x,y)也作为参数传入,返回值我用了ret 去接收
在这个函数里面,虽然是直接相加但还有几点很重要
不要忘记了这个‘0’和‘1’是字符,而不是单纯的数字,做不到单纯的相加减,这里用到ACALL码值,(如:'1' - '0' = 1),这里就可以实现字符和数字的转换
上图:
回到大框架FindMine(),由于返回值是用于FindMine()函数查找雷的个数,所以这个函数应当在FindMine()函数内部的,scanf输入x,y的值,标明查找坐标,如果踩雷,结束游戏,并显示雷的位置,如果没有踩雷,继续游戏(很明显,又用到循环,这里我们还是用while循环,还用到 if 判断)注意几点:
要对输入的值进行判断,不能超出棋盘格的范围
如果把雷排查完了,如何判断胜利,这里采取的方法是设置一个变量(我设置了变量win)等于0,每排查成功一次,就++直到不满足循环条件(这里我用到的条件是win < 总个数 - 雷的个数),为什么不用等于?会造成雷排查完,还没有结束游戏,所以建议在外面继续判断是否两者相等
上图:
重要内容讲完了
———————————————————————————————————————————
补充几点题外话(也不完全是题外话):
1. 这个游戏我是分两个源文件和头文件来写的,这里我没有具体去说明,所以如果放整个代码出来,你会发现有些地方我没说明也不是很清楚为什么(不过好像是头文件那块没说,其它差不多说到了),放也能放吧,就放评论区好了
2. 有些地方不好讲述,如果我展开说,可能要更长时间,我主讲整个过程的思路和一些注意事项,看懂整个流程,需要自己本身也要有基础功
3. 这个只是最简单的扫雷游戏,还有很多值得优化的地方:我直接提出来哪里可以改进:
可不可以输入一个坐标,把周围没有雷的地方展开(用到递归,我的代码还没写,一些大佬这点是做出来了,真有时间,我可以来考虑把这点完善,我已经看懂了他们的递归思路,就是不确定会不会再写一篇博客来说明)
可不可以标记雷的位置(欢迎有人来讨论,目前我还没想到)
可不可以记录游戏的时间(等有人告诉我好吧)
4. 目录创建好了,方便快速回到需要的位置,也给了相应提示,电脑支持使用这个目录,手机我不确定
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)