又到一年寒假集训时,看着大一萌新们开始正式写五子棋游戏了,去年我们也是写的五子棋。我的五子棋没有用到AlphaBeta剪枝,就一层估值,虽没能干掉大佬的AI,但也还获得了一个小奖品。那时自己确实实力不够,连AlphaBeta套板都不会,想在想想,要是做个哪怕3层的估值效果应该也会好一点吧,当然没有加算杀优化等实力也不会强到哪里去。我的这个五子棋说起来也惭愧,它的棋型估值部分也是网上找的,虽是一层,我竟然还输过。现将代码(C++)公开以供大家参考交流!
#include
#include
#include
#include
# define SPA 0
# define MAN 2
# define COM 1
using namespace std;
struct Chess_xy
{
int x,y;
};
int trend[4][2][2]={{{1,0},{-1,0}},{{0,1},{0,-1}},{{1,1},{-1,-1}},{{-1,1},{1,-1}}};
int map[16][16]={0};
int map_val[16][16]={0};
struct stepp//用来定义最大价值棋子位置的结构体
{
int x,y,val;
};
stepp stpp;
void ChessMap_1()
{
printf(" ");
for(int i=1; i<=15; i++)
if(i<=9) printf(" %d",i);
else printf("%d",i);
printf("\n");
for(int i=1; i<=15; i++)
{
if(i<=9) printf("%d ",i);
else printf("%d",i);
if(i==1)
{
for(int j=1; j<=15; j++)
{
if(j==1)printf("┏");
else if(j==15) printf("┓");
else printf("┯");
}
}
else if(i==15)
{
for(int j=1; j<=15; j++)
{
if(j==1)printf("┗");
else if(j==15) printf("┛");
else printf("┷");
}
}
else
{
for(int j=1; j<=15; j++)
{
if(j==1)printf("┠");
else if(j==15) printf("┨");
else printf("┼");
}
}
printf("\n");
}
return ;
}
void ChessMap_2()
{
printf("\n\n\n\n\n");
printf(" ");
for(int i=1; i<=15; i++)
if(i<=9) printf(" %d",i);
else printf("%d",i);
printf("\n");
for(int i=1; i<=15; i++)
{
if(i<=9) printf("%d ",i);
else printf("%d",i);
if(i==1)
{
for(int j=1; j<=15; j++)
{
if(map[i][j]==1) printf("○");
else if(map[i][j]==2) printf("●");
else if(j==1)printf("┏");
else if(j==15)printf("┓");
else printf("┯");
}
}
else if(i==15)
{
for(int j=1; j<=15; j++)
{
if(map[i][j]==1) printf("○");
else if(map[i][j]==2) printf("●");
else if(j==1)printf("┗");
else if(j==15)printf("┛");
else printf("┷");
}
}
else
{
for(int j=1; j<=15; j++)
{
if(map[i][j]==1) printf("○");
else if(map[i][j]==2) printf("●");
else if(j==1) printf("┠");
else if(j==15) printf("┨");
else printf("┼");
}
}
printf("\n");
}
return;
}
int judge(int i,int j,int groal)//map[i][j]当前落子位置
{
Chess_xy chess,ches;
int count[4]={1,1,1,1};
for(int k=0; k<4; k++)//横竖两斜四个大方向分别检索
{
chess.x=i+trend[k][0][0]; chess.y=j+trend[k][0][1];//第一个小方向
if( (map[chess.x][chess.y]==groal) && (chess.x>=1) && (chess.y<=15) && (chess.x<=15) && (chess.y>=1) )
{
count[k]++;
if(count[k]>=5) return groal;
ches.x=chess.x; ches.y=chess.y;
for(int kk=1; kk<=3; kk++)
{
ches.x+=trend[k][0][0]; ches.y+=trend[k][0][1];
if( (map[ches.x][ches.y]==groal) && (ches.x>=1) && (ches.y<=15) && (ches.x<=15) && (ches.y>=1) )
{
count[k]++;
if(count[k]>=5) return groal;
}
else break;
}
}
chess.x=i+trend[k][1][0]; chess.y=j+trend[k][1][1];//第二个小方向
if( (map[chess.x][chess.y]==groal) && (chess.x>=1) && (chess.y<=15) && (chess.x<=15) && (chess.y>=1) )
{
count[k]++;
if(count[k]>=5) return groal;
ches.x=chess.x; ches.y=chess.y;
for(int kk=1; kk<=3; kk++)
{
ches.x+=trend[k][1][0]; ches.y+=trend[k][1][1];
if( (map[ches.x][ches.y]==groal) && (ches.x>=1) && (ches.y<=15) && (ches.x<=15) && (ches.y>=1) )
{
count[k]++;
if(count[k]>=5) return groal;
}
else break;
}
}
}
return 0;
}
void yiwei(int n,int *i,int *j) /* 在n方向上对坐标 i j 移位 n为1-8方向 从右顺时针开始数 */
{
switch(n)
{
case 1: *i+=1; break;
case 2: *i+=1; *j+=1; break;
case 3: *j+=1; break;
case 4: *i-=1; *j+=1; break;
case 5: *i-=1; break;
case 6: *i-=1; *j-=1; break;
case 7: *j-=1; break;
case 8: *i+=1; *j-=1; break;
}
}
int qixing(int n,int p,int q) /* 返回空点p q在n方向上的棋型号 n为1-8方向 从右顺时针开始数 */
{
int k,m=0; /* 棋型号注解: 己活000-003 己冲010-013 对活100-103 对冲110-113 己空活020-023 己空冲030-033 对空活120-123 对空冲130-133 空-1 边界冲-2 边界空冲-3*/
yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
k=-2; /* 边界冲棋型 */
switch(map[q][p])
{
case COM:
{
m++; yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
{
k=m+9;
return k;
}
while(map[q][p]==COM)
{
m++;
yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
{
k=m+9;
return k;
}
}
if(map[q][p]==SPA)
k=m-1; /* 己方活棋型 */
else
k=m+9; /* 己方冲棋型 */
}break;
case MAN:
{
m++; yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
{
k=m+109;
return k;
}
while(map[q][p]==MAN)
{
m++;
yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
{
k=m+109;
return k;
}
}
if(map[q][p]==SPA)
k=m+99; /* 对方活棋型 */
else
k=m+109; /* 对方冲棋型 */
}break;
case SPA:
{
yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
{
k=-3;
return k;
} /* 边界空冲棋型 */
switch(map[q][p])
{
case COM:
{
m++; yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
{
k=m+29;
return k;
}
while(map[q][p]==COM)
{
m++;
yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
{
k=m+29;
return k;
}
}
if(map[q][p]==SPA) k=m+19; /* 己方空活棋型 */
else k=m+29; /* 己方空冲棋型 */
}break;
case MAN:
{
m++; yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
{
k=m+129;
return k;
}
while(map[q][p]==MAN)
{
m++;
yiwei(n,&p,&q);
if(p<1||p>15||q<1||q>15)
{
k=m+129;
return k;
}
}
if(map[q][p]==SPA)
k=m+119; /* 对方空活棋型 */
else
k=m+129; /* 对方空冲棋型 */
}break;
case SPA: k=-1; break; /* 空棋型 */
}
}break;
}
return k;
}
int value(int p,int q) /* 计算空点p q的价值 以k返回 */
{
int n=1,k=0,k1,k2,K1,K2,X1,Y1,Z1,X2,Y2,Z2,temp;
int a[2][4][4]={40,400,3000,10000,6,10,600,10000,20,120,200,0,6,10,500,0,30,300,2500,5000,2,8,300,8000,26,160,0,0,4,20,300,0};
/* 数组a中储存己方和对方共32种棋型的值 己方0对方1 活0冲1空活2空冲3 子数0-3(0表示1个子,3表示4个子) */
while(n!=5)
{
k1=qixing(n,p,q); n+=4; /* k1,k2为2个反方向的棋型编号 */
k2=qixing(n,p,q); n-=3;
if(k1>k2)
{
temp=k1;
k1=k2;
k2=temp;
} /* 使编号小的为k1,大的为k2 */
K1=k1; K2=k2;
/* K1 K2储存k1 k2的编号 */
Z1=k1%10; Z2=k2%10; k1/=10; k2/=10; Y1=k1%10; Y2=k2%10; k1/=10; k2/=10; X1=k1%10; X2=k2%10;
/* X Y Z分别表示 己方0对方1 活0冲1空活2空冲3 子数0-3(0表示1个子,3表示4个子) */
if(K1==-1) { if(K2<0) { k+=0; continue; } else k+=a[X2][Y2][Z2]+5; continue; }; /* 空棋型and其他 */
if(K1==-2) { if(K2<0) { k+=0; continue; } else k+=a[X2][Y2][Z2]/2; continue; }; /* 边界冲棋型and其他 */
if(K1==-3) { if(K2<0) { k+=0; continue; } else k+=a[X2][Y2][Z2]/3; continue; }; /* 边界空冲棋型and其他 */
if(((K1>-1 && K1<4) && ((K2>-1&&K2<4)|| (K2>9 && K2<14))) || ((K1>99 && K1<104) && ((K2>99 && K2<104) || (K2>109 && K2<114))))
{
/* 己活己活 己活己冲 对活对活 对活对冲 的棋型赋值*/
if(Z1+Z2>=2)
{
k+=a[X2][Y2][3];
continue;
}
else
{
k+=a[X2][Y2][Z1+Z2+1];
continue;
}
}
if(((K1>9 && K1<14) && (K2>9 && K2<14)) || ((K1>109 && K1<114) && (K2>109 && K2<114)))
{
/* 己冲己冲 对冲对冲 的棋型赋值*/
if(Z1+Z2>=2)
{
k+=10000;
continue;
}
else
{
k+=0;
continue;
}
}
if(((K1>-1 && K1<4) && ((K2>99 && K2<104) || (K2>109 && K2<114))) || ((K1>9 && K1<14) && ((K2>99 && K2<104) || (K2>109 && K2<114))))
{
/* 己活对活 己活对冲 己冲对活 己冲对冲 的棋型赋值*/
if(Z1==3||Z2==3)
{
k+=10000;
continue;
}
else
{
k+=a[X2][Y2][Z2]+a[X1][Y1][Z1]/4;
continue;
}
}
else
{
k+=a[X1][Y1][Z1]+a[X2][Y2][Z2];
continue;
} /* 其他棋型的赋值 */
}
return k;
}
void AI() /* 电脑下子 *p *q返回下子坐标 */
{
int i,j,k,max=0,I,J; /* I J为下点坐标 */
for(j=1;j<=15;j++)
for(i=1;i<=15;i++)
if(map[j][i]==SPA)
{ /* 历遍棋盘,遇到空点则计算价值,取最大价值点下子。 */
k=value(i,j);
if(k>=max) { I=i; J=j; max=k; }
}
stpp.y=I; stpp.x=J;
}
int main()
{
int ST,ans,color,xx,yy;
char YN;
system("title Gobang--Made by InspAlgo");
system("mode con cols=52 lines=23");
printf("***********Welcome To AI System of Gobang***********\n");
printf("输入:1 电脑先手\n");//1---black chess
printf("输入:2 电脑后手\n");//2---white chess
printf("输入:x y 表示落子点(!!!x为纵坐标,y为横坐标)\n");
memset(map,0,sizeof(map));
ChessMap_1();
printf("请选择先后手:");
scanf("%d",&ST);
printf("\n");
if(ST==1)
{
color=ST;
map[2][5]=1;
ChessMap_2();
bb:;
printf("请选择落子点:");
scanf("%d %d",&xx,&yy);
printf("\n");
map[xx][yy]=3-color;
ChessMap_2();
ans=judge(xx,yy,3-color);
if(ans==color)
{
printf("你输了,哈哈!AI要征服人类!吼吼~\n");
system("pause");
return 0;
}
if(ans==3-color)
{
printf("你赢了,好厉害!我弱,我战五渣!(ˇ^ˇ〉\n");
system("pause");
return 0;
}
AI();
map[stpp.x][stpp.y]=color;
ChessMap_2();
printf("AI落子:%d %d\n",stpp.x,stpp.y);
ans=judge(stpp.x,stpp.y,color);
if(ans==color)
{
printf("你输了,哈哈!AI要征服人类!吼吼~\n");
system("pause");
return 0;
}
if(ans==3-color)
{
printf("你赢了,好厉害!我弱,我战五渣!(ˇ^ˇ〉\n");
system("pause");
return 0;
}
goto bb;
}
else
{
color=ST;
aa:;
printf("请选择落子点:");
scanf("%d %d",&xx,&yy);
printf("\n");
map[xx][yy]=3-color;
ChessMap_2();
ans=judge(xx,yy,3-color);
if(ans==color)
{
printf("你输了,哈哈!AI要征服人类!吼吼~\n");
system("pause");
return 0;
}
if(ans==3-color)
{
printf("你赢了,好厉害!我弱,我战五渣!(ˇ^ˇ〉\n");
system("pause");
return 0;
}
AI();
map[stpp.x][stpp.y]=color;
ChessMap_2();
printf("AI落子:%d %d\n",stpp.x,stpp.y);
ans=judge(stpp.x,stpp.y,color);
if(ans==color)
{
printf("你输了,哈哈!AI要征服人类!吼吼~\n");
system("pause");
return 0;
}
if(ans==3-color)
{
printf("你赢了,好厉害!我弱,我战五渣!(ˇ^ˇ〉\n");
system("pause");
return 0;
}
goto aa;
}
}