目录
前言
相信大家都玩过三子棋,也就是我们口中常说的井字棋。今天我就来教大家如何自己在电脑上制作一个井字棋吧。
设计思路
- 打印棋盘
- 玩家下棋
- 电脑下棋
- 判断输赢
程序设计
在这里我们先规定,玩家的棋子为‘*’,电脑的棋子为‘#’。
并且我们将棋盘的大小做了宏定义
#define ROW 3 #define COL 3
1. 印打棋盘
程序的第一步就是先打印棋盘了, 但是在打印棋盘之前,我们应该先创建并初始化一个二维数组来表示玩家和电脑的落子的位置。
char board[ROW][COL] = { 0 };
void init_board(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] = ' ';
}
}
}
需要保证数组里面为看不见的 ‘ ’(空格)符,这样在接下来打印棋盘的时候就不会发生位置不正确的错误。
接下来就是打印棋盘了。
void display_board(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");
}
}
}
一行为玩家与电脑的下棋行,一行为分割行。每一行打印完需要换行。每次的循环在打印分割线或分割行的时候总是会少打印一次的,因此可以在循环打印分割的代码中加入判断语句,例如打印 3 x 3的棋盘时,需要判断row和col是否少于3-1。这样就可以解决每次多打印一次分割的问题。
2. 玩家下棋
此前我们已经规定了玩家的棋子为‘*’。
首先,玩家需要输入一个坐标来表示他想要落子的位置。为了具有user-friendly的程序,当用户输入坐标时,我们应该主动在坐标中减1以达到控制对应的位置。
普通人一般都不了解计算机中数组的存储是以下标来表示的,例如玩家输入1 1,实际对应的棋盘数组的下标应该为0 0。
void play_move(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 > 0 && y > 0 && x <= row && y <= col)
{
//判断坐标是否被占用,若没有,则落子
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("请输入正确的坐标\n");
}
}
else
{
printf("请输入正确的坐标\n");
}
}
printf("\n");
}
虽然一开始棋盘都是空的,玩家可以随意下棋,但是当电脑下完棋之后玩家就无法再落子在该位置上。因此,我们可以使用循环来解决,直到玩家落子的位置满足以下两个条件为止。
判断坐标是否在规定的位置。
判断坐标是否被占用。
当两个条件满足时才允许玩家在此处落子。
3. 电脑下棋
此前规定电脑的棋子为‘#’。
电脑的落子坐标我们需要使用到随机数rand()函数。但在使用rand()函数前需要使用到
srand()函数来创建一个随机数触发器。
srand() 需要一个随时可以变化的数,因此我们可以传一个一直在变化的时间戳。
关于srand()函数详细的用法,大家可以在www.cplusplus.com中查找,在这就不细说了。
srand((unsigned int)time(NULL));//最好可以放在main函数里面第一句
因为我们现时的棋盘为3 x 3,所以只要在随机数中取模3就可以得到0~2的数字。
int x = rand() % row;
int y = rand() % col;
如果说得到的坐标已经被占用了我们该怎么办呢?此时应该用到循环来解决问题,当坐标被占用了就再生成一个新的坐标,直到坐标可以被落子为止。
void computer_move(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
电脑下棋的代码相对少点步骤,因为不需要告诉玩家随机生成的坐标无法落子,只需要程序不断获取坐标就行。
4. 判断输赢
光有玩家和电脑下棋还不行,还要同时判断双方落子后是否出现结果。
我们简单的分析以下,三子棋一共有四种结果:
- 玩家胜利
- 电脑胜利
- 平局
- 继续
此时我们可以为每个结果附上返回值。
玩家胜利--->'*'
电脑胜利--->'#'
平局 --->'D'
继续 --->'C'
三字棋胜利的条件:
某一行、某一列或某条对角线最先形成同一种棋子时为胜利
三子棋平局的条件:
棋盘上的坐标全部落子,且没有人获胜
当棋盘没有出现胜利或者平局时应该继续下棋。
所以对应的代码如下设计(只适用于3x3棋盘):
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][0] != ' ')
{
return board[i][0];
}
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
//检查两个对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')
{
return board[0][0];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
{
return board[0][2];
}
//检查棋盘是否已满
if (is_full(board, row, col))
{
return 'D';
}
return 'C';
}
在检查是否平局时,可以使用for循环来检查棋盘中是否还含有‘ ’(空格)。
当is_full()函数返回0时,表示棋盘还有位置可以落子
返回1时,表示棋盘已经满了,无法继续下棋。
int is_full(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++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
最后在函数调用端中也可以透过返回值来确定是否结束游戏。因此整个游戏的逻辑就可以列出来了。
void game()
{
char board[ROW][COL] = { 0 };
char ret = 0;
//初始化棋盘
init_board(board, ROW, COL);
//打印棋盘
display_board(board, ROW, COL);
while (1)
{
//玩家输入
play_move(board, ROW, COL);
display_board(board, ROW, COL);
//判断胜利
if ('C' != (ret = is_win(board, ROW, COL)))
{
break;
}
//电脑输入
computer_move(board, ROW, COL);
display_board(board, ROW, COL);
//判断胜利
if ('C' != (ret = is_win(board, ROW, COL)))
{
break;
}
}
if ('*' == ret)
{
printf("恭喜玩家胜利\n");
}
else if ('#' == ret)
{
printf("电脑胜利\n");
}
else if ('D' == ret)
{
printf("平局\n");
}
}
总结
希望大家看完本篇文章后,可以对c语言有更进一步的认识,也希望大家可以认识如何设计和制作三子棋游戏。因为是第一次发布自己的文章,若文章内容有不当之处,也希望大家多多包涵。
最后我会放上源码供大家学习之用。
源码链接:
c-language/game1_7_28/game1_7_28 at main · qingjiuz/c-language · GitHub