前言
本篇博客主要介绍如何使用C语言实现三子棋游戏。
一、游戏规则
玩家在一张9*9的棋盘上与电脑下棋,玩家使用“X”作为棋子,电脑使用“O”作为棋子。其中一方3个棋子连成一线则获胜;若棋盘铺满仍未有一方获胜,则视为和棋。宏定义如下:
#define ROW 3
#define COL 3
二、游戏流程
1.创建棋盘并初始化
使用字符型数组作为棋盘,初始化即将数组内所有元素设为空字符。代码如下:
void init(char chessBoard[ROW][COL]){
for (int i = 0; i < ROW; i++){
for (int j = 0; j < COL; j++){
chessBoard[i][j] = ' ';
}
}
}
2.打印棋盘
利用循环语句,逐行打印棋盘。使用“-”和“+”模拟棋盘格的样式。代码如下:
void printChessBoard(char chessBoard[ROW][COL]){
printf("+---+---+---+\n");
for (int i = 0; i < ROW; i++){
printf("| %c | %c | %c |\n", chessBoard[i][0], chessBoard[i][1], chessBoard[i][2]);
printf("+---+---+---+\n");
}
}
3.玩家落子
使用scanf语句读取玩家输入的坐标,若坐标越界或坐标上不为空,则提示重新输入;否则将字符“X”赋给数组的这个元素,完成一次落子。代码如下:
void playerMove(char chessBoard[ROW][COL]){
int row = 0;
int col = 0;
while (1){
printf("请输入落子坐标(row col)# ");
scanf("%d %d", &row, &col);
if ((row < 0 || row >= ROW) || (col < 0 || col >= COL)){
printf("输入有误!\n");
continue;
}
else if (chessBoard[row][col] != ' '){
printf("此坐标已经有子了!\n");
continue;
}
chessBoard[row][col] = 'X';
break;
}
}
4. 判定胜负关系
检查是否有某一行、某一列或者某一条对角线上存在三个相同棋子,若有,则函数返回这种棋子,用字符“X”或“O”表示。若没有,则检查棋盘是否满了(即数组中不再有空字符),若满了,则返回字符“D”,表示和棋。若没有满,则输出空字符,表示游戏继续。代码如下:
char checkState(char chessBoard[ROW][COL]){
for (int row = 0; row < ROW; row++) {
if (chessBoard[row][0] != ' '
&& chessBoard[row][0] == chessBoard[row][1]
&& chessBoard[row][0] == chessBoard[row][2]) {
return chessBoard[row][0];
}
}
for (int col = 0; col < COL; col++) {
if (chessBoard[0][col] != ' '
&& chessBoard[0][col] == chessBoard[1][col]
&& chessBoard[0][col] == chessBoard[2][col]) {
return chessBoard[0][col];
}
}
if (chessBoard[0][0] != ' '
&& chessBoard[0][0] == chessBoard[1][1]
&& chessBoard[0][0] == chessBoard[2][2]) {
return chessBoard[0][0];
}
if (chessBoard[0][2] != ' '
&& chessBoard[0][2] == chessBoard[1][1]
&& chessBoard[0][2] == chessBoard[2][0]) {
return chessBoard[0][2];
}
if (isFull(chessBoard)){
return 'D';
}
return ' ';
}
int isFull(char chessBoard[ROW][COL]){
for (int row = 0; row < ROW; row++) {
for (int col = 0; col < COL; col++) {
if (chessBoard[row][col] == ' ') {
return 0;
}
}
}
return 1;
}
5. 电脑落子(随机位置落子)
使用rand()函数产生随机数,通过对棋盘行数和列数取余,得到合法坐标值。若此坐标已经落子,则跳过,重新产生随机数;否则在此位置落子。代码如下:
void comMove(char chessBoard[ROW][COL]){
while (1){
int row = rand() % ROW;
int col = rand() % COL;
if (chessBoard[row][col] != ' '){
continue;
}
chessBoard[row][col] = 'O';
break;
}
}
6. 判定胜负关系
电脑落子后也要进行胜负关系的判断。此时若仍胜负未分,则重新从第一步开始循环。
三、game函数、menu函数和主函数
将上文的流程封装成game()函数,代码如下:
void game(){
char chessBoard[ROW][COL] = { 0 };
init(chessBoard);
char state = ' ';
printChessBoard(chessBoard);
while (1){
playerMove(chessBoard);
state = checkState(chessBoard);
if (state != ' '){
break;
}
comMove(chessBoard);
printChessBoard(chessBoard);
state = checkState(chessBoard);
if (state != ' '){
break;
}
}
printChessBoard(chessBoard);
if (state == 'X'){
printf("你赢了!\n");
}
if (state == 'O'){
printf("你输了!\n");
}
if (state == 'D'){
printf("和棋!\n");
}
}
实现一个menu函数,在开始游戏前显示一个菜单,让玩家选择是否玩游戏。代码如下:
int menu(){
printf("#######################\n");
printf("####### 1.play ########\n");
printf("####### 0.exit ########\n");
printf("#######################\n");
int option = -1;
scanf("%d", &option);
return option;
}
主函数中,加入随机数种子,保证每次游戏电脑落子的位置不同。由于time(0)的返回值是time_t,而srand()的参数应为无符号整形,所以此处使用强制类型转换, 把 time_t 转成 unsigned int。代码如下:
int main(){
srand((unsigned int)time(0));
while (1){
int option = 0;
option = menu();
if (option == 1){
game();
break;
}
else if (option == 0){
break;
}
else{
printf("输入有误,请重新输入!\n");
getchar();
continue;
}
}
system("pause");
return 0;
}
四、总结
实现三子棋游戏的过程中,主要涉及了分支、循环语句、函数和数组的知识。将大问题分解成小问题,编写多的函数实现不同的功能,最后按照逻辑顺序调用,实现整个游戏的运行。语法上确实难度不大,重点在于理清游戏的运行过程。