引入
谈到三子棋,大家应该都不陌生,学生时代我们大多人都爱拿作文本有事没事就跟同桌下两把,只要任意一方三点连成一线,就可以胜利。今天我作为一个计算机方面的博主,将会用C语言实现这个简单的小游戏(人机对战,电脑非智能)。由于三子棋是3*3的棋盘,所以我们主要要用到二维数组的有关知识点,该项目也是主要围绕二维数组来展开的。
基本框架
要实现这个简单的游戏,也需要实现不同的函数与结构,也要将不同部分放在不同源文件里,我们今天将会用(test.c-测试游戏的代码部分)和(game.c-游戏有关函数的实现),还有(game.h-游戏有关函数声明),下面我们来看一下这个游戏的框架。
首先我们先来看一下主函数的基本框架:
int main()
{
int input = 0;//利用input来记录玩家的选择
srand((unsigned int)time(NULL));//随机数时间戳设定,后面会用到
do//设置循环使玩家在推出游戏前可多次选择再次游玩
{
menu();//打印一个基本的菜单,这里选择1.play(玩)或 0.exit(退出游戏)
printf("请选择:>\n");
scanf("%d", &input);
switch (input)//根据input进入不同选择
{
case 1:
game();//选1直接进入游戏
break;
case 0:
printf("退出游戏\n");//后面input也为0,在while(input)为假,结束程序
break;
default:
printf("选择错误,请重新选择:>\n");
break;
}
} while (input);
return 0;
}
下面我们来看一下game.h这个头文件,上面的思维导图也有讲,这里包含的是函数声明等内容,
game.c和test.c都会使用到,非常重要。
//所用库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3//ROW即行数,定义为3
#define COL 3//COL即列数
//所需要函数的函数声明
void menu();
void Initboard(char board[ROW][COL], int row, int col);
void Displayboard(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);
char Iswin(char board[ROW][COL], int row, int col);
然后我们来看一下这个游戏运行的大致逻辑,由test.c中的game()函数展示。
void game()
{
char board[ROW][COL] = { 0 };//定义数组board,作为三子棋的主要框架
Initboard(board,ROW,COL);//初始化二维数组(棋盘)
Displayboard(board, ROW, COL);//打印棋盘
char ret = 0;//这里定义字符类型ret,与后面判断输赢平有关
while (1)//若未分胜负或者棋盘满了,则一直下,满足条件时跳出
{
/*首先玩家下棋,然后展示一下棋盘,
然后利用Iswin函数判断输赢
(Iswin会返回一个字符,用ret接受,在后面进行判断)*/
Playermove(board, ROW, COL);//玩家下棋函数
Displayboard(board, ROW, COL);
ret = Iswin(board, ROW, COL);
/*电脑下棋,方法同上*/
Computermove(board, ROW, COL);//电脑下棋函数
Displayboard(board, ROW, COL);
ret = Iswin(board, ROW, COL);
//---为了更好地理解,这里先大致讲一下Iswin函数的大致逻辑。
//判断输赢,在玩家后者电脑每一次落子之后进行判断
//玩家赢 则返回'*'
//电脑赢 则返回'#'
//继续 则返回'C'
//平局 则返回'Q'
if (ret != 'C')
break;//如果未返回'C',跳出对战,对最终结果进行判断
}
//最终结果的判断
if (ret == '*')
{
printf("玩家赢\n");
}
else if (ret == '#')
{
printf("电脑赢\n");
}
else if (ret == 'Q')
{
printf("平局\n");
}
}
根据前面的内容,相信你一定了解了三子棋项目的大致逻辑,下面,我将对game()中所使用的所有函数,进行逐一讲解。
具体函数讲解
Initboard(初始化棋盘)函数:分析:棋盘是用来落子的,在程序中就是用来存放数据的,我们能自然而然地想到用二维数组来存放数据.
//参数说明:二维数组board,行数row,列数col。
void Initboard(char board[ROW][COL], int row, int col)
{
//利用两个for循环对二维数组进行遍历,二维数组存放的数据都初始化为空格
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
//这样就完成了棋盘的初始化
Display(打印棋盘)函数:分析:这个函数是这个项目的一个难点,磨刀不误砍柴工,首先让我们来看一下这个棋盘长什么样:
可以看出,该棋盘是由“ %c | %c | %c ”与“---|---|---”交替连接而成的。我们可以将“ %c | %c | %c ”与“---|---|---”作为一个分割行的部分,当到达最后一行时,再少打印一个“---|---|---”,然后,我们再将“ %c |”和“---|”作为每一个分割列的部分,当到达最后一列时,两个部分都不打印“|”。
下面我们来看一下代码实现:
void Displayboard(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");
for (j = 0; j < COL; j++)
{
if (i < ROW - 1)
{
printf("---");
if (j < ROW - 1)
printf("|");
}
if (j == COL - 1)
printf("\n");
}
}
}
Playermove(玩家落子)函数:分析:这里主要记录玩家的落子,需要等待玩家输入要下的坐标。
这里主要要注意两点:1.玩家不是程序员,他输入的坐标肯定不是数组形式的(数组首元素从1开始),因此要注意输入坐标与数组元素的关系。
2.注意坐标的合法性:应该需要有判断,判断坐标是否合法(输入坐标超出数组的实际范围,输入的坐标那里有其他内容),若不合法,则重新输入,所以外层应该还需要嵌套循环。
void Playermove(char board[ROW][COL], int row, int col)
{
printf("玩家下棋\n");
printf("请输入下的坐标:>");
int i = 0, j = 0;
while (1)
{
scanf("%d%d", &i, &j);
//输入坐标-1即为数组中对应坐标
if ((board[i - 1][j - 1] != ' ')||(i - 1>ROW||j - 1>COL))
{
printf("输入坐标非法,请重新输入!");
}
else
{
board[i - 1][j - 1] = '*';//坐标合法,对应坐标落子'*'
break;
}
}
}
Computermove(电脑落子)函数:分析:这里主要记录电脑的落子,需要电脑随机确定下的坐标。
注意:这里的电脑落子坐标需要随机,即需要rand()函数(在主函数中已经定义随机数时间戳,确保了完全随机),而且随机数可以指定范围,因此在落子合法性上不需要判断越界问题。
下面再来看一下代码实现。
void Computermove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋\n");
int i = 0;
int j = 0;
while (1)
{
i = rand() % ROW;//范围为(0-2)
j = rand() % COL;//范围为(0-2)
if (board[i][j] == ' ')
{
board[i][j] = '#';//输入坐标合法,落子为'#'
break;
}
}
}
Iswin(判断输赢平)函数:分析:这里就运用到了三子棋的规则(三行其一或三列其一或主次对角线连成一线即可)。定义类型为char,上文也说过,该函数返回的字符有决策功能。
下面来看一下代码实现:
//Isfull是一个判断棋盘是否满了的辅助函数
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;//未满返回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[1][0] != ' ')
{
return board[i][0];//可以是'*''#'任意一个
}
}
for (i = 0; i < col; i++)
{
//判断三条列是否连成一线
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
//判断主对角线是否连成一线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')
return board[0][0];
//判断此对角线是否连成一线
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
return board[0][2];
//利用Isfull判断棋盘是否满了,满了则平局
if (Isfull(board, row, col) == 1)
{
return 'Q';
}
return 'C';
}
以上就是所有函数,下面我们来运行一下试试:
这里作者由于
太菜
输了。
好了,三子棋就分享到这,拜拜。