基于C++实现五子棋AI算法思想
今天我想要分享一下我做五子棋AI的思路。因为在做这个之前,我没有接触过任何像这种类似的东西。通过这一次,我也算是有所了解,我的思路也是来自很多网络上的博客,看了很多,最终总结出了自己的这样一个。
那我的五子棋是15*15的大小(一般也就是这样的一个大小)。我的AI算法要求每一次落子之后都要去计算每一个空暇的位置的“分值”,简单的说,我们需要一个存放棋子的数组,表示是否存放了棋子,还要一个计算每一个空格的数组来记录“分数”,这个分数是后期AI用来运算的基础,也是你AI难度控制的点。
我现有的思路就是分两部分。首先是如果是玩家先落子,那么要求电脑AI随即在你落子的地方的任意一个方向,随机落子,这是第一步。接下来以后就正式进入到算法中去。
首先初始化你的分数数组,让他们全部为零。然后在每一次落子之后进行全盘的遍历,如果发现该处为空白,于是检查其四周八个方向(当然如果是边缘位置就相对修改,判断是否出了边界)。若在空白处,且发现在某一对角线方向发现有一个其他颜色的棋子,那么相对的给这个空白区域的分数数组加上一定的分值,然后继续往这个方向检测是否还有连续的同一颜色的棋子,若没有则检查其他方向或者检测下一个空白位置。若是还在同一方向上面找到了相同颜色的棋子,那么第二个棋子的出现,你可以给改空白处加上双倍的分值,表明这个空白位置更加重要。一次类推,继续检测。(PS:因为最终AI棋子落在什么地方,依靠的是最后遍历整个分数数组,然后根据分数的高低来进行判断落子落在哪里的,在下面讲)。
经过上一遍的遍历,每一次落子都会使得分数数组得到一些变化,每一次都会导致AI判断的变化。在这个基础上,每一次落子还要进行一次对自己本身棋子颜色的一个遍历,判断自己的情况,同时加分加在分数数组之中,这样一来,电脑就会根据自己的棋子的情况以及玩家的落子情况进行判断,哪一个地方更加适合落子。
因为我是第一次做AI,网络上搜到的一些思想一般也是这种类似的遍历思想。理解了以后写代码就比较方便。最后可能会有一些点的分数是相同的,所以还有设置一下随机落子。把分数相同的地点随机落子。
个人感觉AI的强弱是根据你每一次给他增加分数的多少来确定的。这个我的AI有时候也会抽风,不过一般情况比较正常,可能运气也占了一部分,当初设计加分的时候其实没想那么多,现在却发现好像还不错。
大家要多去实践练习,多改改分数可能就会出来不错的AI了,o(^▽^)o。
下面贴上我的代码!
voidGameScene::Robot(int*x,int*y,int*Sum) { ExWhile1=true; if(*Sum==1) { while(ExWhile1) { ChessOne(*x,*y); if(ch[*x][*y]==2){ExWhile1=false;} } ch[*x][*y]=tp;//记录这个点 printpart(*x,*y,tp);//打印出电脑AI第一次落子 isTouch=true; tp++; tp=tp%2; } else//从第2步开始,使用评分系统 { Findscore(*x,*y); } } voidGameScene::Findscore(int&x,int&y)//查找评分最高的坐标 { srand((unsigned)time(NULL)); inti,j,x1,x2,y1,y2,lx; intMax=0; ChessScore();//调用评分函数 for(i=0;i<15;i++) { for(j=0;j<15;j++) { if(Score[i][j]>Max) { Max=Score[i][j];//获取所有点中,评分最高的 x1=i; y1=j; } } } x2=x1;y2=y1; for(i=0;i<15;i++)//可能的话,有评分相同的多个点 { for(j=0;j<15;j++) { if(Score[i][j]==Max&&i!=x2&&j!=y2)//在这么多个相同分数的点中,随机找一个 { lx=rand()%10; if(lx<5) { x2=i,y2=j; break; } } } } if(x2!=x1||y2!=y1)//棋盘上有2个最高分 { lx=rand()%10;//随机一个 if(lx>6) { x=x1,y=y1; } else { x=x2,y=y2; } } else//棋盘上只有一个最高分 { x=x1,y=y1; } Max=0;//清空最大值 ch[x][y]=tp;//记录这个点 printpart(x,y,tp);//打印出电脑AI落子 if(winerValue==2) { isTouch=true; } tp++; tp=tp%2; } inlinevoidGameScene::ChessOne(int&x,int&y)//玩家走第1步时的落子 { inti,j; srand((unsigned)time(NULL));//随机数随着时间的改变而改变 for(i=0;i<15;i++) { for(j=0;j<15;j++) { if(ch[i][j]==0)//如果找到了玩家的棋子,在它的8个方的任意一点落子 { intlx=rand()%7; if(lx==0) { x=i+1;y=j+1; if(ch[x][y]==2){break;} } elseif(lx==1) { x=i+1;y=j-1; if(ch[x][y]==2){break;} } elseif(lx==2) { x=i-1;y=j-1; if(ch[x][y]==2){break;} } elseif(lx==3) { x=i-1;y=j+1; if(ch[x][y]==2){break;} } elseif(lx==4) { x=i-1;y=j;//上 if(ch[x][y]==2){break;} } elseif(lx==5) { x=i;y=j-1;//左 if(ch[x][y]==2){break;} } elseif(lx==6) { x=i;y=j+1;//右 if(ch[x][y]==2){break;} } else { x=i+1;y=j;//下 if(ch[x][y]==2){break;} } } } } } voidGameScene::ChessScore() { intx,y,i,j,k;//循环变量 intnumber1=0,number2=0;//number用来统计玩家或电脑棋子连成个数 intempty=0;//empty用来统计空点个数 memset(Score,0,sizeof(Score));//把评分数组先清零 for(x=0;x<15;x++) { for(y=0;y<15;y++) { if(ch[x][y]==2)//如果这个点为空 { for(i=-1;i<=1;i++) { for(j=-1;j<=1;j++)//判断8个方向 { if(i!=0||j!=0)//若是都为0的话,那不就是原坐标嘛 { //对玩家落点评分 for(k=1;i<=4;k++)//循环4次 {//这点没越界且这点存在黑子(玩家) if(x+k*i>=0&&x+k*i<=14&& y+k*j>=0&&y+k*j<=14&& ch[x+k*i][y+k*j]==0) { number1++; } elseif(ch[x+k*i][y+k*j]==2)//这点是个空点,+1后退出 { empty++; break; } else//否则是墙或者对方的棋子了 { break; } } for(k=-1;k>=-4;k--)//向它的相反方向判断 {//这点没越界且这点存在黑子(玩家) if(x+k*i>=0&&x+k*i<=14&& y+k*j>=0&&y+k*j<=14&& ch[x+k*i][y+k*j]==0) { number1++; } elseif(ch[x+k*i][y+k*j]==2)//这点是个空点,+1后退出 { empty++; break; } else { break; } } if(number2==1)//2个棋子 { Score[x][y]+=1; } elseif(number1==2)//3个棋子 { if(empty==1) { Score[x][y]+=5;//有一个空点+5分死3 } elseif(empty==2) { Score[x][y]+=10;//有两个空点+10分活3 } } elseif(number1==3)//4个棋子 { if(empty==1) { Score[x][y]+=20;//有一个空点+20分死4 } elseif(empty==2) { Score[x][y]+=100;//有2个空点+100分活4 } } elseif(number1>=4) { Score[x][y]+=1000;//对方有5个棋子,分数要高点,先堵 } empty=0;//统计空点个数的变量清零 //对电脑落点评分 for(k=1;i<=4;k++)//循环4次 {//这点没越界且这点存在白子(电脑) if(x+k*i>=0&&x+k*i<=14&& y+k*j>=0&&y+k*j<=14&& ch[x+k*i][y+k*j]==1) { number2++; } elseif(ch[x+k*i][y+k*j]==2) { empty++;break;//空点 } else { break; } } for(k=-1;k>=-4;k--)//向它的相反方向判断 { if(x+k*i>=0&&x+k*i<=14&& y+k*j>=0&&y+k*j<=14&& ch[x+k*i][y+k*j]==1) { number2++; } elseif(ch[x+k*i][y+k*j]==2) { empty++;break; } else { break;//注释与上面玩家版相同 } } if(number2==0) { Score[x][y]+=1;//1个棋子 } elseif(number2==1) { Score[x][y]+=2;//2个棋子 } elseif(number2==2)//3个棋子 { if(empty==1) { Score[x][y]+=8;//死3 } elseif(empty==2) { Score[x][y]+=30;//活3 } } elseif(number2==3)//4个棋子 { if(empty==1) { Score[x][y]+=50;//死4 } elseif(empty==2) { Score[x][y]+=200;//活4 } } elseif(number2>=4) { Score[x][y]+=10000;//自己落在这点能形成5个,也就能胜利了,分数最高 } number1=0;//清零,以便下次重新统计 number2=0; empty=0; } } } } } } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。