三子棋,也称井字棋,该游戏进行起来十分方便,只需要一支笔和一张纸就可以进行游戏,是上课摸鱼的好游戏(bushi),也是我的童年回忆。
本篇博客将采用C语言来模拟实现简单的三子棋游戏。
人机大战,谁能更胜一筹?

该项目分为三个文件:
test.c:游戏的逻辑game.h:函数声明,符号的定义game.c:游戏的实现其中game.h,game.c为游戏模块,test.c为测试区域。
在进入游戏前,我们需要让玩家通过菜单选择开始游戏或退出游戏。
游戏菜单应该实现三个功能:
而对于游戏,我们至少需要玩一次,应该用do...while循环来完成游戏的重复游玩功能。对于游戏选项,则可以用switch语句完成。
对应代码:
void menu()
{
printf("**********************************\n");
printf("************* 1.play *************\n");
printf("************* 0.exit *************\n");
printf("**********************************\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三子棋\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
运行结果:

友情提示:
test.c的game()函数中调用、game.h中进行声明和相关常量的定义,函数功能在game.c中实现。#include"game.h"。game.c文件中实现的游戏功能。为了提高代码的专业性和方便对代码进行修改,我们将对于行和列进行定义(game.h).
对应代码:
#pragma once
#include
#define ROW 3
#define COL 3
既然是三子棋,那棋盘并不可少。在进行游戏前,我们需要将棋盘中的元素都置为空白,以便后续进行下棋的操作。
棋盘,无非就是一个3 * 3的二维数组,我们在game()函数中定义:
void game()
{
//棋盘的创建
char board[ROW][COL] = { 0 };
//初始化棋盘
init_board(board, ROW, COL);
}
对于棋盘的初始化,我们直接利用嵌套循环即可:
void init_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < col; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';//置为空白
}
}
}
在完成棋盘的初始化后,我们需要将初始化后的棋盘展示给玩家看,棋盘如图所示:

