三子棋是一个民间的益智小游戏,游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子连成一条线的一方则视为胜利者。下面将说明如何利用C语言在我们的计算机上简单的实现三子棋。
1.逻辑框架设计
在vs上创建两个.c文件test.c和game.c,再创建一个头文件game.h。我们在test.c文件中写游戏的逻辑框架,在game.c文件中写游戏细节,在game.h文件中进行声明。
在test.c文件中建立主函数,并设计调用test()函数。
int main()
{test();return 0;
}
继续写test()函数,首先我们采用do while循环建立程序的框架,游戏的开头需要一个游戏菜单,所以设计一个menu()函数放在循环的开头。
void menu()
{printf("**********************************\n");printf("******** 1.PLAY ********\n");printf("******** 0.EXIT ********\n");printf("**********************************\n");
}
再设计printf()scanf()函数引导玩家,后面采用switch case 语句把玩家输入的数input分为三种情况:输入1进入游戏,输入0退出游戏,输入其他发生错误重新输入。该循环的条件语句为input,如果input为0则程序结束。效果如下:
void test()
{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);}
2.游戏内容设计
基本的框架建立好之后,我们要对玩家输入1的情况下的开始游戏进行设计,定义一个game()函数用来写游戏的细节。首先我们需要在game()中创建一个的数组char board[ROW][COL],分别表示9宫格的9个位置,ROW表示行数,COL表示列数,虽然我们设计的是三子棋,但是为了通用性这里用ROW COL 来代替3 3,再将ROW COL在game.h中定义为3 3,这样有利于对ROW COL进行修改。
void game()
{char board[ROW][COL];
}
然后我们对board数组进行初始化,因为不进行初始化它的值是随机的。在game.c文件中定义一个init_board()函数,将所有元素都初始化为" "。此处我们采用两个for循环简单的解决这个问题。再在test.c的game()中调用这个函数。
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] = ' ';
}
注意:调用game.c中的函数需要在game.h中进行声明。
#define ROW 3
#define COL 3void init_board(char board[ROW][COL], int row, int col);
下面就是将我们的棋盘打印出来啦,首先在game.c中定义一个is_print()函数,我们需要打印一个九宫格,设想通过三个for循环实现,其中需要注意两个细节,一个是在行棋盘的打印中需要打印3个" %c "和2个“|”,而不是3个"|",所以j<col-1。一个是在列棋盘的打印中需要打印2个“---”而不是3个,所以i<row-1。
void is_pirnt(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");}
}
最终棋盘打印的效果是这样的
棋盘设计好之后,就开始我们的下棋环节。
第一步,玩家下棋。在game.c中定义一个player_move()函数,在game.h中声明此函数,最后在game()函数中调用。我们需要玩家输入一个坐标把棋下在想下的地方,注意玩家输入的坐标与计算机中真正的坐标有所不同,我们需要对输入的这个坐标-1。搞清楚逻辑后开始设计,首先限制输入的这个坐标的范围,如果超出了范围就打印“坐标错误”,同时还要注意输入的这个坐标有没有被占用,如果被占用需要重新输入,所以我们使用嵌套的两个if语句和一个while循环来实现。
void player_move(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;printf("玩家下棋\n");printf("请输入坐标:>");scanf("%d %d", &i, &j);while (1){if (i >= 1 && i <= row && j >= 1 && j <= col)//限制范围{if (board[i - 1][j - 1] == ' ')//坐标-1{board[i - 1][j - 1] = '*';break;}elseprintf("已被占用,请重新输入\n");}elseprintf("坐标错误\n");}
玩家下棋之后需要在game()函数中调用一次打印棋盘is_print()函数,把棋盘打印出来。
第二步,电脑下棋。同样在game.c中定义computer_move()函数,在game.h中声明,在game()中调用。由于电脑没有自主意识,所以我们把电脑下棋的坐标设计为随机值,使用rand()函数对坐标进行初始化,想要实现rand()函数必须先实现一次srand()函数,此处用到时间戳的知识,由于srand((unsigned int)time(NULL))只需要执行一次,所以我们把它放入test()函数中,并且在game.h中包含time.h头文件。(NULL是空指针的意思)
坐标初始化之后,同样建立一个while循环,与玩家下棋部分类似,如果随机值产生的坐标没有被占用,就将棋子下入该坐标,跳出循环。如果被占用就一直循环到有空位置为止。
void computer_move(char board[ROW][COL], int row, int col)
{printf("电脑下棋\n");while (1){int i = rand() % ROW;// %ROW COL 的目的是把随机值限制在0~ROW-1和0~COL-1int j = rand() % COL;if (board[i][j] == ' '){board[i][j] = '#';break;}}
}
电脑下棋之后在game()函数中再调用一次is_print()函数。
第三步,判断胜利。在game.c中定义一个is_win()函数,在game.h中声明,在game()中定义一个整型来接受is_win()的返回值。三子棋的游戏规则是率先将自己的棋子连成一条线的那一方胜利,所有有几种胜利的可能性,一是行连成一条线,二是列连成一条线,三是两条对角线连成一条线,由此我们可以写出这四种情况
char is_win(char board[ROW][COL], int row, int col)
{int i = 0;int j = 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];}for (j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')return board[0][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];
}
除了胜利,还有可能平局,平局就是把棋盘下满了也没有胜利者,所以我们在game.c中定义一个is_full()函数,此函数不需要在game.h中声明,因为不需要把它写入test.c中。在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;
}
is_full()写好之后,将它加入到is_win()中
char is_win(char board[ROW][COL], int row, int col)
{int i = 0;int j = 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];}for (j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')return board[0][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 (is_full == 1)return 'Q';//用Q表示平局return 'C';//用C表示继续
}
此时我们再看test.c中的game()
void game()
{char ret = 0;char board[ROW][COL];init_board(board,ROW,COL);is_pirnt(board, ROW, COL);player_move(board, ROW, COL);is_pirnt(board, ROW, COL);computer_move(board, ROW, COL);is_pirnt(board, ROW, COL);ret = is_win(board, ROW, COL);
}
我们注意到这里玩家和电脑只下了一次棋,所以我们给它们套上一个循环,让它们一直下棋。
void game()
{char ret = 0;char board[ROW][COL];init_board(board,ROW,COL);is_pirnt(board, ROW, COL);while (1){player_move(board, ROW, COL);is_pirnt(board, ROW, COL);computer_move(board, ROW, COL);is_pirnt(board, ROW, COL);ret = is_win(board, ROW, COL);}
}
然后我们发现判断输赢不能总是在电脑下完棋之后,玩家下完棋之后也需要判断输赢,并且加上if语句用来判定循环是否继续。
void game()
{char ret = 0;char board[ROW][COL];init_board(board,ROW,COL);is_pirnt(board, ROW, COL);while (1){player_move(board, ROW, COL);is_pirnt(board, ROW, COL);ret=is_win(board, ROW, COL);if (ret != 'C')break;computer_move(board, ROW, COL);is_pirnt(board, ROW, COL);ret = is_win(board, ROW, COL);if (ret != 'C')break;}
}
最后当跳出循环的时候,对胜负情况进行打印。
void game()
{char ret = 0;char board[ROW][COL];init_board(board,ROW,COL);is_pirnt(board, ROW, COL);while (1){player_move(board, ROW, COL);is_pirnt(board, ROW, COL);ret=is_win(board, ROW, COL);if (ret != 'C')break;computer_move(board, ROW, COL);is_pirnt(board, ROW, COL);ret = is_win(board, ROW, COL);if (ret != 'C')break;}if (ret == '*')printf("玩家胜利\n");else if (ret == '#')printf("电脑胜利\n");else if (ret == 'Q')printf("平局\n");
}
这样,我们的三子棋代码就完成啦!
让我们看看效果吧。
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void game()
{char ret = 0;char board[ROW][COL];init_board(board,ROW,COL);is_pirnt(board, ROW, COL);while (1){player_move(board, ROW, COL);is_pirnt(board, ROW, COL);ret=is_win(board, ROW, COL);if (ret != 'C')break;computer_move(board, ROW, COL);is_pirnt(board, ROW, COL);ret = is_win(board, ROW, COL);if (ret != 'C')break;}if (ret == '*')printf("玩家胜利\n");else if (ret == '#')printf("电脑胜利\n");else if (ret == 'Q')printf("平局\n");
}
void menu()
{printf("**********************************\n");printf("******** 1.PLAY ********\n");printf("******** 0.EXIT ********\n");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;
}
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
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 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 is_pirnt(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");}
}
void player_move(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;printf("玩家下棋\n");printf("请输入坐标:>");scanf("%d %d", &i, &j);while (1){if (i >= 1 && i <= row && j >= 1 && j <= col){if (board[i - 1][j - 1] == ' '){board[i - 1][j - 1] = '*';break;}elseprintf("已被占用,请重新输入\n");}elseprintf("坐标错误\n");}
}
void computer_move(char board[ROW][COL], int row, int col)
{printf("电脑下棋\n");while (1){int i = rand() % ROW;int j = rand() % COL;if (board[i][j] == ' '){board[i][j] = '#';break;}}
}
char is_win(char board[ROW][COL], int row, int col)
{int i = 0;int j = 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];}for (j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')return board[0][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 (is_full == 1)return 'Q';return 'C';
}
game.h
#include<stdio.h>
#include<time.h>
#define ROW 3
#define COL 3
//声明初始化数组函数
void init_board(char board[ROW][COL], int row, int col);
//声明打印棋盘函数
void is_pirnt(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);