今天我们就来写一个简易的三子棋游戏吧!
首先,我们还是简单地介绍一下简单的三子棋(tic-tac-toe)游戏吧~
一开始听到三子棋这个名字的时候,我还有些懵……三子棋是啥玩意儿?我只听说过五子棋啊……
后来才知道,原来是三子棋就是我小时候经常和小伙伴画着玩的井字棋,可能你光听名字也不知道这是啥,但是我猜你看到图片就明白了。上图!
当当当当~
敢问这是不是90后小伙伴们的童年回忆???(暴露年龄了!!!)
哈哈哈哈哈相信看完上图大家应该就都明白三子棋是怎么玩的了吧~那我们接下来就进入正题:C语言实现三子棋游戏。
程序员虽然是敲代码的,但是代码前的分析尤为重要噢~
我们需要先分析一下整个游戏的逻辑以及实现的方式,才能更加顺利的写出好的代码噢!
首先,我们在VS2019中建立一个新的解决方案。
为了使我们的代码更加有序整洁,这里我们创建一个test.c文件用于测试整个游戏程序的执行,再创建一个game.c文件用于存放实现三子棋游戏所需要调用的函数,相应地创建一个game.h头文件。
首先,像我们之前写过的猜数字游戏(点击此处可跳转)一样,我们要先给出一个菜单,让用户进行选择(玩或者不玩),并且用户玩完之后可以再次进行选择。
然后,我们要开始实现三子棋游戏。
那么具体如何实现呢?
我们先看看下图:
想象一下,从前我们和小伙伴玩三子棋游戏的时候,是不是得先有纸和笔画出一个井字格来,然后再开始下棋。
下棋又分为玩家下棋和**电脑下棋 **,玩家的棋子为“*”,电脑的棋子为“#”,并且我们需要把下棋的数据记录下来,确保落了子的地方不会再被落子。
最后每落一子我们都要对结果进行判断。
而每一次判断可能出现的结果有4种:玩家赢、电脑赢、平局和游戏继续。如果游戏继续,则继续下棋,否则,输出游戏结果,返回到游戏菜单。
以上就是三子棋的整个游戏实现逻辑,我们将其中的重点总结如下:
- 菜单
- 打印井字格
- 下棋
- 判断结果
菜单是实现和之前的猜数字游戏一样,这里就直接给出代码了,如果有想要了解这一部分的代码是如何写的,可点击此处跳转到猜数字游戏的文章中了解。
#include
void menu()
{
printf("**********************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("**********************\n");
}
int main()
{
int input = 0;
do
{
menu();//打印菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)//根据intput的值做出响应
{
case 1:
printf("开始游戏\n");
//进入三子棋的游戏
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
由于我们的棋盘是一个九宫格,并且在这个九宫格中还会存放游戏中下棋的数据(哪个位置放玩家的棋子,哪个位置放电脑的棋子)。
所以,这里我们应该创建一个3×3的二维数组。
由于玩家和电脑的棋子是用“*”和“#”来表示的,所以我们创建一个字符类型的数组。
由于后面下棋时也会反复用到这个二维数组,在每次传参时我们都会将数组的行和列作为参数传给函数。
因此,我们不妨在头文件中将行和列分别定义为一个常量,这样代码的可读性将会更高,并且以后如果要对行和列进行更改,就可以直接在头文件中对相应的值进行更改。
接下来我们要对棋盘数组进行初始化。
在最开始没有下棋的时候,我们看到的数组内容是空的,所以我们可以认为初始化棋盘的时候,我们在里面放的是一空格,等到玩家或者电脑下棋的时候,再将其中的值覆盖为“*”或者“#”。
在将数组初始化之后,我们就要打印棋盘了。
首先我们先把打印棋盘函数的调用写出来,并将参数陈传进去:
然后再头文件中对打印棋盘函数进行声明:
最后完成打印棋盘的函数实现:
我们要打印的应该是下面这个棋盘。
我们可以将上面的井字格分割成三大行,一个大行包含一个数据行和一个分割行,每行有三列。
根据以上的拆解,我们就可以写出打印棋盘的代码了。
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//打印数据行
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ",board[i][j]);//打印数据
if (j < col - 1)
printf("|");//打印分割符
}
printf("\n");
//打印分隔行
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
打印完棋盘之后,我们就要开始下棋了,这里的下棋分为玩家下棋和电脑下棋。分别封装为两个函数。
下棋的实现其实就是在棋盘的对应位置中放入代表玩家的的棋子的字符“*”或者代表电脑的棋子的“#”。所以我们还是要将数组的地址以及数组的行和列传参给函数。
但是下棋不是只下一次,如果双方都下完一步棋,米有分出胜负,则要继续走下一步,所以这里我们应该把下棋这一部分的代码放在一个循环里面。
同时我们也在game.h文件中对这两个函数进行声明。
在玩家下棋的函数中,我们要实现的关键有以下几点:
玩家下棋的函数代码如下:
void PlayerMove(char board[ROW][COL], int row, int col)
{
printf("玩家走\n");
int x = 0;
int y = 0;
while (1)
{
printf("请输入坐标:>");
scanf("%d%d", &x, &y);
//判断坐标合法性
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//判断棋盘中是否已经落子
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;//下棋成功后,跳出循环
}
else
{
printf("该位置已有棋子,请重新输入\n");
}
}
else
{
printf("坐标非法,超出范围\n");
}
}
}
注意玩家下完棋之后,要让玩家下的棋被看到,所以我们要打印一下棋盘,同理电脑电脑下棋之后也要打印棋盘。
这是运行程序,分别输入几组下标,结果如下:
那么电脑下棋应该怎么实现呢?
首先,应该生成一个坐标。
而电脑生成的坐标应该是随机的,所以这里我们要先生成随机值。
关于随机值的生成,可参考我之前的博客:【手把手带你搞定】第一个C语言猜数字游戏(超详细教程,不仅学如何写代码,更学方法思路!)
然后再让行(或列)的坐标等于生成的随机数%行数(或者列数),就可以生成一个合法的随机坐标。
生成坐标之后,我们要判断坐标所指向的位置是否已经被占用:
如果没被占用,则在该位置中放入“#”。
如果已经占用,则要重新生成随机坐标,直到得到一个未被占用的位置。
因此,生成随机坐标的代码块也需要放到一个循环中。
电脑下棋的函数代码如下:
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑走\n");
while (1)
{ //生成随机坐标
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')//判断坐标是否被占用
{
board[x][y] = '#';
break;
}
}
}
每次下完棋,我们都应该对棋局的结果进行判断。
而棋局的结果分为四种:玩家赢、电脑赢、平局和继续。其中若得到前三种结果,游戏结束,否则游戏继续。
我们可以写一个返回类型为char的函数,如果函数返回“*”,则代表玩家赢了,如果返回“#”则说明电脑赢了,如果返回“q”则是平局,返回“c”则继续下棋。
当然,在封装 IsWin( ) 函数时,别忘了对其进行声明噢。
首先判断输赢。判断输赢又分为三种情况:分别判断横、竖、对角线方向上相邻三个元素是否均相同且不等于空格。
如果有三个相邻元素相同,说明赢家已经产生了。我们只需要把相同的元素(字符)返回去,便可轻松得到对应的赢家
如果没有赢家产生,说明只剩下平局和继续两种情况,我们可以通过调用一个 IsFull( ) 函数来判断棋盘是否已经放满。
这里就直接上代码了:
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)//遍历数组
{
if (board[i][j] == ' ')
return 0;//棋盘不满
}
}
return 1;
}
char IsWin(char board[ROW][COL], int row, int col)
{
//1.判断输赢
//行
int i = 0;
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] != ' ')//三个元素均相同且不等于空格
return board[i][0];
}
//列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')
return board[0][i];
}
//对角线
if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] != ' ')
return board[0][0];
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
return board[1][1] != ' ';
//2.判断平局
if (IsFull(board, row, col))//调用一个IsFull函数
return 'q';
//3.游戏继续
return 'c';
}
你学会了吗?
如果你觉得本文对你有用,记得点赞收藏加关注噢~
下面我把完整的代码截图一份放这里,并且在gitee中上传三子棋的完整代码,欢迎大家自取~
这是我们入门C语言之后写出的第一个比较长的程序。通过对三子棋游戏的实现,我们对于这两块内容的理解应该更加深刻。
下面给出几点总结:
在写游戏代码时,我们先思考整个游戏程序的大致流程和逻辑,然后再进行代码的编写。先分析,后代码,整个过程应该是十分流畅并且逻辑清晰的。
在函数设计上,我们一般先根据我们的需要把函数的调用写出来,然后再来完成函数内部的代码。
写完一个关键步骤之后,测试(运行)一下代码,保证每一步代码都是按照我们的思路执行的,这样后面如果出错了也更好找问题。