三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。
由于代码过多,堆在一个文件中会比较复杂,所以我们将分成
test.c --------游戏的整体框架
game.c -------过程实现
game.h -------函数的声明
三个文件来实现整个游戏的逻辑。
需要源代码的可以去我GitHub上面下载源代码
我们把主函数写在test.c文件中,并且在这个文件创建了一个游戏框架,我们知道,游戏至少会进行一次,所以用do while循环比较合适,利用switch判断来作为游戏继续或者退出的条件。
void game()
{
int ret = 0;
//数组储存是个二维数组
char board[ROW][COL];
//把棋盘初始化成空格
Initboard(board, ROW, COL);//在game.h声明,game.c中实现
//打印棋盘
Displayboard(board, ROW, COL);//在game.h声明,game.c中实现
while (1)
{
//以下所有函数都在在game.h声明,game.c中实现
//玩家下棋
PlayMove(board, ROW, COL);
//每下完一个棋子,打印一次棋盘
//Sleep(500);
Displayboard(board, ROW, COL);
//判断输赢
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋,随机下棋
CompuMove(board, ROW, COL);
//电脑下完一个棋子,打印一次棋盘
//Sleep(500);
Displayboard(board, ROW, COL);
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == '*')
{
system("cls");
printf("玩家获胜\n");
Displayboard(board, ROW, COL);
system("pause");
}
else if (ret == '#')
{
system("cls");
printf("电脑获胜\n");
Displayboard(board, ROW, COL);
system("pause");
}
else
{
system("cls");
printf("平局\n");
Displayboard(board, ROW, COL);
system("pause");
}
}
void menu()
{
printf("**************************\n");
printf("*** 1.play 0.exit ***\n");
printf("**************************\n");
}
int main()
{
srand((unsigned int)time(NULL));//使用系统时间作为随机值的种子
int input = 0;
char ch = 0;
do
{
system("cls");
menu();//菜单
printf("请选择:>");
scanf("%d", &input);
system("cls");
while (ch != '\n')
{
ch = getchar();
}
switch (input)
{
case 1:
printf("开始游戏\n");
game();//实现游戏过程的函数
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while(input);//输入的选择作为循环的条件
return 0;
}
我们看到test.c文件中的game函数是用来实现游戏过程的,调用了很多自定义函数,这些函数并没有声明,所以这时候就要我们引用创建的game.h头文件了,引用自己创建的头文件是用" "引用的。
#include"game.h"
#include
#include
#include
#include
#define ROW 3//行
#define COL 3//列
//声明函数
void Initboard(char arr[ROW][COL], int row, int col);//初始化棋盘
void Displayboard(char arr[ROW][COL], int row, int col);//打印棋盘
void PlayMove(char arr[ROW][COL],int row,int col);//玩家下棋过程
void CompuMove(char arr[ROW][COL],int row,int col);//电脑下棋过程
char Iswin(char arr[ROW][COL], int row, int col);//判断输赢
现在我们一一来实现这些函数的功能
首先我们在game.c文件种也要调用"game.h"头文件。
#include"game.h"
我们看到在test.c文件中游戏刚开始的时候需要打印一个空棋盘到屏幕上去,所以我们用两个函数来实现这两个功能
//初始化棋盘
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
游戏开始前我们需要将整个棋盘初始化成空格,方便后面打印出来更加美观。代码实现如下
//初始化棋盘
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] = ' ';
}
}
}
我们可以把这个图形分解成三个对象,横线有N行就打印N-1行,有N列就打印N列,而竖线就是有N行就打印N行,有N列就打印N-1列,字符是N行N列就打印N行N列个
代码实现
//打印棋盘
void Displayboard(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");
}
}
}
当空棋盘打印在屏幕面前时,接下来就开始进行下棋操作了,下棋操作分为两种,玩家下棋和电脑下棋,并且,每下完一步棋,就将棋盘给打印出来,我们也将其封装成两个函数来实现,下棋过程是个循环过程,所以我们用while循环来控制这下棋过程
while(1)
{
PlayMove(board, ROW, COL);//玩家下棋过程
Displayboard(board, ROW, COL);//每下完一个棋子,打印一次棋盘
CompuMove(board, ROW, COL);//电脑下棋
Displayboard(board, ROW, COL);//每下完一个棋子,打印一次棋盘
}
在玩家下棋的过程,玩家要输入坐标,我们知道数组对应的坐标应该是这样的
可有的玩家不一定接触过编程,站在他们的角度想,对应的坐标应该是这样的
所以在下棋时,我们要将玩家输入的坐标-1就是对应的我们数组真实的坐标,并且在此之前需要判断该坐标是否合法,并检测该坐标有没有被占用,那么这个过程也是个循环,在下子成功后退出循环。
//玩家下棋
void PlayMove(char board[ROW][COL], int row, int col)
{
char ch = 0;
int x = 0;
int y = 0;
while (1)
{
printf("请输入要下棋的坐标:>");
scanf("%d%d", &x, &y);
while (ch != '\n')
{
ch = getchar();
}
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标是否合法
{
if (board[x - 1][y - 1] == ' ')//判断真实坐标是否被下过棋子
{
board[x - 1][y - 1] = '*';
system("cls");
break;//下子成功立马跳出循环
}
else
{
printf("坐标被占用请重新输入!!!\n");
}
}
else
{
printf("坐标不合法,请重新输入!!!\n");
}
}
}
电脑下棋过程就比较简单了,电脑是随机下棋,只需要判断电脑随机数的坐标有没有被占用就行了,这里注意的是rand()这个(伪)随机值函数是需要srand()函数生成随机数起点的种子,我们这里用系统时间来当作随机数的种子,而种子只需要生成一次就行,所以我们将srand()函数放在了主函数那里
srand((unsigned int)time(NULL));//使用系统时间作为随机值的种子
//电脑下棋
void CompuMove(char board[ROW][COL], int row, int col)
{
system("cls");
printf("电脑走:>");
while (1)
{
int x = rand() % row;//产生0-row的随机坐标
int y = rand() % col;//产生0-row的随机坐标
if (board[x][y] == ' ' && board[x][y] != '*')
{
printf("%d %d\n", x + 1, y + 1);
board[x][y] = '#';
break;
}
}
}
游戏总会结束的,所以我们接下来用Iswin()函数来判断游戏状态,并在每次玩家或电脑下完棋后判断一次。
while (1)
{
//以下所有函数都在在game.h声明,game.c中实现
//玩家下棋
PlayMove(board, ROW, COL);
//每下完一个棋子,打印一次棋盘
//Sleep(500);
Displayboard(board, ROW, COL);
//判断输赢
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋,随机下棋
CompuMove(board, ROW, COL);
//电脑下完一个棋子,打印一次棋盘
//Sleep(500);
Displayboard(board, ROW, COL);
ret = Iswin(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
在这个函数里给我们返回4种状态
//判断平局
int IsFull(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++)
{
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][0];
}
//竖三列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
return board[0][i];
}
//对角线 左上->右下
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] != ' ')
return board[1][1];
//对角线 左下->右上
if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[0][2] != ' ')
return board[1][1];
//判断平局
if(IsFull(board,ROW,COL)==1)
return 'Q';
return 'C';
}
如果返回的不是‘C’,则跳出整个游戏的循环,在输出对应的结果
if (ret == '*')
{
system("cls");
printf("玩家获胜\n");
Displayboard(board, ROW, COL);
system("pause");
}
else if (ret == '#')
{
system("cls");
printf("电脑获胜\n");
Displayboard(board, ROW, COL);
system("pause");
}
else
{
system("cls");
printf("平局\n");
Displayboard(board, ROW, COL);
system("pause");
}