先将整个游戏源码放出,后面有对应说明,输出结果在最后
一、项目
1.game.h 关于游戏相关的函数声明、符号声明、头文件包含
#include <stdio.h>//头文件的包含
#include<stdlib.h>//rand函数头文件的包含
#include<time.h>
#define ROW 3
#define COL 3
//初始化棋盘函数的声明
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);
//玩家胜利 return *
//电脑胜利 return #
//平局 return Q
//游戏照常进行 return C
2.test.c 测试游戏的逻辑
#include"game.h"
void menu()
{
printf("*******************************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("*******************************\n");
}
void game()
{
char board[ROW][COL];//定义数组
initboard(board, ROW, COL);//调用初始化数组这个函数
displayboard(board, ROW, COL);//打印棋盘(调用打印棋盘的函数)-本质是打印数组
//到此为止对于棋盘的打印就结束了,其次进行玩家和电脑下棋
char ret = 0;//接受游戏状态
while (1)
{
playermove(board, ROW, COL);//调用玩家下棋的函数
displayboard(board, ROW, COL);//显示玩家下完棋后棋盘棋子情况
ret = iswin(board, ROW, COL);//判断胜负函数返回值赋值给变量ret
if (ret != 'C')
break;
computermove(board, ROW, COL);//调用电脑下棋的函数
displayboard(board, ROW, COL);//调用电脑下完棋后棋盘
ret = iswin(board, ROW, COL);//判断胜负函数返回值赋值给变量ret
if (ret != 'C')
break;
}
if (ret == '*')
{
printf("玩家胜利\n");
}
else if (ret == '#')
{
printf("电脑胜利\n");
}
else
{
printf("平局\n");
}
displayboard(board, ROW, COL);//调用游戏结束后棋盘
}
int main()
{
srand((unsigned int)time(NULL));//rand函数在生成坐标的时候要调用srand,为方便将其放入主函数中,利用time函数的返回值作为随机数的生成器
int input = 0;
do
{
menu();
printf("请选择->");
scanf_s("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
3.game.c 游戏相关函数的实现
#include"game.h"
//初始化棋盘的函数定义
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] = ' ';
}
}
}
//棋盘的函数定义
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");
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
//玩家移动的函数定义
void playermove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家回合->\n");
while (1)
{
printf("请输入落棋位置\n");
scanf_s("%d %d", &x, &y);
//判断落点是否合法
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//输入进来之后,判断是否重复在同一落点下棋
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
printf("落点重复,请重新输入\n");
}
else
printf("非法输入,请重新输入\n");
}
}
//电脑移动的函数定义
void computermove(char board[ROW][COL], int row, int col)
{
printf("电脑落子->\n");
//对于电脑来讲,它的行为可以定义为随机输入,因此我们利用函数来生成随机数
//电脑输入的列值在0到2之间,列值在0到2之间,我们要进行限制
//将随机数对3求余,得到的数一定在3以内
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//判断棋盘是否满的函数
int isfull(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[row][col] == ' ');
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][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[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];
}
//判断平局,此时调用一个函数
int t = isfull(board, row, col);
{
if (t == 1)
{
return 'Q';
}
//继续
return 'C';
}
}
二、具体步骤
首先在test.c中进行编译,运用do while语句,先将游戏菜单及初始选项输出后,再输入数字进行判断游戏是否开始 相关逻辑: while的括号内放入input即为输入的数字,利用switch语句,若输入为0,输出“退出游戏”并且跳出循环,若输入1,开始游戏,若为其他数字,则输出“错误”后再次进入循环进行输入
int main()
{
int input = 0;
do
{
menu();
printf("请选择->");
scanf_s("%d", &input);
switch (input)
{
case 1:
//开始游戏
game()
break;
case 0 :
printf("退出游戏\n");
break;
default:
printf("错误,请重新输入");
break;
}
} while (input);
return 0;
游戏菜单:函数menu实现 返回类型为空类型
void menu()
{
printf("*******************************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("*******************************\n");
}
游戏逻辑:game函数实现 返回类型为空类型 要明确三子棋中,玩家下的棋子和电脑下的棋子都相当于一个字符将其储存起来,此时我们可以定义一个字符的二维数组就可以实现这一目的
void game()
{
char board[ROW][COL];
}
要注意,这个时候行和列不能单纯为某一数字,我们将行写为ROW列写为COL,在game.h头文件中进行定义,最后将头文件包含即可
【game.h】
#define ROW 3
#define COL 3
此时再将数组进行初始化,因为最开始时我们并不希望数组里面什么也没有,也不希望出现一些奇怪的东西,因此我们将数组里面全部先写为空格,最后输入字符时候将其覆盖即可,我们将这个功能也用函数来表示,函数放在头文件里定义,在源文件里面进行相应的函数实现,函数声明的意思是告诉编译器我要使用这个函数,你现在没有找到它的定义不要紧,请不要报错,稍后我会把定义补上。
【test.c】
void game()
{
char board[ROW][COL];
initboard(board, ROW, COL);
}
【game.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");
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
到此为止,对于棋盘的定义就结束了,接下来我们进行玩家和电脑下棋的逻辑(此时电脑仅为随机下棋,深层次算法此时先不涉及)
首先声明函数playermove
void playermove(char board[ROW][COL], int row, int col);
接着在game.c中编译函数的功能
void playermove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家回合->\n");
while (1)
{
printf("请输入落棋位置\n");
scanf_s("%d %d", &x, &y);
//判断落点是否合法
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//输入进来之后,判断是否重复在同一落点下棋
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
printf("落点重复,请重新输入\n");
}
else
printf("非法输入,请重新输入\n");
}
}
此时玩家的回合结束 在test.c中,要对玩家移动函数进行调用,调用结束后再将棋盘进行打印
playermove(board, ROW, COL);//调用玩家下棋的函数
displayboard(board, ROW, COL);//显示玩家下完棋后棋盘棋子情况
之后电脑进行落子,声明函数computermove
void computermove (char board[ROW][COL], int row, int col);
接着在game.c中编译函数的功能 对于电脑来讲其下棋相对容易,只需给定随机值进行限制便可随机落子,深层次的算法逻辑此时先不探讨 (对于随机数的取法可移步看本人另一篇博客《如何生成随机数》,有对应说明)
void computermove(char board[ROW][COL], int row, int col)
{
printf("电脑落子->");
//对于电脑来讲,它的行为可以定义为随机输入,因此我们利用函数来生成随机数
//电脑输入的列值在0到2之间,列值在0到2之间,我们要进行限制
//将随机数对3求余,得到的数一定在3以内
while (1)
{
int x = rand() % row;// rand函数在生成坐标的时候要调用srand,为方便将其放入主函数中,利用time函数的返回值作为随机数的生成器
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
玩家和电脑下棋的逻辑都已完成,我们此时应该在每次玩家和电脑下棋之后去判断玩家或电脑的胜负,此函数应该在玩家下棋和电脑下棋之后分别调用
playermove(board, ROW, COL);//调用玩家下棋的函数
displayboard(board, ROW, COL);//显示玩家下完棋后棋盘棋子情况
iswin(board, ROW, COL);//调用判断胜负函数
computermove(board, ROW, COL);//调用电脑下棋的函数
displayboard(board, ROW, COL);//调用电脑下完棋后棋盘
iswin(board, ROW, COL);//调用判断胜负函数
函数声明
char iswin(char board[ROW][COL], int row, int col);
//因为要返回字符 因此定义为char型
//玩家胜利 return *
//电脑胜利 return #
//平局 return Q
//游戏照常进行 return C
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][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[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];
}
//判断平局,此时调用一个函数
int t = isfull(board, row, col);
{
if (t == 1)
{
return 'Q';
}
//继续
return 'C';
}
}
可以看到调用了isfull这个函数,但这个函数还没定义 故有
//判断棋盘是否满的函数
int isfull(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[row][col] == ' ');
return 0;
}
}
return 1;
}
将此函数定义之后,整个简易三子棋的逻辑就完成了,接下来我们看看输出结果
本人使用vs2019,读者可自行复制代码在跑一跑