三子棋是一个民间的益智小游戏,游戏分为双方对战,双方依次在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 3
void 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;
}
else
printf("已被占用,请重新输入\n");
}
else
printf("坐标错误\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-1
int 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;
}
else
printf("已被占用,请重新输入\n");
}
else
printf("坐标错误\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);