我们用 C 语言代码实现了一个简单的控制台版三子棋游戏,代码分为三个部分,分别是头文件game.h
中定义的函数声明以及两个源文件game.c
和test.c、
game.c
文件。
1.头文件(game.h
)部分
首先包含了<stdio.h>
(用于标准输入输出操作,像printf
、scanf
函数的使用)、<stdlib.h>
(包含了如rand
、srand
等函数,用于生成随机数以及内存分配等相关操作)和<time.h>
(获取时间相关信息,用于初始化随机数生成器)这几个常用的标准库头文件。
接着通过#define
定义了ROW
和COL
两个宏常量,都赋值为 3,代表三子棋棋盘的行数和列数。
最后声明了五个函数,这些函数分别用于初始化棋盘、打印棋盘、玩家下棋、电脑下棋以及判断游戏的输赢状态,它们的具体实现在game.c
文件中。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>#define ROW 3
#define COL 3//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);//打印棋盘
void DispalyBoard(char board[ROW][COL], int row, int col);//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);//'X'表示玩家获胜
//'O'表示电脑获胜
//'Q'表示平局
//'C'表示游戏未结束
char IsWin(char board[ROW][COL], int row, int col);
2.源文件(game.c
)部分
(InitBoard
函数)
这个函数的作用是初始化棋盘,通过两层嵌套的for
循环遍历整个二维字符数组(即棋盘),将每个元素都设置为空字符' '
,表示棋盘初始状态下各个格子都是空白的,没有棋子。
(DispalyBoard
函数)
该函数用于在控制台打印出三子棋棋盘的样子。它通过两层循环来实现,外层循环控制行数,内层循环在每行中先打印出每个格子对应的字符(棋子符号或者空白),并且在格子之间用|
进行分隔,每一行数据打印完后,若不是最后一行,还会打印出一行分割线(由---
和|
组成),这样就能清晰地展示出棋盘的布局和当前棋子的放置情况。
(PlayerMove
函数)
这个函数实现了玩家下棋的操作逻辑。首先提示玩家输入下棋的坐标,然后通过while
循环不断接收玩家输入的坐标,并进行合法性检查:一是检查坐标是否在棋盘的有效范围内(1 到row
或col
之间),二是检查输入坐标对应的格子是否为空(没有被其他棋子占据),只有当坐标合法且对应格子为空时,才会将玩家的棋子(用'X'
表示)放置到该格子中,随后跳出循环;如果坐标不合法或者格子已被占用,则会相应地提示玩家重新输入。
(ComputerMove
函数)
该函数用于实现电脑下棋的操作逻辑。首先会提示 “电脑下棋:>”,然后通过while
循环不断生成随机坐标(利用rand
函数生成 0 到row - 1
以及 0 到col - 1
范围内的随机数作为棋盘的行列索引),接着检查该随机坐标对应的格子是否为空,如果为空,就将电脑的棋子(用'O'
表示)放置到该格子中,然后跳出循环;这样电脑就能随机地在棋盘上的空白格子中放置棋子了。需要注意的是,这里使用rand
函数前一般需要在main
函数中通过srand((unsigned int)time(NULL));
初始化随机数生成器,以保证每次游戏电脑下棋的随机性。
(IsFull
函数)
这个函数用于判断棋盘是否已满,也就是判断棋盘上是否还有空白的格子。通过两层嵌套的for
循环遍历整个棋盘,如果发现有任意一个格子的值为' '
(即空白),就返回 0,表示棋盘未满;如果整个棋盘遍历完都没有发现空白格子,那就返回 1,表示棋盘已满。
(IsWin
函数)
此函数用于判断游戏的输赢状态。它首先按照行、列、对角线的顺序来检查是否有一方的棋子连成了三子一线的情况:
在检查行时,通过外层循环遍历每一行,然后比较该行的三个格子的棋子是否相同且不为空白,如果满足条件,就返回该行棋子对应的字符('X'
或者'O'
),表示该行对应的玩家或电脑获胜。
检查列的逻辑类似,通过内层循环遍历每一列,比较每列三个格子的棋子情况来判断是否有一方获胜。
对于对角线,分别检查两条对角线上的格子棋子是否相同且不为空白来确定是否有一方获胜。
如果经过上述检查都没有发现有一方获胜,那么接着调用IsFull
函数来判断棋盘是否已满,如果已满则返回'Q'
,表示平局;如果棋盘未满,则返回'C'
,表示游戏还未结束,需要继续进行。
#include"game.h"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] = ' ';}}
}void DispalyBoard(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 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] = 'X';break;}else{printf("坐标被占用,请选择其他位置\n");}}else{printf("坐标非法,请重新输入\n");}}
}void ComputerMove(char board[ROW][COL], int row, int col)
{printf("电脑下棋:>\n");int x = 0;int y = 0;while (1){x = rand() % row;//0~2y = rand() % col;//0~2if (board[x][y] == ' '){board[x][y] = 'O';break;}}}//判断棋盘状态,返回值含义://'X'表示玩家获胜//'O'表示电脑获胜//'Q'表示平局//'C'表示游戏未结束//满了就返回1
//不满就返回0
int IsFull(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;
}char IsWin(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];}}//列int j = 0;for (j = 0;j< col;j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j]!= ' '){return board[1][j];}}//对角线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 (IsFull(board, row, col)){return 'Q';}//游戏继续return 'C';}
2.源文件(test.c
)部分
(menu
函数)
这个函数很简单,就是在控制台打印出游戏的菜单,显示了两个选项,分别是1.play
用于开始游戏和0.exit
用于退出游戏,使玩家能清晰地看到可操作的选择。
(game
函数)
game
函数实现了一局三子棋游戏的完整流程。首先定义了一个字符变量ret
用于记录游戏的输赢状态,以及一个二维字符数组board
来表示棋盘,接着调用InitBoard
函数初始化棋盘,并通过DispalyBoard
函数显示初始棋盘。然后进入一个while
循环,在循环中先让玩家下棋(调用PlayerMove
函数),之后调用IsWin
函数判断此时游戏是否有输赢结果,如果游戏未结束(返回值为'C'
),则显示当前棋盘状态(再次调用DispalyBoard
函数),接着让电脑下棋(调用ComputerMove
函数),同样再判断游戏输赢情况,如此反复,直到游戏有了输赢或者平局的结果(IsWin
函数返回值不为'C'
),最后根据ret
的值输出相应的游戏结果(玩家赢、电脑赢或者平局),并再次显示棋盘。
(main
函数)
main
函数是整个程序的入口点。首先通过srand((unsigned int)time(NULL));
初始化随机数生成器,确保电脑下棋的随机性。然后进入一个do-while
循环,在循环中先调用menu
函数显示游戏菜单,接着让用户输入选择,根据用户输入的不同选项(通过switch
语句判断),如果输入1
则调用game
函数开始一局游戏,如果输入0
则退出游戏,其他输入则提示选择错误,循环继续,直到用户选择退出游戏为止,最后返回 0 表示程序正常结束。
#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 };//初始化棋盘的函数InitBoard(board, ROW, COL);DispalyBoard(board, ROW, COL);//下棋while (1){PlayerMove(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}//显示棋盘DispalyBoard(board, ROW, COL);ComputerMove(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}//显示棋盘DispalyBoard(board, ROW, COL);}if (ret == 'X'){printf("玩家赢了\n");}else if (ret == 'O'){printf("电脑赢了\n");}else{printf("平局\n");}DispalyBoard(board, ROW, COL);} int main()
{srand((unsigned int)time(NULL));//设置随机数生成起点的int input = 0;do{menu();//打印菜单printf("请选择:>");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}