TicTacToe(井字棋)的演算法——不比人的智商低的AI

iteye_4389發表於2013-03-28

何為TicTacToe?

TicTacToe也俗稱井字棋或三連棋(兩人輪流在一有九格方盤上劃加字或圓圈, 誰先把三個同一記號排成橫線、直線、斜線, 即是勝者),男女老少皆宜的入門級棋類遊戲。

關於遊戲的實現方式,比較簡單,在這就不多說了。直接談一談如何寫出在TicTacToe遊戲中不輸給人類棋手的電腦。

想要寫出遊戲AI,第一步是自己要會玩遊戲,有自己的策略。第二部才是告訴我們的AI如何實現這樣的策略。我們在玩的時候發現能儘可能佔據有利的位置會幫助我們快速贏下比賽,玩遊戲還能發現的一點就是一定要防住對面即將連成一條線的一路,還有很多,很多。這些特徵可以說明,遊戲中不同情況下,盤面的每一個位置似乎有著不同的重要性,我們完全可以利用這一點來指導我們的AI去馳騁沙場。(我一開始打算仔細判斷每一種情況,比如雙二單二什麼的,結果不但程式碼量大而且效果和這樣的思路沒什麼區別)

也就是說,給與AI一個非常重要的變數(一個3*3的陣列),表示每一個位置的權值,也就是該位置的重要性的體現。比賽中第一重要的當然是自己連成一條線,這可以賦予最高的權值。而且,這個權值應該大到什麼地步呢?當然應該大到無論其他什麼情況發生都以此為第一要素的地步。所以我們可以在AI檢查出自己能成一條線的位置時,給這個位置賦值10000,代表這個位置至高無上的地位。

第二重要的則是防止對手成一條線,此舉的重要性,可謂一人之下萬人之上,就在有這種情況的時候賦予這個位置1000的值吧。

其他需要賦值的三種情況是:1.某條線僅僅有己方棋子一枚;2.某條線僅僅有敵方棋子一枚;3.某條線上空空如也。如果是我們的AI先手,我認為第一條的權重大於第二條大於第三條。而AI後手則2>3>1。

因為玩過Tictactoe的朋友一定知道,這是一個先手贏不了,後手輸不了的遊戲。但是作為後手一旦失誤就會輸棋,先手若能抓住後手的失誤則能贏棋。數年前中國五子棋第一人那威曾經說過“先手要攻,後手要守”。雖然是說五子棋,不過Tictactoe實際上就是三字棋,與五子棋的區別僅僅是少了很多很多的變化而已。所以我們需要給我們的AI貫徹這樣的思想:先手要積極進攻,後手要小心放手。好了,AI的思路基本上就是這樣了,下面貼上核心部分的程式碼(java)

注:遊戲的地圖儲存在一個叫net的陣列裡,每一個位置值為0表示沒有棋,1表示為X棋,2表示為O棋

int[][] net=new int[4][4];//0 means null;1 means X;2 means O;
int level[][]=new int[4][4];//越大優先順序越高
IOHelper h=new IOHelper();
int o2=10000;//一個己方活二權值
int x2=1000;//一個對方活二權值
int x=10;//一個對方活一權值
int o=6;//一個己方活一權值
int nothing=4;//一個空行權值

for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(net[i][j]!=0)level[i][j]=0;
else{
//橫豎兩條,每人都有
//自己活二,,220
if(((net[0][j]+net[1][j]+net[2][j])==4)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==-1)
level[i][j]=level[i][j]+o2;
if(((net[i][0]+net[i][1]+net[i][2])==4)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==-1)
level[i][j]=level[i][j]+o2;
//對方活二,110
if(((net[0][j]+net[1][j]+net[2][j])==2)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==0)
level[i][j]=level[i][j]+x2;
if(((net[i][0]+net[i][1]+net[i][2])==2)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==0)
level[i][j]=level[i][j]+x2;
//單個X,100
if(((net[0][j]+net[1][j]+net[2][j])==1)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==0)
level[i][j]=level[i][j]+x;
if(((net[i][0]+net[i][1]+net[i][2])==1)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==0)
level[i][j]=level[i][j]+x;
//單個O,200
if(((net[0][j]+net[1][j]+net[2][j])==2)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==1)
level[i][j]=level[i][j]+o;
if(((net[i][0]+net[i][1]+net[i][2])==2)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==1)
level[i][j]=level[i][j]+o;
//空行,000
if(((net[0][j]+net[1][j]+net[2][j])==0)&&(net[0][j]*net[1][j]*net[2][j])==0&&((net[0][j]-1)*(net[1][j]-1)*(net[2][j]-1))==-1)
level[i][j]=level[i][j]+nothing;
if(((net[i][0]+net[i][1]+net[i][2])==0)&&(net[i][0]*net[i][1]*net[i][2])==0&&((net[i][0]-1)*(net[i][1]-1)*(net[i][2]-1))==-1)
level[i][j]=level[i][j]+nothing;

//分情況
//主對角線
if((i==0&&j==0)||(i==2&&j==2)||(i==1&&j==1)){
//己方活二
if(((net[0][0]+net[1][1]+net[2][2])==4)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==-1)
level[i][j]=level[i][j]+o2;
//對方活二
if(((net[0][0]+net[1][1]+net[2][2])==2)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==0)
level[i][j]=level[i][j]+x2;
//單個X
if(((net[0][0]+net[1][1]+net[2][2])==1)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==0)
level[i][j]=level[i][j]+x;
//單個O
if(((net[0][0]+net[1][1]+net[2][2])==2)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==1)
level[i][j]=level[i][j]+o;
//空行,000
if(((net[0][0]+net[1][1]+net[2][2])==0)&&(net[0][0]*net[1][1]*net[2][2])==0&&
((net[0][0]-1)*(net[1][1]-1)*(net[2][2]-1))==-1)
level[i][j]=level[i][j]+nothing;
}

//副對角線
if((i==0&&j==2)||(i==2&&j==0)||(i==1&&j==1)){
//己方活二
if(((net[0][2]+net[1][1]+net[2][0])==4)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==-1)
level[i][j]=level[i][j]+o2;
//對方活二
if(((net[0][2]+net[1][1]+net[2][0])==2)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==0)
level[i][j]=level[i][j]+x2;
//單個X
if(((net[0][2]+net[1][1]+net[2][0])==1)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==0)
level[i][j]=level[i][j]+x;
//單個O
if(((net[0][2]+net[1][1]+net[2][0])==2)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==1)
level[i][j]=level[i][j]+o;
//空行,000
if(((net[0][2]+net[1][1]+net[2][0])==0)&&(net[0][2]*net[1][1]*net[2][0])==0&&
((net[0][2]-1)*(net[1][1]-1)*(net[2][0]-1))==-1)
level[i][j]=level[i][j]+nothing;
}//the end of for if
}//the end of if
}//the end of for j
}//the end of for j


//尋找最大權值的位置
int maxi = 0,maxj = 0,temp = 0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(level[i][j]>temp)
{

temp=level[i][j];
maxi=i;
maxj=j;
}
System.out.println("net[i][j]="+net[i][j]);
System.out.println("level[i][j]="+level[i][j]);
}
}

覺得作者不容易的話請頂一個(*^__^*) 謝謝

相關文章