首先,我们因该分装3个文件,分别是:
以方便后期维护和增加代码可读性,防止代码冗余
game.c用来封装函数,实现各部分功能;test.c用来构建整个程序的框架;game.h用来存放所有头文件,我们只需把#include包含的头文件放在test.h文件,再在game.c 、test.c里加上#include"game.h"即可。
现在我们来构建框架
玩游戏的时候总要在开始界面有个菜单,我们可以这样做
void menu()
{
printf("*************************\n");
printf("******1.play 0.exit*****\n");
printf("*************************\n");
}
int main()
{
menu();
return 0;
}
考虑到玩家玩完之后还想继续玩,所以可以构建一个do while循环的结构;既然有了菜单可选项,我们可以给程序一个输入值1和0,用switch语句进行选择
void menu()
{
printf("*************************\n");
printf("******1.play 0.exit*****\n");
printf("*************************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入非法,请重新输入\n");
break;
}
}while(input);
return 0;
}
注意:每完成块步骤,记得执行以下,看看有没有bug,以免到最后程序写完了,出现一堆bug,一个一个找起来实在麻烦!
程序走了case 1就进入游戏啦,这里可以写一个game()函数来封装
这里我们继续一步一步走,既然玩的是三子棋或者五子棋,那肯定先要有个棋盘嘛,而且下棋怎么实现呢?这里可以创建一个二维数组来实现,这里初始化一下棋盘,封装一个函数,可以放在game.h里啦,在test.c文件里只需声明一下即可
void game()
{
char board[3][3] = { 0 };
//初始化棋盘
Initboard(board, 3, 3);
}
这里细心的人可能就发现了,“哎呀,如果我要将这个三子棋改成五子棋那岂不是重新创建一个矩阵,那程序内部不就乱套了啊”
哈哈,没事,不用着急,这里我们可以这样做
这样以后如果要把游戏改成五子棋也方便了不少
那这里函数自然也要变
void game()
{
char board[ROW][COL] = { 0 };
//初始化棋盘
Initboard(board, ROW, COL);
}
记得把这个函数声明放到.h文件里哦
//初始化棋盘
void Initboard(char board[ROW][COL],int row, int col);
接下来在game.c里实现一下 初始化棋盘:
void Initboard(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++)
{
board[i][j] = ' ';
}
}
}
实际上,就是将数组里的每个元素初始化成空格
光是空格也不行啊,该看看实际效果了,该打印展示一下棋盘了
先来看一下,棋盘大概样子
这里如果你直接用一个printf搞定也不是不可以,但又没有想过太挫了(狗头保命)
如果要修改成五子棋,那printf岂不是要重写
所以我们可以这样
void Showboard(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 < col - 1)
{
printf("|");
}
}
}
printf("\n");
}
}
这里大家仔细看一下,不难理解
接下来看一下目前程序执行的结果
如果想要更大的棋盘,直接修改#define定义的行和列就好啦
是不是很nice!
好咯
现在重头戏来了
该轮到玩家或者电脑下棋了
来分析一波~
这里可以给玩家的棋子设置为‘ * ’符号,电脑的棋子为‘ # ’符号
首先玩家下棋,再继续轮到电脑下棋,直到最后谁赢了或者是平局,先抛开这个不想,先解决手头的问题,这样对弈的过程实际上是一个循环,可以这样来写
void game()
{
char board[ROW][COL] = { 0 };
//初始化棋盘
Initboard(board, ROW, COL);
//展示棋盘
Showboard(board, ROW, COL);
//下棋
while (1)
{
//玩家下棋
Game_board(board, ROW, COL);
//电脑下棋
//Computer_board(board, ROW, COL);
}
}
因为暂时不考虑其他因素,可以先把循环写死
同时,玩家或电脑下完棋,我们最好能看到棋盘,所以
void game()
{
char board[ROW][COL] = { 0 };
//初始化棋盘
Initboard(board, ROW, COL);
//展示棋盘
Showboard(board, ROW, COL);
//下棋
while (1)
{
//玩家下棋
Game_board(board, ROW, COL);
Showboard(board, ROW, COL);
//电脑下棋
//Computer_board(board, ROW, COL);
//Showboard(board, ROW, COL);
}
}
接下来可以实现以下,玩家下棋了
这里可以通过让玩家输入坐标来完成下棋
void Game_board(char board[ROW][COL], int row, int col)
{
printf("玩家走(输入坐标):");
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
}
接着,我们因该思考这样一个问题,咱写程序的人知道数组下标是从0开始的,但不见得玩家也是程序员啊,所以我们在输入坐标以后可以给x-1 , y-1,这样OK~
不仅仅是这个问题,比如:如果玩家已经在(1,1)这个格子里下过了,玩家又不小心下在这个格子里怎么办?如果棋盘大小只有3*3,玩家走(4,8)下出格了怎么办...
这一些列问题我们都因该考虑到,这里我们可以用if else 来解决
这个用来解决下出格子的问题
这个用来解决重复问题
成品:
void Game_board(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("玩家走(输入坐标):");
scanf("%d %d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= col)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("该位置已被占用,请重新输入!\n");
}
}
else
{
printf("输入非法,请重新输入!\n");
}
}
}
接下来轮到电脑下棋咯
先分析一下,大致思路和玩家下棋一样,要避免一些重复,出格等问题,除此之外,还因该想想电脑该如何下棋呢?
可以这样做,创造一个随机数,让电脑随机下棋;
怎么创造随机数呢?
这里我们引入两个函数来产生随机数
rand用来产生随机数
srand用来产生随机数生成起点
还需要引入两个头文件
至于time.h这个头文件,是一会用srand函数会用到的
srand函数需要放在整个程序的开头用,这样rand产生的数才是随机的(具体用法可以去搜一下,这里主要讲三子棋和五子棋,这里不细讲);
srand((unsigned int)time(NULL));//设置随机数起点
unsigned int 强制类型转换成无符号整形,防止出现负数.
有个了这个随机数种子,就可以去编写电脑下棋的代码了
void Computer_board(char board[ROW][COL], int row, int col)
{
printf("电脑走:\n");
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == " ")
{
board[x][y] = '#';
break;
}
}
}
rand()可以产生随机数
rand()%3可以产生0~2的数字
再用 if 来排除重复问题.
提醒以下:别忘了电脑下完棋以后也要展示以下喔~
说到这里,不知道大家有没有发现上面这段代码有问题(哈哈哈)
上方代码会让程序死循环...
作者在这蹭了有一会时间哈哈哈哈
我就刚开始瞪着眼睛看,怎么找都找不出来!把我可急坏了哈哈哈哈
再后来我在while那里设置了断点,调试了以下,才发现原来 if 语句那里我用的是 " " 双引号,哈哈哈哈~
所以在这里提醒以下大家:
#出错了不要光瞪着眼睛看!去调试以下,打开你的监视窗口,看你想要知道的变量的值,调试真的很有用!!!哭晕~
看到这里觉得不错的小伙伴点个赞咯
正确代码如下
void Computer_board(char board[ROW][COL], int row, int col)
{
printf("电脑走:\n");
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
这样就可以和电脑下棋喽~
哈哈,不要高兴的太早,还要分出胜负呢,今天不是你死就是我亡!当然也有可能平局哈哈
接下来,回到我们的test.c文件
我们需要分几种情况:
不妨分装一个函数 用来判断,玩家下一次棋判断一次,电脑下一次棋判断一次,一直循环,直到分出胜负
接下来我们转化成代码实现:
char Judgment(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])
{
if (board[i][1] == '*')
return '*';
else if (board[i][1] == '#')
return '#';
}
//竖
if (board[0][i] == board[1][i] && board[1][i] == board[2][i])
{
if (board[1][i] == '*')
return '*';
else if (board[1][i] == '#')
return '#';
}
}//对角线
if ((board[0][0] == board[1][1] && board[1][1] == board[2][2])
|| (board[0][2] == board[1][1] && board[1][1] == board[2][0]))
{
if (board[1][1] == '*')
return '*';
else if (board[1][1] == '#')
return '#';
}
}
这是电脑和玩家谁赢的判断
接下来判断平局
char Judgment(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])
{
if (board[i][1] == '*')
return '*';
else if (board[i][1] == '#')
return '#';
}
//竖
if (board[0][i] == board[1][i] && board[1][i] == board[2][i])
{
if (board[1][i] == '*')
return '*';
else if (board[1][i] == '#')
return '#';
}
}//对角线
if ((board[0][0] == board[1][1] && board[1][1] == board[2][2])
|| (board[0][2] == board[1][1] && board[1][1] == board[2][0]))
{
if (board[1][1] == '*')
return '*';
else if (board[1][1] == '#')
return '#';
}
//平局和继续
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 'C';
}
}
return 'P';
}
回到test.c文件中,继续建立框架,对judgment函数的反馈作出反应
看一下上面这段代码,如果要判断的话,电脑和玩家下面都要进行相同的判断,代码就有些地方冗余了,不够优化
我们可以这样
void game()
{
char board[ROW][COL] = { 0 };
//初始化棋盘
Initboard(board, ROW, COL);
//展示棋盘
Showboard(board, ROW, COL);
//下棋
while (1)
{
//玩家下棋
Game_board(board, ROW, COL);
Showboard(board, ROW, COL);
if (Judgment(board, ROW, COL) != 'C')
{
break;
}
//电脑下棋
Computer_board(board, ROW, COL);
Showboard(board, ROW, COL);
if (Judgment(board, ROW, COL) != 'C')
{
break;
}
}
if (Judgment(board, ROW, COL) == '*')
{
printf("玩家赢\n");
}
else if (Judgment(board, ROW, COL) == '#')
{
printf("电脑赢\n");
}
else
{
printf("平局\n");
}
}
这样是不是看起来舒服多了呢~
三子棋其实到这里就完成啦!
///
如果想改成五子棋,其实也很简单
只需要改一下Judgment这个函数,再将#define定义的棋盘大小一改成5*5就okk(这里有点局限性,棋盘大小如果超过5*5,对角线将不好判断,可以这样解决:加几个for循环寻找任意对角线上相同元素,在进行判断即可);
那怎么改呢?
char Judgment(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][2] == board[i][3] && board[i][3] == board[i][4])
{
if (board[i][0] == '*')
return '*';
else if (board[i][0] == '#')
return '#';
}
//竖
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] == board[3][i] && board[3][i] == board[4][i])
{
if (board[0][i] == '*')
return '*';
else if (board[0][i] == '#')
return '#';
}
}//对角线
if ((board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] == board[3][3] && board[3][3] == board[4][4])
|| (board[0][4] == board[1][3] && board[1][3] == board[2][2] && board[2][2] == board[3][1] && board[3][1] == board[4][0]))
{
if (board[2][2] == '*')
return '*';
else if (board[2][2] == '#')
return '#';
}
//平局和继续
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 'C';
}
}
return 'P';
}
再将
#define ROW 5
#define COL 5
即可
以上就是三子棋以及五子棋的创作过程即讲解了