文章目录
一、三子棋游戏简介
我想大家应该都玩过五子棋,三子棋的玩法和五子棋是一样的,五子棋是不论横竖还是对角先连成一条线就算赢了,而你的对手要想尽办法阻止你,同时也要让自己的五颗棋子连成一条线,而三子棋和五子棋的原理是一样的,只不过从五子变成了三子。
二、游戏实现流程
1.前期准备
要想实现三子棋游戏首先要创建一个名叫三子棋的工程,要创建一个game.c 源文件用来实现三子棋程序中的函数,还要创建一个test.c的源文件来实现三子棋游戏的大体框架,并进行实现游戏逻辑的测试等,以及实现电脑智能化下棋的文件实现电脑智能化.c ,还有要创建一个头文件game.h来实现三子棋函数的声明以及定义一些具有常属性的变量,和声明一些在game.h和test.c中要使用的头文件。
- game.h
- game.c
- tese.c
- 实现电脑智能化.c
2.游戏实现的大体思路
1.游戏大体框架
-
首先我们要实现一个菜单,每当程序运行时会出现一个菜单,显示给我们可以选择的选项
-
其次我们要先搭建好程序的大体结构,这个程序我们的设定是可以无限次进行游戏,直至玩家退出游戏,所以我们可以知道这个游戏应该具有一个主循环,而且这个循环最少要执行一次,所以采用do- while的循环。
-
当我们进入循环后,首先我们要调用菜单,然后提示我们输入选项,当我们输入后,我们可以执行相应的函数,这里涉及一个分支选择的问题,所以我们要使用switch-case来实现,在菜单中我们设置1为play开始游戏,0为exit退出游戏,所以case 1:中应该具有一个函数来实现游戏的具体功能,而case 2:里应该实现游戏的退出,并且还要有一个default来实现错误的判断,如果选择错误提示用户重新输入,最后while的判断条件应该是你输入的选项值,以此来控制游戏主循环的运行。
#include "game.h" #include<stdlib.h> void menu() { printf("********************\n"); printf("******* 1.play *****\n"); printf("******* 0.exit *****\n"); printf("********************\n"); } void game() { //储存数据-二维数组 char board[ROW][COL]; //初始化棋盘 - 初始化空格 InitBoard(board, ROW, COL); //打印一下棋盘 - 本质是打印数组内容 DisplayBoard(board, ROW, COL); char tmp = 0; while (1) { //玩家走 PlayerMove(board, ROW, COL); DisplayBoard(board, ROW, COL); //判断玩家是否赢得游戏 tmp = IsWin(board, ROW, COL, '*'); if (tmp != 'C') { break; } //电脑走 ComputerMove(board, ROW, COL, '#'); DisplayBoard(board, ROW, COL); //判断电脑是否赢得游戏 tmp = IsWin(board, ROW, COL, '#'); if (tmp != 'C') { break; } } if (tmp == '*') { printf("玩家获胜\n"); } else if (tmp == '#') { printf("电脑获胜\n"); } else { printf("平局\n"); } DisplayBoard(board, ROW, COL); } int main() { int input = 0; do { menu(); printf("请选择: >"); scanf("%d", &input); switch (input) { case 1: printf("三子棋游戏\n"); game(); break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,重新选择\n"); break; } } while (input); return 0; }
2.game函数的粗略分析
-
游戏的大体框架已经实现,我们要来实现一下game函数具体是怎么运行的,我们将game函数放在test.c中
-
game函数应包含的功能:
1.首先我们要创建一个二维数组来储存玩家和电脑下棋的棋子;
2.其次game函数应该可以初始化棋盘,当玩家和电脑都未落子时棋盘应为空,需要创建一个可以初始化的函数;
3.game函数应该可以打印棋盘,需要创建一个可以打印棋盘的函数;
4.game函数应该可以实现玩家和电脑分别落子,并在落子后打印棋盘,需要创建玩家落子和电脑落子的函数;
5.game函数应该具有判断谁获胜的能力,并在有人获胜后停止继续下棋,并输出谁获胜,并打印棋盘,需要创建判断获胜条件的函数;
3.game嵌套函数的实现
1.初始化函数 InitBoard 的实现
在每一次开始游戏之前要求保证棋盘为空,故每次开始游戏之前要对棋盘进行初始化,初始化代码如下。
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
board[i][j] = ' ';
}
}
}
2.棋盘展示函数 DisplayBoard 的实现
初始化棋盘后我们要打印棋盘来观察一下,每次玩家和电脑下完棋后我们也要看棋盘,以为每次下棋我们保存在二维数组里,我们要打印棋盘,不光要输出二维数组还要输出棋盘的形状,我们这里对棋盘的形状不做过多要求,如下图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UAQfrXUT-1621075382416)(E:\图片\Cache_4ec102abfdb94e52…jpg)]
对于这个棋盘的实现,我们可以看到,在行上每当我们输出一个棋子时会输出一个数杠,但输出最后一个数时没有竖杠输出,同理列也是如此, 以上想法转换为代码为如下所示。
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)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");//最后一行不设横杠
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
3.实现玩家落子 PlayerMove 的实现
首先要知道玩家落子并不是一次就会成功,有可能或出现落子到棋盘外或者想要落子的位子已经被占用的情况,这个时候就要重新落子,知道落子成功,所以我们应该可以理解为这是一个循环的过程,直达成功落子,否则无限循环,其次我们要明白,我们是将棋子落到二维数组中,二维数组的下标与我们实际输入的落子位置是不一样的,比如我们想将棋子落到棋盘上第一行第一列的位置,实际上数组的下标应该为 0 0, 所以我们可知我们要落子的位置比我们实际数组中储存的位置大1, 其次我们要设置条件防止,落子的位置越界和重复,以上就是实现 PlayerMove需要注意的一些问题。下面我们来看代码。
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家走: >\n");
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");
}
}
}
4.实现电脑落子 ComputerMove 的实现
在实现电脑落子时我们赋予电脑智能的落子,也就是说我们为电脑编写好了如何应对玩家落子的方法,让电脑在阻止玩家获得胜利的同时也有可能获得胜利,实际上,让电脑智能化的主要思想是,通过计算棋盘上的每一个落子点的分值来判断电脑下在哪一个位置是最合适的,遍历棋盘上的每一个位置,判断前位置是否为空,以及当前位置的四周是否为空,如果不为空判断有几颗相连的棋子,考虑1,2,3,4颗棋子相连的情况,并判断相连棋子是否为相同棋子,如是相同的棋子,接下来判断是否为我方棋子(站在电脑的角度考虑)如果是我方棋子则加十分,如果为对方棋子,则加二十分,实际上电脑拦截对手获胜的优先级大于自己获胜的优先集 ,所以我们在考虑分值是要注意这个问题, 在本代码里还考虑了,有三颗棋子相连一边没有棋子的分值,以及有四颗棋子一边没有棋子的情况,以及在中间没棋子两边有棋子的情况,最后通过比较得到最大的值,并在最大的值对应的下标处落子。
具体代码放在GitHub 和gitee中
5. 游戏状态的判断 以及函数的实现
当每一次玩家和电脑都落子后需要判断游戏的状态,以确定游戏的进程,游戏的状态分为四种:
- 玩家胜利 - *
- 电脑胜利 - #
- 平局 - 棋盘已满代表平局 Q
- 未分出胜负继续下棋 C
在这部分我们需要两个函数,一个 IsWin 函数判断输赢,一个IsFull函数来判断棋盘是否已满,在IsWIn函数中我们通过比较每一行每一列,以及对角线上的5个棋子是否连成一条线来判断输赢,如果直到棋盘满了都未曾分出胜负,则视为平局,如果即为分出胜负,也不是平局则继续游戏。
IsWin函数和IsFull函数的实现如下所示:
char IsWin(char board[ROW][COL], int row, int col, char ret)
{
int i = 0;
int j = 0;
int num = 0;
//判断五行
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == board[i][j+1] && board[i][j] == ret)
{
num++;
if (num == 4)//五颗棋子需要进行四次比较
{
return ret;
}
}
else
{
num = 0;//必须是五颗棋子连在一起否者重新计数
}
}
}
//判断五列
for (j = 0; j < col; j++)
{
for (i = 0; i < row; i++)
{
if (board[i][j] == board[i+1][j] && board[i][j] == ret)
{
num++;
if (num == 4)
{
return ret;
}
}
else
{
num = 0;
}
}
}
//判断对角线
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == board[i+1][j+1] && board[i][j]== board[i + 2][j + 2] && board[i][j] == board[i + 3][j + 3]
&& board[i][j] == board[i + 4][j + 4] && board[i][j] == ret)
{
return ret;
}
}
}
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == board[i + 1][j - 1] && board[i][j] == board[i + 2][j - 2] && board[i][j] == board[i + 3][j - 3] && board[i][j] == board[i + 4][j - 4] && board[i][j] == ret)
{
return ret;
}
}
}
//判断平局
//如果棋盘满了返回1,不满返回0;
int ful = IsFull(board, row, col);
if(ful == 1)
{
return 'Q';//平局
}
//继续游戏
return 'C';
}
4.game函数的完整实现
在game函数嵌套函数的实现完成后用一些语句来将其来连接得到完整的game函数,其中需要注意的是,下棋的过程本就是循环的过程,但是始终要有跳出循环的过程,我们设置一个点来跳出循环,并且我们不再循环内部判断输赢,而是跳出之后在判断输赢,这样不仅使IsWin函数更加简洁,并且提高了函数的独立性,在这里我们设置的跳出循环的点是IsWin函数的返回值ret, 如果函数返回值不为C则跳出循环,否则继续循环直到棋盘下满返回Q,跳出循环后,判断函数返回值是还是#,如果是号则玩家获胜,如果实#号电脑获胜,否则为平局,代码如下。
void game()
{
//储存数据-二维数组
char board[ROW][COL];
//初始化棋盘 - 初始化空格
InitBoard(board, ROW, COL);
//打印一下棋盘 - 本质是打印数组内容
DisplayBoard(board, ROW, COL);
char tmp = 0;
while (1)
{
//玩家走
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断玩家是否赢得游戏
tmp = IsWin(board, ROW, COL, '*');
if (tmp != 'C')
{
break;
}
//电脑走
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断电脑是否赢得游戏
tmp = IsWin(board, ROW, COL, '#');
if (tmp != 'C')
{
break;
}
}
if (tmp == '*')
{
printf("玩家获胜\n");
}
else if (tmp == '#')
{
printf("电脑获胜\n");
}
else
{
printf("平局\n");
}
DisplayBoard(board, ROW, COL);
}
三、源码下载及效果显示:
三子棋游戏github源码链接
三子棋游戏gitee源码链接
效果展示:
四、后记
本次代码实现了电脑的智能下棋,但是仍然存在很多不足的地方,比如初衷是要实现三子棋,但是最后却做成了三子棋,没有实现代码的灵活性,其最终最优版本应该是,能够实现多子棋 ,可以任意切换想要玩的模式,还有在判断五子是否连成一线时,代码完成的比较粗糙,可以更加细化完成,再有电脑实现智能化下棋时,模式单一未能实现难易模式的转变等问题,有现在时间精力,及个人能力有限,暂时未能完成优化改进,相信在将来的学习中,随着知识的不断进步,也会将代码修改的更加优秀,如果有大佬看到我的文章,希望您多多 指点帮助,如果你也是刚刚接触C语言,那么我们一起学习进步!