第一次写博客,写的不好的话还请见谅。这是之前课程设计用C语言写的五子棋小游戏,包含了一些五子棋常见的功能,比如:人人对战、简单的人机对战。游戏界面做得比较比较简陋,有好的想法可以自行修改,整体的代码还是比较简单且容易理解的。博主水平有限,如有写得不好的地方欢迎指正或共同讨论。
首先是定义一些变量,用于记录棋盘的各种情况,都有注释。
int status[N][N]={{0},{0}}; //记录棋盘情况,0无,1红棋/玩家,2为白棋/电脑
int flag=0; //判断输赢
int direct[2]; //光标的位置,direct[0]为x轴,direct[1]为y轴
int Value1[N][N]={{0},{0}}; //棋盘上各个位置的进攻权值
int Value2[N][N]={{0},{0}}; //棋盘上各个位置的防守权值
int regrex,regrey,regrex1,regrey1; //上一个棋子的位置,用于悔棋
int count=0; //计算下棋总数,用于判断平局
首先是判断输赢的函数,如何判断输赢,应该是棋牌类游戏的一个重点,就像我们自己下棋一样,每下一步都要判断输赢。电脑判断输赢的方式和人一样,简单来说就是判断各个方向上是否可以存在某个方向可以连成五枚棋子,如果存在,就代表有人获胜,下面是详代码和相关注释。
/*
判断输赢的函数
传进来的参数x、y表示刚下的这枚棋子的位置
temp表示当前棋子的信息,1:红棋/玩家,2:白棋/电脑
判断逻辑(此函数的作用):分别从四个方计算棋子总数,如果存在某个方向
的棋子总数大于等于5则表示该棋子获胜,同时返回获胜的棋子的信息。
*/
int judge_winner(int x,int y,int temp)
{
int i,j,n1,n2;
n1=n2=0;
for(i=x,j=y+4;j<=58;j+=4) //往右累加,计算右边棋子数
{
if(status[i][j]==temp)
n1++;
else
break;
}
for(i=x,j=y;j>=2;j-=4) //往左累加,计算左边棋子数
{
if(status[i][j]==temp)
n2++;
else
break;
}
if(n1+n2>=5) //左右两边的棋子数量相加,如果大于等于5则表示有人获胜,下面同理
return temp;
n1=n2=0;
for(i=x,j=y;i>=1;i-=2) //上
{
if(status[i][j]==temp)
n1++;
else
break;
}
for(i=x+2,j=y;i<=30;i+=2) //下
{
if(status[i][j]==temp)
n2++;
else
break;
}
if(n1+n2>=5)
return temp;
n1=n2=0;
for(i=x-2,j=y+4;i>=1&&j<=58;i-=2,j+=4) //右上
{
if(status[i][j]==temp)
n1++;
else
break;
}
for(i=x,j=y;i<=30&&j>=2;i+=2,j-=4) //左下
{
if(status[i][j]==temp)
n2++;
else
break;
}
if(n1+n2>=5)
return temp;
n1=n2=0;
for(i=x,j=y;i>=0&&j>=0;i-=2,j-=4) //左上
{
if(status[i][j]==temp)
n1++;
else
break;
}
for(i=x+2,j=y+4;i<=30&&j<=58;i+=2,j+=4) //右下
{
if(status[i][j]==temp)
n2++;
else
break;
}
if(n1+n2>=5)
return temp;
return 0;
}
下面是人机对战部分的函数,要实现五子棋的人机对战有很多种方法,博主采用的是权值法。
首先我们联想一下人类是如何下棋的,简单来说,就是要考虑这枚棋子要下在哪里最有价值,你考虑的越多,自然也就越准确。当然,博主这里考虑到的情况只是很少一部分,毕竟博主本人也是个棋渣。
这里,我把权值分为进攻权值和防守权值,其中进攻的权值要大于防守,然后根据下在棋盘的不同位置后的连子数赋予不同权值,其中,4连子的权值会大于3连子的权值,然后以次类推。
下面是计算进攻权值的代码:
void machine_attack() //电脑在期盘各个位置的进攻权值
{
int i1,j1;
int k1,k2,k;
for(int i=1;i<=30;i+=2)
{
for(int j=2;j<=58;j+=4)
{
if(status[i][j])
Value1[i][j]=0;
if(status[i][j]==0)
{
k1=k2=0;
for(i1=i,j1=j-4;j1>=2;j1-=4)//往左数寻找电脑棋子数
{
if(status[i1][j1]==2)
k1++;
else
break;
}
for(i1=i,j1=j+4;j1<=58;j1+=4)//往右数寻找电脑棋子数
{
if(status[i1][j1]==2)
k2++;
else
break;
}
k=k1>k2? k1:k2;
k1=k2=0;
for(i1=i-2,j1=j;i1>=1;i1-=2)//往上数寻找电脑棋子数
{
if(status[i1][j1]==2)
k1++;
else
break;
}
for(i1=i+2,j1=j;i1<=30;i1+=2)//往下数寻找电脑棋子数
{
if(status[i1][j1]==2)
k2++;
else
break;
}
k1=k1>k2? k1:k2;
k=k>k1? k:k1;
k1=k2=0;
for(i1=i-2,j1=j-4;i1>=0&&j1>=0;i1-=2,j1-=4)//往左上数寻找电脑棋子数
{
if(status[i1][j1]==2)
k1++;
else
break;
}
for(i1=i+2,j1=j+4;i1<=30&&j1<=58;i1+=2,j1+=4)//往右下数寻找电脑棋子数
{
if(status[i1][j1]==2 )
k2++;
else
break;
}
k1=k1>k2? k1:k2;
k=k>k1?k:k1;
k1=k2=0;
for(i1=i+2,j1=j-4;i1<=30&&j1>=2;i1+=2,j1-=4)//往左下数寻找电脑棋子数
{
if(status[i1][j1]==2)
k1++;
else
break;
}
for(i1=i-2,j1=j+4;i1>=1&&j1<=58;i1-=2,j1+=4)//往右上数寻找电脑棋子数
{
if(status[i1][j1]==2)
k2++;
else
break;
}
k1=k1>k2? k1:k2;
k=k>k1?k:k1;
switch(k) //根据连子数赋予权值
{
case 3:
Value1[i][j]=15;break;
case 4:
Value1[i][j]=25;break;
default:
Value1[i][j]=3+2*k;break;
}
}
}
}
}
下面是计算防守权值的代码:
void machine_defend() //电脑在棋盘各个位置的防守权值
{
int i1, j1;
int k1,k2,k;
for(int i=1;i<=30;i+=2)
{
for(int j=2;j<=58;j+=4)
{
if(status[i][j])
Value2[i][j]=0;
if(status[i][j]==0)
{
k1=k2=0;
for(i1=i,j1=j-4;j1>=2;j1-=4)//往左数寻找玩家棋子数
{
if(status[i1][j1]==1)
k1++;
else
break;
}
for(i1=i,j1=j+4;j1<=58;j1+=4)//往右数寻找玩家棋子数
{
if(status[i1][j1]==1)
k2++;
else
break;
}
k=k1>k2? k1:k2;
k1=k2=0;
for(i1=i-2,j1=j;i1>=1;i1-=2)//往上数寻找玩家棋子数
{
if(status[i1][j1]==1)
k1++;
else
break;
}
for(i1=i+2,j1=j;i1<=30;i1+=2)//往下数寻找玩家棋子数
{
if(status[i1][j1]==1)
k2++;
else
break;
}
k1=k1>k2? k1:k2;
k=k>k1?k:k1;
k1=k2=0;
for(i1=i-2,j1=j-4;i1>=1&&j1>=2;i1-=2,j1-=4)//往左上数寻找玩家棋子数
{
if(status[i1][j1]==1)
k1++;
else
break;
}
for(i1=i+2,j1=j+4;i1<=30&&j1<=58;i1+=2,j1+=4)//往右下数寻找玩家棋子数
{
if(status[i1][j1]==1)
k2++;
else
break;
}
k1=k1>k2? k1:k2;
k=k>k1?k:k1;
k1=k2=0;
for(i1=i+2,j1=j-4;i1<=30&&j1>=2;i1+=2,j1-=4)//往左下数寻找玩家棋子数
{
if(status[i1][j1]==1)
k1++;
else
break;
}
for(i1=i-2,j1=j+4;i1>=1&&j1<=58;i1-=2,j1+=4)//往右上数寻找玩家棋子数
{
if(status[i1][j1]==1)
k2++;
else
break;
}
k1=k1>k2? k1:k2;
k=k>k1?k:k1;
switch(k) //根据连子数赋予权值
{
case 3:
Value2[i][j]=10;break;
case 4:
Value2[i][j]=20;break;
default:
Value2[i][j]=2+k*2;
}
}
}
}
}
下面的函数的作用是:选出当前棋盘上权值最大位置,然后传给光标。
void find_position() //找到最有价值的位置
{
int k1=0, k2=0;
int i, j, max=0;
for( i=1;i<=30;i+=2)
for( j=2;j<=58;j+=4)
{
if(max<=Value1[i][j])
{
max=Value1[i][j];
k1=i;
k2=j;
}
}
for( i=1;i<=30;i+=2)
for( j=2;j<=58;j+=4)
{
if(max<=Value2[i][j])
{
max=Value2[i][j];
k1=i;
k2=j;
}
}
direct[1]=k1; //将找到的位置传给光标
direct[0]=k2;
}
上面这些就是实现人机对战的关键代码了,想要获得更强大AI,就需要更复杂的算法,如果大家感兴趣可以自己尝试。
附上完整代码,有感兴趣的可以下载(仅供参考,代码最好自己敲一遍):
链接:五子棋源码 提取码:90wk