棋盘分为三块:
空格数据空格的形式.|,分隔符的个数每行均为两个,边界不需要分隔符。---,该分割线只存在于第一、二行数据的下方。对于分隔符可以用列来进行限制,对于分隔线则是用行来限制:
void display_board(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");
}
}
}
对弈过程不止一次,直到分出胜负为止,因此在game()函数中应用循环表示。
思路:
1~3,若不在范围内应提示输入错误。*表示,若选择坐标为空白,则将坐标置为*,并退出,让电脑进行后续操作,若选择坐标不是空白,则操作为悔棋操作,应提示重新输入。对应代码:
void player_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家下棋\n");
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");
}
}
}
思路:
rand、srand、time函数来完成相关操作,srand函数在test中调用,rand函数在电脑下棋中用临时变量接收。0~2之间,由行和列的值都为3,直接让rand()%3即可(数值1模数值2结果为0~数值2-1)#表示,由于自动下棋并不需要人为操作,只要条件判断即可,不需要输入提示。对应代码:
void computer_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋\n");
while (1)
{
x = rand() % row;//0-2
y = rand() % col;//0-2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;//注意退出
}
}
}
我们用以下方式判定游戏情况:
*#Qc思路:
game()中应对相应的返回结果做出判断,且在玩家和电脑下棋后均应该判断棋盘是否下满,下满则直接进入判断部分。结合对弈过程和判断在game()函数的对应代码:
void game()
{
while (1)
{
//玩家下棋
player_move(board, ROW, COL);
//玩家下棋后展示棋盘
display_board(board, ROW, COL);
//判断输赢
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
computer_move(board, ROW, COL);
//电脑下棋后棋盘展示
display_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("玩家赢\n");
}
else if (ret == '#')
{
printf("电脑赢了\n");
}
else
{
printf("平局\n");
}
}
game.c对应代码:
static int is_full(char board[ROW][COL], int row, int col)//static修饰为静态函数,只为is_win起辅助作用
{
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 is_win(char board[ROW][COL], int row, int col)
{
//判断行
int i = 0;
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][i] != ' ')
{
return board[i][1];
}
}
//判断列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
}
//判断主对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
return board[1][1];
}
//判断副对角线
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
{
return board[1][1];
}
if (is_full(board, row, col) == 1)
{
return 'Q';
}
return 'C';
}
对于电脑下棋,原先的代码使得在下棋过程中,电脑的操作过于呆板,没有挑战性。所以我们可以对电脑下棋进行升级。
智能化下棋分为两部分:
思路:
我们封装一个函数,被computer_move函数所调用,函数中实现这两个功能。
这两个操作都是对于行、列、主副对角线进行判断,它们的实现逻辑是相同的,所以我们可以把它们在同一函数中实现,电脑赢棋的取决于一条路线上是否有两个#,阻拦玩家下棋则取决于一条路线上是否有两个*。
所以我们在传参时,只需要把元素传入,这样就可以将两部分在一个函数中实现。
对应代码:
static int intelligent_computer(char board[ROW][COL], int row, int col, char ch)//ch为'#'进行下棋,ch为'*'进行阻拦
{
//检查电脑能否获得胜利/阻拦玩家
int i = 0;
//行
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
//判断第一个和第二个的元素电脑是否已下棋,对第三个元素进行下棋/阻拦
if (board[i][0] == board[i][1] && board[i][0] == ch && board[i][2] == ' ')
{
board[i][2] = '#';
return 1;
}
//判断第一个和第三个的元素电脑是否已下棋,对第二个元素进行下棋/阻拦
if (board[i][0] == board[i][2] && board[i][0] == ch && board[i][1] == ' ')
{
board[i][1] = '#';
return 1;
}
//判断第二个和第三个的元素电脑是否已下棋,对第一个元素进行下棋/阻拦
if (board[i][1] == board[i][2] && board[i][1] == ch && board[i][0] == ' ')
{
board[i][0] = '#';
return 1;
}
}
}
//列
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
//第一和第二个已下,下/阻拦第三个
if (board[0][j] == board[1][j] && board[0][j] == ch && board[2][j] == ' ')
{
board[2][j] = '#';
return 1;
}
//第一和第三个已下,下/阻拦第二个
if (board[0][j] == board[2][j] && board[0][j] == ch && board[1][j] == ' ')
{
board[1][j] = '#';
return 1;
}
//第二和第三个已下,下/阻拦第一个
if (board[1][j] == board[2][j] && board[1][j] == ch && board[0][j] == ' ')
{
board[0][j] = '#';
return 1;
}
}
}
//主对角线
if (board[0][0] == board[1][1] && board[1][1] == ch && board[2][2] == ' ')//落子/阻拦主对角线第三个元素
{
board[2][2] = '#';
return 1;
}
if (board[0][0] == board[2][2] && board[0][0] == ch && board[1][1] == ' ')//落子/阻拦主对角线第二个元素
{
board[1][1] = '#';
return 1;
}
if (board[1][1] == board[2][2] && board[1][1] == ch && board[0][0] == ' ')//落子/阻拦主对角线第一个元素
{
board[0][0] = '#';
return 1;
}
//副对角线
if (board[0][2] == board[1][1] && board[0][2] == ch && board[2][0] == ' ')//落子/阻拦副对角线第三个元素
{
board[2][0] = '#';
return 1;
}
if (board[0][2] == board[2][0] && board[0][2] == ch && board[1][1] == ' ')//落子/阻拦副对角线第二个元素
{
board[1][1] = '#';
return 1;
}
if (board[1][1] == board[2][0] && board[1][1] == ch && board[0][2] == ' ')//落子/阻拦副对角线第一个元素
{
board[0][2] = '#';
return 1;
}
return 0;//若无法赢棋或无法阻拦,返回0
}
注:对于原先的computer_move函数也需要做出相应改进,改进内容在完整展示部分。
#pragma once
#include
#include
#include
#include
#define ROW 3
#define COL 3
//初始化棋盘
void init_board(char board[ROW][COL], int row, int col);
//打印棋盘
void display_board(char board[ROW][COL], int row, int col);
//玩家下棋
void player_move(char board[ROW][COL], int row, int col);
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);
//判断输赢
char is_win(char board[ROW][COL], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void init_board(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < col; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
void display_board(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");
}
}
}
void player_move(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家下棋\n");
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");
}
}
}
static int intelligent_computer(char board[ROW][COL], int row, int col, char ch)//ch为'#'进行下棋,ch为'*'进行阻拦
{
//检查电脑能否获得胜利/阻拦玩家
int i = 0;
//行
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
//判断第一个和第二个的元素电脑是否已下棋,对第三个元素进行下棋/阻拦
if (board[i][0] == board[i][1] && board[i][0] == ch && board[i][2] == ' ')
{
board[i][2] = '#';
return 1;
}
//判断第一个和第三个的元素电脑是否已下棋,对第二个元素进行下棋/阻拦
if (board[i][0] == board[i][2] && board[i][0] == ch && board[i][1] == ' ')
{
board[i][1] = '#';
return 1;
}
//判断第二个和第三个的元素电脑是否已下棋,对第一个元素进行下棋/阻拦
if (board[i][1] == board[i][2] && board[i][1] == ch && board[i][0] == ' ')
{
board[i][0] = '#';
return 1;
}
}
}
//列
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
//第一和第二个已下,下/阻拦第三个
if (board[0][j] == board[1][j] && board[0][j] == ch && board[2][j] == ' ')
{
board[2][j] = '#';
return 1;
}
//第一和第三个已下,下/阻拦第二个
if (board[0][j] == board[2][j] && board[0][j] == ch && board[1][j] == ' ')
{
board[1][j] = '#';
return 1;
}
//第二和第三个已下,下/阻拦第一个
if (board[1][j] == board[2][j] && board[1][j] == ch && board[0][j] == ' ')
{
board[0][j] = '#';
return 1;
}
}
}
//主对角线
if (board[0][0] == board[1][1] && board[1][1] == ch && board[2][2] == ' ')//落子/阻拦主对角线第三个元素
{
board[2][2] = '#';
return 1;
}
if (board[0][0] == board[2][2] && board[0][0] == ch && board[1][1] == ' ')//落子/阻拦主对角线第二个元素
{
board[1][1] = '#';
return 1;
}
if (board[1][1] == board[2][2] && board[1][1] == ch && board[0][0] == ' ')//落子/阻拦主对角线第一个元素
{
board[0][0] = '#';
return 1;
}
//副对角线
if (board[0][2] == board[1][1] && board[0][2] == ch && board[2][0] == ' ')//落子/阻拦副对角线第三个元素
{
board[2][0] = '#';
return 1;
}
if (board[0][2] == board[2][0] && board[0][2] == ch && board[1][1] == ' ')//落子/阻拦副对角线第二个元素
{
board[1][1] = '#';
return 1;
}
if (board[1][1] == board[2][0] && board[1][1] == ch && board[0][2] == ' ')//落子/阻拦副对角线第一个元素
{
board[0][2] = '#';
return 1;
}
return 0;//若无法赢棋或无法阻拦,返回0
}
void computer_move(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
int sign1 = 0;
int sign2 = 0;
sign1 = intelligent_computer(board, ROW, COL, '#');//电脑智能赢棋
if (sign1 == 0)//若电脑无法赢棋,则进行阻拦
{
sign2 = intelligent_computer(board, ROW, COL, '*');//阻拦玩家下棋
if (sign2 == 0)//若赢棋和阻拦都不满足,电脑自行下棋
{
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
}
}
static int is_full(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 is_win(char board[ROW][COL], int row, int col)
{
//判断行
int i = 0;
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
}
//判断列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
}
//判断主对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
return board[1][1];
}
//判断副对角线
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
{
return board[1][1];
}
if (is_full(board, row, col) == 1)
{
return 'Q';
}
return 'C';
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("**********************************\n");
printf("************* 1.play *************\n");
printf("************* 0.exit *************\n");
printf("**********************************\n");
}
void game()
{
char ret = 0;
char board[ROW][COL] = { 0 };
//初始化棋盘
init_board(board, ROW, COL);
//打印棋盘
system("cls");//清屏
display_board(board, ROW, COL);
while (1)
{
//玩家下棋
player_move(board, ROW, COL);
//玩家下棋后展示棋盘
display_board(board, ROW, COL);
//判断输赢
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
system("cls");//清屏
computer_move(board, ROW, COL);
//电脑下棋后棋盘展示
display_board(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
printf("恭喜你赢得游戏!\n");
}
else if (ret == '#')
{
printf("很遗憾,你输了,电脑赢了\n");
}
else
{
printf("平局,再来一把吧!\n");
}
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}

到这里,一个简易三子棋游戏也就实现成功了!
我们发现经过智能化的电脑下棋还是很厉害的嘛,我都输了好几次,不过经我挑战后,我赢的次数还是比电脑多的,所以本次人机大战,我宣布,由anduin我,获得胜利!
以上就是C语言实现三子棋的全部内容,如果觉得anduin写的还不错的话,还请一键三连!
我是anduin,一名C语言初学者,我们下期见!
(ps:如果大家觉得粘贴代码比较费劲的话,也可以从我的gitee打包下载源码:戳我得三子棋源码)