学完函数,可以用函数来做一个五子棋小项目练练手。
项目代码放在了码云(gitee)上,有兴趣的可以下载看看。
https://gitee.com/YHF_200623/C_gobang
五子棋游戏
先简单介绍一下游戏,游戏玩法和传统的五子棋一样。通过输入坐标来选择下棋的位置,当有一方的棋子达到连续五颗及以上时,游戏结束,该玩家获胜。
黑棋为@,白棋为O ,空白处用 * 表示。
定义并初始化数据
需要的定义的数据有:
- 定义棋盘数组 15*15
- 定义角色变量
- 定义变量用于记录下棋位置
棋盘没什么好说的,定义一个15*15的数组,初始化为0就好了。角色变量我采用的是1和-1,这样设置在切换角色时会很方便。
最后在定义两个变量来接收键盘输入的坐标值。为了使用方便,将它们都定义为全局变量,这样可以省去参数传递的过程。
char Arr[15][15] = {};
char X = 0,Y = 0;
char Now = -1;
清屏并打印棋盘
下面写一下显示棋盘的函数,其实就是遍历迷宫数组,在打印时判断输出对应的字符,用两层for循环加switch语句即可。在这里我把在打印时判断输出对应的字符也作为一个单独的函数print()。linux系统可以使用函数system调用系统命令clear,实现清屏效果,需要调用头文件stdlib.h。windows系统需要使用windows.h头文件,里面的清屏命令改成cls。由于差别不大,就以linux系统的为例,windows系统的自己稍微修改一下。
char print(char n)
{
char ch = ' ';
switch(n)
{
case 0: ch = '*'; break;
case 1: ch = '@'; break;
case -1: ch = 'O'; break;
}
return ch;
}
void show(void)
{
system("clear");
printf(" ");
for(int i=0;i<15;i++)
{
printf(" %2d",i);
}
printf("\n");
for(int i=0;i<15;i++)
{
printf("%2d ",i);
for(int j=0;j<15;j++)
{
printf(" %c ",print(Arr[i][j]));
}
printf("\n");
}
}
判断是否五子连珠
判断是否五子连珠可以分类讨论,分成横、纵、左上到右下、右上到左下四种情况,每种情况又大同小异。我的思路是缩小范围,只考虑当前坐标的前四个棋子和后四个棋子,从前四个棋子遍历到后四个棋子,如果该棋子与下一个棋子都为当前角色的下的棋(黑棋或白棋),计数加一。一旦中间出现其他棋子或者空白,计数中断并重新计数。当计数到达5时,可以得出结论:该玩家获胜。若没有则判断其他情况。当其他情况也不满足时,就返回失败,下一个玩家继续落子。
注意:下面这段代码不能运行,因为全局变量没有大写。这段代码仅供参考,后面有优化的代码,可以直接运行。如果需要尝试,请自己修改一下。
int judge(void)
{
int cunt = 1;
for(int i=x-4;i<=x+4;i++)
{
if(i<0)
{
continue;
}
if(i>=14)
{
break;
}
if(arr[i][y] == now && arr[i+1][y] == now)
{
cunt++;
}
else
{
cunt = 1;
}
if(cunt == 5)
{
printf("%c棋获胜",print(now));
return now;
}
}
for(int i=y-4;i<=y+4;i++)
{
if(i<0)
{
continue;
}
if(i>=14)
{
break;
}
if(arr[x][i] == now && arr[x][i+1] == now)
{
cunt++;
}
else
{
cunt = 1;
}
if(cunt == 5)
{
printf("%c棋获胜",print(now));
return now;
}
}
for(int i=-4;i<=4;i++)
{
if(x+i<0)
{
continue;
}
if(x+i>=14)
{
break;
}
if(arr[x+i][y+i] == now && arr[x+i+1][y+i+1] == now)
{
cunt++;
}
else
{
cunt = 1;
}
if(cunt == 5)
{
printf("%c棋获胜",print(now));
return now;
}
}
for(int i=-4;i<=4;i++)
{
if(x+i<0)
{
continue;
}
if(x+i>=14)
{
break;
}
if(arr[x+i][y-i] == now && arr[x+i+1][y-(i+1)] == now)
{
cunt++;
}
else
{
cunt = 1;
}
if(cunt == 5)
{
printf("%c棋获胜",print(now));
return now;
}
}
return 0;
}
可以看到,这个判断函数很长,而且很多都是重复的代码。因此可以做优化,也可以采用偏移量的方法进行优化。
int judge(int a,int b)
{
int cunt = 1;
for(int i=-4;i<=4;i++)
{
if(X+i<0||Y+i<0)
{
continue;
}
if(X+i>=14||Y+i>=14)
{
break;
}
if(Arr[X+i*a][Y-i*b] == Now && Arr[X+(i+1)*a][Y-(i+1)*b] == Now)
{
cunt++;
}
else
{
cunt = 1;
}
if(cunt >= 5)
{
printf("%c棋获胜",print(Now));
return Now;
}
}
return 0;
}
这样就可以把四个方向的代码统一成一个函数,通过传入不同的参数,实现不同方向的判断。
落子
落子时,使用scanf函数从键盘中接收到值传入X,Y中。并且判断改坐标点是否越界,同时需要满足该点为空白点,可以落子。否则输出提示信息,并重新调用此方法,直到遇到合法输入,将该点设置为当前角色的棋子。
代码如下
void act(void)
{
printf("请输入%c棋子的坐标:",print(Now));
scanf("%hhd%hhd",&X,&Y);
if(!(X>=0 && X<=14 && Y>=0 && Y<=14 && Arr[X][Y] == 0))
{
printf("该坐标不能落子,请重新输入。\n");
act();
}
else
{
Arr[X][Y] = Now;
}
}
确定获胜条件
什么时候游戏算结束了呢,当在目标点处箱子数量等于4个,游戏就结束了。统计箱子数量可以放在显示地图的循环里,当箱子在目标点处(值为7),cnt++。因此可以写出游戏结束的条件:
//结束判断
if(4 == cnt)
{
printf("游戏结束\n");
return 0;
}
主函数调用
我们现在已经实现的功能有:
- 棋盘并显示棋盘
- 判断五子连珠
- 落子
游戏大部分的代码都在这里了,我们需要对它们进行组装使用即可。先捋一捋游戏流程:
- 清屏并显示地图
- 判断五子连珠
- 落子
- 回到第一步
根据游戏流程,可以看出需要使用死循环。在每次循环清屏并显示棋盘。然后判断五子连珠,如果获胜就退出,否则游戏继续,交换角色下棋。通过获取键盘输入的坐标,判断是否可以落子。成功落子后进入下一次循环。
int main(int argc,const char* argv[])
{
while(1)
{
show();
if(!(judge(0,1) || judge(1,0) ||judge(1,-1) ||judge(1,1)))
{
Now = - Now;
}
else
{
return 0;
}
act();
}
}
组合代码
将上述代码组合,即可获得下面代码(只能在运行linux中运行,windows需要修改,下面会提到)
#include<stdio.h>
#include<stdlib.h>
char Arr[15][15] = {};
char X = 0,Y = 0;
char Now = -1;
char print(char n);
void show(void);
void act(void);
int judge(int a,int b);
int main(int argc,const char* argv[])
{
while(1)
{
show();
if(!(judge(0,1) || judge(1,0) ||judge(1,-1) ||judge(1,1)))
{
Now = - Now;
}
else
{
return 0;
}
act();
}
}
char print(char n)
{
char ch = ' ';
switch(n)
{
case 0: ch = '*'; break;
case 1: ch = '@'; break;
case -1: ch = 'O'; break;
}
return ch;
}
void show(void)
{
system("clear");
printf(" ");
for(int i=0;i<15;i++)
{
printf(" %2d",i);
}
printf("\n");
for(int i=0;i<15;i++)
{
printf("%2d ",i);
for(int j=0;j<15;j++)
{
printf(" %c ",print(Arr[i][j]));
}
printf("\n");
}
}
void act(void)
{
printf("请输入%c棋子的坐标:",print(Now));
scanf("%hhd%hhd",&X,&Y);
if(!(X>=0 && X<=14 && Y>=0 && Y<=14 && Arr[X][Y] == 0))
{
printf("该坐标不能落子,请重新输入。\n");
act();
}
else
{
Arr[X][Y] = Now;
}
}
int judge(int a,int b)
{
int cunt = 1;
for(int i=-4;i<=4;i++)
{
if(X+i<0||Y+i<0)
{
continue;
}
if(X+i>=14||Y+i>=14)
{
break;
}
if(Arr[X+i*a][Y-i*b] == Now && Arr[X+(i+1)*a][Y-(i+1)*b] == Now)
{
cunt++;
}
else
{
cunt = 1;
}
if(cunt >= 5)
{
printf("%c棋获胜",print(Now));
return Now;
}
}
return 0;
}
windows 使用系统命令清屏,里面的清屏命令改成cls。
结语
希望这篇文章对你有所帮助