1.首先向大家介绍一下三子棋的原理
首先我们需要有一个棋盘,初始时棋盘全为空,我们可以通过数组的方式,将空格改为一定的符号,当有任一行或一列或对角线为相同符号时则有一方胜出。简单介绍了三子棋原理后接下来为大家讲解分步目标及代码如何实现。
2.分块代码实现
1>首先我们需要打印一个菜单,引导用户输入决定玩游戏或是退出游戏,我们使用函数实现这一步骤
void menu()
{
printf("************************\n");
printf("***** 1. play ******\n");
printf("***** 0. exit ******\n");
printf("************************\n");
}
我们将此函数嵌套在设定的一个test函数中,这样做的好处可以让我们主函数尽可能的简介,主函数只需调用test函数就可实现整个工程,我们使用switch case来接受用户的输入值并给出相应结果
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);
}
在这一段中srand的使用我们后面再说,接下来为大家介绍game函数中游戏的实现。
2>game函数的实现
前文说到我们需要使用数组来实现三子棋
所以我们首先要初始化一个数组,既然是三子棋,我们需要一个三行三列的二维数组,为了后续维护代码方便,我们首先新建一个头文件,在头文件中定义一个ROW和COL,将这两个值作为行和列,值均为三。
#define ROW 3
#define COL 3
这时候我们就可以很方便的创建一个二位数组
char board[ROW][COL] = { 0 };
如在前文提到的,我们需要让打印出来的棋盘在游戏开始时时空的,所以卫门将数组初始化为空格,我们使用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] = ' ';
}
}
}
在将数组初始化后我们需要将棋盘打印出来,要打印如前文所示有分割的键盘,我们可以利用两次循环,外循环每次循环打印一行,内循环打印一列,要注意到棋盘的右边和下面是没有分割的,我们加入一个if判断来控制分割只在棋盘中间,我们使用DisplayBoard函数来实现这一目标。
void DisplayBoard(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++)
{
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");
}
}
在test函数中调用上述两个函数后,我们可以打印出一个空气盘,接下来为大家讲解玩家和电脑分别如何下棋。
首先在下棋前,我们需要判断一下棋盘中是否还有空位,若棋盘已满,则无法继续下棋。在判断完之后,我们假定玩家下棋符号为*,电脑下棋符号为#,我们使用符号替换相应坐标中数组的空格。我们使用player_move函数来实现玩家下棋。
void player_move(char board[ROW][COL], int row, int col)
{
printf("玩家下棋:>");
int x = 0;
int y = 0;
while (1)
{
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");
}
}
}
在玩家下棋后,电脑同样需要下棋,这时候我们给出一个随机值,上文提到的srand就在这用到了,我们用rand()%ROW或COL就会的到一个0~2的数字,再判断相应位置是否为空,若不为空则将相应数组中的空格替换为#,若为空则给出其他随机数字。
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;
}
}
}
我们实现了玩家和电脑的下棋后,我们需要编写一个函数来判断双方的输赢。我们使用Iis_win函数来实现这一目标。(这里简单介绍一下is_win函数原理,我们判断是否有7一行或一列或对角线为同一符号,若有则返回这一符号,若无则判断棋盘是否为空,判断后再返回对应的符号,在这我们设定:
若满足一行或一列或对角线为同一符号,则返回相应符号
若不满足上述条件且棋盘判断为不满则返回'Q',若判断为满则返回'C'。
具体代码如下:
har 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[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//判断平局
if (1 == is_full(board, row, col))i
{
return 'Q';
}
//继续
return 'C';
}
我们再game函数中再判断is_win的返回值便可以很好地判断双方的输赢和是否继续,game函数完全体如下
void game()
{
char board[ROW][COL] = { 0 };
InitBoard(board, ROW, COL);//初始化棋牌
//打印棋盘
DisplayBoard(board, ROW, COL);
//下棋
char ret = 0;
while (1)
{
player_move(board, ROW, COL);
DisplayBoard(board, ROW, COL);
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
computer_move(board, ROW, COL);
DisplayBoard(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");
}
}
3.总结
我们为了代码的简介明了,我们使用game.g的头文件,且将所需函数都声明在里面,再建立game.c的源文件,将函数具体实现放在里面,分别再test.c和game.c中引入头文件后,就可将代码分块,简介易懂。
具体代码请见c语言/2021.11.20 · 吴昌盛/code - 码云 - 开源中国 (gitee.com)
欢迎各位讨论点赞,感谢大家的阅读。