前言
学习新知识固然重要,但如何运用所学的知识解决生活中的问题更为重要。只会学而不会用,何异于纸上谈兵。那么就让我们来看看如何用C语言来实现一个简单的三子棋吧!
目录
前言
一点建议
一、游戏菜单
1.菜单界面
2.开始或退出游戏
二、游戏内容
1.棋盘
2.走子过程
3.判断结果
4.游戏内容拼接
三、界面优化
1. 棋盘界面
2.文字提示和过场停顿
3.清屏
四、拓展内容
1.双人游戏
2.简易AI
由于实现三子棋的代码相比我们学习时所用的代码要长得多,因此我们最好把不同的游戏功能分装到不同函数中分别实现,并且可以分装多个源文件和头文件,方便调用函数,这样我们的代码就不会显得很杂乱,也方便后续对代码的维护和更新。
如图,当我在一个自定义头文件中包含了所需的所有头文件并声明了所有所需的函数时,我只需要在源文件中包含该自定义头文件,即可调用我需要的所有函数。
我们知道,当我们打开游戏,映入眼帘的应该是游戏菜单,在菜单里可以选择进行游戏或者退出游戏,那么我们就需要用C语言写一个简易的菜单界面。
如图,用printf函数打印出一个简易的菜单界面。
如菜单所展示的,我们如何使玩家选择开始或退出游戏呢?这时我们可以分装一个函数,专门用于控制游戏的开始与退出,例如test函数
test函数中含有一个循环体,其实很好理解,当我们玩完一把游戏后就会回到菜单重新选择开始或者退出游戏,写成循环就可以使玩家在不退出游戏的情况下连续开始游戏。那么我们来分析循环内容,首先,调用menu函数,打印一个菜单,然后用scanf函数让玩家选择开始或者结束,这时候我们可以分为三种情况,例如,当玩家输入1时,说明他想开始游戏,那么判断input等于1则进入game函数(game函数为游戏内容,如图中“游戏开始”);还有可能input等于2,说明玩家想退出游戏,那么我们用break跳出循环,游戏结束;最后一种情况,input既不等于1,也不等于2,说明输入错误,那么程序应该给出反馈并让玩家重新选择。
这样一来,三子棋小游戏的菜单就做好了。
初始化棋盘
三子棋的棋盘相信大家都很熟悉,一个九宫格和两种不同的棋子,那么在C语言中实现的三子棋大致如下图
由于下棋是一个动态过程,我们并不能只考虑打印棋盘,还要考虑如何将棋子放上棋盘。我们看到,没放棋子的地方都是空格,而棋子可以用字符来代表,那么我们可以定义一个字符数组来代表棋子, 而棋盘为三行三列,正好可以用一个三行三列的二维数组来代表棋子。那么我们创建一个二维数组。由于游戏开始时棋盘的九个位置均为' '(空格),所以我们应该将数组的所有元素初始化为' '。我们在函数init中实现
打印棋盘
当我们完成初始化后,就需要把棋盘呈现在玩家面前了,我们可以用一个print_board函数实现。这里我们有两种方式来打印棋盘,一种是像打印目录一样,用多行printf函数实现,另一种是我提倡的,用循环来打印,如图
图中的COL和ROW均为宏常量,值为3,用宏常量的好处在于,当我们需要调整棋盘大小时,仅需调整宏常量的值即可。我们来看循环体,变量i控制行数,变量j控制列数,嵌套循环体中的if else语句用于判断是否为最后一列,若为最后一列,则不打印|。下方的if语句同理,打印|和---主要是为了优化界面,方便玩家观察。而棋子的打印才是这套循环的重点,我们可以看到printf函数中有格式符%c用于打印数组元素arr[i][j],而这个二维数组正好对应了棋盘的九个位置。在前面我们已经用init函数初始化了棋盘,那么此时数组的九个元素均为' '(空格),所以我们打印出来的棋盘就是这个效果
玩家走子
棋盘已经制作完成,接下来就该下棋了,这里我们设计一个player_move函数,当玩家输入一个坐标时,我们将该坐标对应的数组元素替换成玩家的棋子,这样便可以实现落子。当然,我们需要做一个简单的判断,如果玩家输入的坐标在棋盘外,或者该坐标上已有棋子,那么当然不能成功落子,需要重新输入坐标下棋,我们用以下逻辑实现
当玩家输入坐标时,判断坐标是否合法,如果合法,则将该坐标上的数组元素(空格)替换为玩家的棋子(图中为'X'),并跳出循环,若坐标非法,则循环重新输入坐标。
电脑走子
玩家走子后轮到电脑走子,电脑走子的逻辑跟玩家走子差不多,但是要让电脑选择一个坐标落子,我们可以用基础的随机数让电脑随机落子,如下图
使用srand函数,rand函数和time函数生成随机数,并生成随机坐标,按照玩家走子的逻辑判断该坐标是否合法,若合法则将该坐标对应的数组元素(空格)替换为电脑的棋子(图中为'O')。
玩家和电脑交替走子,总不可能一直走下去,需要判断对局结果。我们可以把结果分成三种,第一种是玩家赢,第二种是电脑赢,第三种是平局。判断玩家赢和电脑赢的逻辑可以说完全相同,当一方的棋子连成三个则获胜,我们可以用以下函数实现
首先判断赢,分成三种情况,第一种为一行连成三子,那么在相同的行数上,即图中i相等的情况,判断同一行上第一列的元素等于第二列的元素,第二列的元素等于第三列的元素,且均为玩家棋子('X')那么判断玩家胜利,同理,第二、三种情况分别是一列连成三子和对角线连成三子,判断方法基本相同。而电脑胜利的判断方法和玩家胜利相同,可以看到图中函数的参数中有一个字符参数,这是用来区分玩家和电脑的,若传入的字符为'X'则该函数判断玩家是否获胜,若传入的字符为'O'则该函数判断电脑是否获胜。
接下来是判断平局,我们知道,当三子棋九个格子全部下满时,若还无人获胜,则判断为平局,要实现这个逻辑就很简单了,我们只需要定义一个变量,每次玩家或电脑下完棋后让这个变量的值加一,当这个变量值等于9时,判断为平局
如图, 创建变量turn,指针变量p指向turn,把指针p传给player_move函数,当玩家成功落子后,令*p的值加一即可。
主要的游戏功能已经分装完成,那么接下来我们就要把这些功能组合起来编成一个完整的游戏了。这里我分享我的实现方法,方法不唯一,还有更高效、更简洁的实现方式欢迎在评论区讨论
首先,进入游戏后,我们初始化并打印棋盘,此时打印出来的为空棋盘
接着用循环结构让玩家和电脑的走子步骤重复发生,首先进入player_move函数玩家走子,然后判断judge函数的返回值(注意,这里的judge函数为整型函数,可回判断结果步骤查看返回值),若为1则判断玩家胜利并跳出循环,若为0则执行后方语句。接下来判断是否平局,这里先判断平局是因为三子棋的第九颗棋子一定是由先手方下,在这里就是玩家下出第九颗棋,所以在palyer_move函数结束后直接判断是否胜利,平局。若将平局判断放至电脑走子后,则会出现电脑无法找到地方落子而导致程序无限循环,无法结束。判断完非平局后,则轮到电脑走子,并判断电脑是否获胜。若游戏未结束,则打印新棋盘(玩家和电脑走子后的棋盘),进入下一轮走子。
至此,三子棋的主要结构就已经编译完成了,快去试试看吧!
作为一个游戏,界面优化必不可少,良好的界面可以提高玩家的游戏体验。我们制作的三子棋当然也可以做界面优化。
棋盘为游戏的主要界面,当然需要美化,首先就是我在print_board函数中所写的|和---,用于分割棋盘,让棋盘看起来更加简洁、工整。另外就是一些外包装和文字修饰之类的,我这里做了一点简单的优化,来看看效果吧
在游戏菜单界面和棋盘界面,我们可以加入以下文字提示,以提示玩家进行操作
当游戏结束时,我们可以做一个过场停顿,将棋盘展示出来,等玩家下达指令后再返回菜单
C语言的printf函数会按顺序从上到下打印,如果我们不做清屏,那么我们会看到这种情况
这样就显得很不美观,但我们在正确的位置加上这段清屏代码,我们的游戏界面就会美观很多
按照人机对战逻辑,我们很容易设计出一套双人对战逻辑,只需要将电脑走子的步骤换成玩家2走子即可,这里就不详细展开讲解了,看看效果吧!
如果电脑只是随机走棋,那么挑战性就低了很多,我们可以编写一套简单的判断系统(当然以我现在的实力写不出人工智能),当玩家即将连成三子时,电脑会优先堵住玩家,而当玩家还未连成两子时,电脑会优先连成两子。这个逻辑其实非常简单,大家可以试试着手实现。