目录
- 数据结构
- 八个方向的权重计算
- 基本逻辑
- 代码实现
- 空位两边连子处理
为了实现人机对战,我们在软件包中新建一个AI类,AI类承担计算八个方向的棋子连子权重,并找到权重最大(最优)位置,返回棋子的功能。
数据结构
- 键值对存储连子和权重
- 初始化代码块,设置权重
`intarr[][]=newint[16][16];HashMap<String,Integer>map=newHashMap<>();{map.put("010",10);map.put("0110",100);map.put("01110",1000);map.put("011110",10000);map.put("020",10);map.put("0220",100);map.put("02220",1000);map.put("022220",10000);map.put("01",7);map.put("011",70);map.put("0111",700);map.put("01111",10000);map.put("02",7);map.put("022",70);map.put("0222",700);map.put("02222",10000);}八个方向的权重计算
基本逻辑
- 当该位置上有棋子–不计算
- 当空位旁边无棋子–不计算
- 当持续遇到相同颜色的棋子–记录
- 当空位向一个方向遇到不同颜色的棋子时–打破循环
- 当空位向一个方向遇到了空位时–记录并打破循环
- 数组越界检查
代码实现
以向左为例
publicvoidtoLeft(intchessArr[][],intr,intc){//如果第一位是0,退出循环if(c-1<0||chessArr[r][c-1]==0)return;intmyFlag=chessArr[r][c-1];StringBuildersb=newStringBuilder("0");//向左遍历,判断棋子数符合哪种情况,加上对应权重for(inti=c-1;i>=0;i--){if(chessArr[r][i]==myFlag){sb.append(myFlag);}else{if(chessArr[r][i]==0){sb.append(0);}break;}//存储进字符串,拿来取出哈希表中对应权重}intscore=map.get(sb.toString());arr[r][c]+=score;}由于八个方向的基本逻辑一样,只是方向不同,因此我们可以提炼出buildLine方法:
privateStringbuildLine(int[][]arr,intr,intc,intdr,intdc,intmyFlag){StringBuildersb=newStringBuilder("0");//从该位置的下一步长开始inti=r+dr;intj=c+dc;while(i>=0&&i<16&&j>=0&&j<16){if(arr[i][j]==myFlag){sb.append(myFlag);}else{//如果遇到0,记录后打破循坏if(arr[i][j]==0){sb.append("0");}break;//如果遇到其他颜色的棋子,直接打破循环}i+=dr;j+=dc;//下一步}returnsb.toString();}优化后的toLeft:
publicvoidtoLeft(int[][]chessArr,intr,intc){//如果第一位是0,退出循环if(c-1<0||chessArr[r][c-1]==0)return;intmyFlag=chessArr[r][c-1];StringlStr=buildLine(chessArr,r,c,0,-1,myFlag);intscore=map.get(lStr);arr[r][c]+=score;}其余几个方向:
publicStringtoRight(intchessArr[][],intr,intc){if(c+1>=16||chessArr[r][c+1]==0){returnnull;}intmyFlag=chessArr[r][c+1];StringrStr=buildLine(chessArr,r,c,0,1,myFlag);intscore=map.get(rStr);arr[r][c]+=score;returnrStr;}publicvoidtoUp(int[][]chessArr,intr,intc){if(r-1<0||chessArr[r-1][c]==0){return;}intmyFlag=chessArr[r-1][c];StringuStr=buildLine(chessArr,r,c,-1,0,myFlag);intscore=map.get(uStr);arr[r][c]+=score;}publicStringtoDown(int[][]chessArr,intr,intc){if(r+1>=16||chessArr[r+1][c]==0){returnnull;}intmyFlag=chessArr[r+1][c];StringdStr=buildLine(chessArr,r,c,1,0,myFlag);intscore=map.get(dStr);arr[r][c]+=score;returndStr;}publicvoidtoLeftUp(int[][]chessArr,intr,intc){if(r-1<0||c-1<0||chessArr[r-1][c-1]==0){return;}intmyFlag=chessArr[r-1][c-1];StringluStr=buildLine(chessArr,r,c,-1,-1,myFlag);intscore=map.get(luStr);arr[r][c]+=score;}publicStringtoLeftDown(int[][]chessArr,intr,intc){if(r+1>=16||c+1>=16||chessArr[r+1][c+1]==0){returnnull;}intmyFlag=chessArr[r+1][c+1];StringldStr=buildLine(chessArr,r,c,1,1,myFlag);intscore=map.get(ldStr);arr[r][c]+=score;returnldStr;}publicvoidtoRightUp(int[][]chessArr,intr,intc){if(r-1<0||c+1>=16||chessArr[r-1][c+1]==0){return;}intmyFlag=chessArr[r-1][c+1];StringruStr=buildLine(chessArr,r,c,-1,1,myFlag);intscore=map.get(ruStr);arr[r][c]+=score;}publicStringtoRightDown(int[][]chessArr,intr,intc){if(r+1>=16||c-1<0||chessArr[r+1][c-1]==0){returnnull;}intmyFlag=chessArr[r+1][c-1];StringrdStr=buildLine(chessArr,r,c,1,-1,myFlag);intscore=map.get(rdStr);arr[r][c]+=score;returnrdStr;}空位两边连子处理
现在计算机可以处理形如此表中的连子情况:
map.put("010",10);
map.put("0110",100); map.put("01110",1000); map.put("011110",10000); map.put("020", 10); map.put("0220", 100); map.put("02220", 1000); map.put("022220", 10000); map.put("01", 7); map.put("011", 70); map.put("0111", 700); map.put("01111", 10000); map.put("02", 7); map.put("022", 70); map.put("0222", 700); map.put("02222", 10000);但是若遇到“101110”或“01101”,计算机则会把它们当作简单的二连三连和一连相加,从而导致误判输局。
为此,我们采用在一个方向方法中调用一次反向,取出同一条线上的连子情况再做判断。
调整反向返回类型为String
StringrStr=toRight(chessArr,r,c);intrFlag=chessArr[r][c+1];//如果两个方向棋子颜色不一样,结束方法if(rFlag!=myFlag){return;}if(rStr!=null){//计算两边的有效棋子数intlc=countChess(lStr);intrc=countChess(rStr);//中间有空位的情况分别当作活二 活三 活四处理if(lc+rc==2)arr[r][c]+=100;elseif(lc+rc==3)arr[r][c]+=1000;elseif(lc+rc>=4)arr[r][c]+=10000;}计算除空位外的连子方法:
privateIntegercountChess(Stringstr){intcount=0;//如果不为0,增加计数for(charc:str.toCharArray()){//toCharArray()将字符串转为char类型数组,便于遍历if(c!='0')count++;}returncount;}