五子棋估值算法


目錄
        程序布局
        估值算法
        完整代碼

 

 

 

 

 

程序布局


  首先說明整個五子棋程序的整體布局。(用Java實現)

class Chess{    //界面類
  Player player1 ;
  Player player2;
  ChessBox box;
  //其余界面顯示相關函數;
}

class Player{
  int code;  //代號  
1:選手1 2:選手2
  ChessBox box;  
  abstract Point play(); //落子操作  
  int getLine(Point p, int i, int j) ;
}
class Person extends Player{
  Point play(
int x,int y );
}
class Robot extends Player{  //機器   int evaluate(Point, int, int);   int Evaluate(Point);   Point play(); } class ChessBox{   int chess_flag[15][15] //0:空 1:選手1 2:選手2 }

 

 

 

 

 

估值算法


要求分析

  估值算法。要求給定棋盤上一個點,求出該點在當前棋局下的權值。若在該點落子后更容易接近勝利,則該點權值就高,越接近5子相連,權值越高。

  則函數的形式為 int Evaluate(Point p); 

  首先考慮每個點有8個方向可以連子,每個方向上又有多種連子棋型,如活四、活三、死三等,而這些子又可能屬於己方或者對方。活四與活三的權值自然不同。而同樣是活三,己方的活三與對方的活三權值也不同,這樣才能實現攻守的策略。假如現在棋局上同時有己方的活三和對方的活三,此時輪到我方落子,則正常情況下應當在己方活三上落子,使之成為活四,從而獲勝。則計算機在判斷棋局時,遇到己方活三,權值應當較高,遇到對方活三,權值應當較低。

  以上即是對於估值函數所應達到的要求的分析。

 

方向問題

  由於着眼處在於對棋型的判斷,而不是方向,所以首先應該想個方法把方向問題先解決掉,這樣在棋型判斷時就能夠對各個方向進行比較統一的處理,不至於棋型判斷時對每個方向都寫一段代碼。

  繼續分析,在判斷棋型時,着眼點在於棋子的相對位置,而常見棋型都呈線形排列,所以這個相對位置也就是順序。相對位置、順序,很容易想到要用一維的坐標解決。若取某一斜列(行、列),假設當前點的坐標為0,取右下(下、右、右上)為正方向,則在該斜列(行、列)上各點都能得到相應的坐標。如下圖。

                                                          

  但若是同樣的一維坐標,不同的方向,又會對應棋盤上不同的位置,也就是說,一維坐標轉換到棋盤上的二維坐標,還需要一個方向。(額,想到這里,突然發現自己的思路明明就是極坐標啊。。。 ̄□ ̄||.........)

  由此,我們需要達到這么一種要求:給定一個點、一個方向、一個相對坐標值,就能得到一個二維坐標,對應棋盤上一個點,進而可以獲得任意一點的落子情況。所以我寫了這么一個函數:

 int getLine(Point p,int i,int j);

  其中p為當前點,i為方向,取值為從1到8的整數,對應8個方向,j為相對於p點的坐標值。在函數體內要依據方向對p的x、y的值進行處理。返回該點的落子情況,0表示無子,1或2分別表示兩個player,-1表示超出棋盤界。

  代碼如下:

 1  int getLine(Point p, int i, int j) { // p:當前點  i:方向  j:坐標相對值 
 2          int x = p.x, y = p.y;
 3          switch (i) {  //對8個方向的處理  4             case 1 :
 5                 x = x + j;
 6                 break;
 7             case 2 :
 8                 x = x + j;
 9                 y = y + j;
10                 break;
11            ...
12            ...
13             case 8 :
14                 x = x + j;
15                 y = y - j;
16         }
17         if (x < 0 || y < 0 || x > 14 || y > 14) { // 越界處理  返回-1
18             return -1;
19         }
20         return box.getFlag(x,y);
21       }
22    } 

 

棋型判斷

  對於方向的處理完成后,就是棋型的判斷。判斷棋型時需要區分當前所判斷的棋型是哪一方的,假設當前所判斷的棋型所屬方的代號為plyer,則它的值可以是1或2,而要確定這個plyer是自己還是對方,就需要和自己的代號比對一下,假設自己的代號是me。則這個判斷棋型的函數應該滿足以下要求:給出一個點p,自己的代號me,一個plyer,能得出當前點對應plyer的權值。於是函數形式如下:

 int evaluate(Point p, int me,int plyer);

  然后結合已有的算法結構,參考下圖(網上找到的)

                   

  將棋型分為以下幾種:

 /* 

  *: 當前空位置;

  0: 其他空位置;

  1: plyer(當前所計算的player的代號);

  2: 3-plyer(對方的代號);

*/

1.活四 :01111*

2.死四A :21111*

3.死四B :111*1

4.死四C :11*11

5.活三(近三位置) :111*0

6.活三(遠三位置) :1110*                               

7.死三            :11*1   

  此外由於兩個或多個方向上都有活二的棋型較為常見且勝率較高(見下圖)。所以又增加對此種棋型的判斷。

                   

  即在每一個方向的棋型判斷中掃描011*0111*0並計數,若最終計數值大於等於2,則權值增加一個較大的數值,否則不增加。

  

  至此只要循環8次,每次循環中掃描各個棋型,並更新權值(設為value)即可。

  代碼如下:

 1 int evaluate(Point p, int me,int plyer) { /* me:我的代號;  plyer:當前計算的player的代號;*/
 2         int value = 0;
 3         int numoftwo=0;
 4      for (int i = 1; i <= 8; i++) { // 8個方向
 5             // 活四       01111*      *代表當前空位置    0代表其他空位置
 6             if (getLine(p, i, -1) == plyer && getLine(p, i, -2) == plyer
 7                     && getLine(p, i, -3) == plyer && getLine(p, i, -4) == plyer
 8                     && getLine(p, i, -5) == 0) {
 9                 value += 300000;
10                 if(me!=plyer){value-=500;}
11                 System.out.print("+ 300000");
12                 continue;
13               }
14        ...
15             //計算011*0或111*0的個數   
16             if (getLine(p, i, -1) == plyer && getLine(p, i, -2) == plyer
17                     && getLine(p, i, -3) != 3-plyer&&getLine(p,i,1)!=3-plyer) {
18                 numoftwo++;
19              }
20         ...
21       }
22     if(numoftwo>=2){
23       value+=3000;
24       if(me!=plyer){
25         value-=100;
26         }
27       }
28     return value;
29 }

  其中每種棋型對value值所做的貢獻要依據實際情況不斷調整優化,優化不當就可能造成計算機放着活三不堵跑去堵活二了。。。

  最終的估值函數 int Evaluate(Point p) 只要調用 int evaluate(Point p, int me,int plyer) 函數就可以獲得p點的權值。 

  代碼如下:

1 int Evaluate(Point p){
2   return evaluate(p,code,1)+ evaluate(p,code,2);  //code是調用者的代號 3 }

 

 

 

成果

  最終程序核心算法只運用該估值算法,沒有進行深度搜索。界面如下:

 

  可見估值算法即便非常完美(當然這個算法離完美還差得遠 ̄□ ̄||),依然無法做到立於不敗之地,因為往往會出現對方有多個接近連五,以至於堵都堵不住。所以博弈還是必須要深度搜索的。

 

 

 

 

 

完整代碼 


 最后貼出自己寫的估值算法完整的代碼(僅供參考,正確性未經嚴格驗證):

  1 int Evaluate(Point p){
  2         return evaluate(p, code,1)
  3                 + evaluate(p, code,2);
  4     }
  5 
  6 int evaluate(Point p, int me,int plyer) { // me:我的代號  plyer:當前計算的player的代號
  7         int value = 0;
  8         int numoftwo=0;
  9         for (int i = 1; i <= 8; i++) { // 8個方向
 10             // 活四 01111* *代表當前空位置  0代表其他空位置    下同 
 11             if (getLine(p, i, -1) == plyer && getLine(p, i, -2) == plyer
 12                     && getLine(p, i, -3) == plyer && getLine(p, i, -4) == plyer
 13                     && getLine(p, i, -5) == 0) {
 14                 value += 300000;
 15                 if(me!=plyer){value-=500;}
 16                 continue;
 17             }
 18             // 死四A 21111*
 19             if (getLine(p, i, -1) == plyer && getLine(p, i, -2) == plyer
 20                     && getLine(p, i, -3) == plyer && getLine(p, i, -4) == plyer
 21                     && (getLine(p, i, -5) == 3 - plyer||getLine(p, i, -5) == -1)) {
 22                 value += 250000;
 23                 if(me!=plyer){value-=500;}
 24                 continue;
 25             }
 26             // 死四B 111*1
 27             if (getLine(p, i, -1) == plyer && getLine(p, i, -2) == plyer
 28                     && getLine(p, i, -3) == plyer && getLine(p, i, 1) == plyer) {
 29                 value += 240000;
 30                 if(me!=plyer){value-=500;}
 31                 continue;
 32             }
 33             // 死四C 11*11
 34             if (getLine(p, i, -1) == plyer && getLine(p, i, -2) == plyer
 35                     && getLine(p, i, 1) == plyer && getLine(p, i, 2) == plyer) {
 36                 value += 230000;
 37                 if(me!=plyer){value-=500;}
 38                 continue;
 39             }
 40             // 活三 近3位置 111*0
 41             if (getLine(p, i, -1) == plyer && getLine(p, i, -2) == plyer
 42                     && getLine(p, i, -3) == plyer) {
 43                 if (getLine(p, i, 1) == 0) {
 44                     value += 750;
 45                     if (getLine(p, i, -4) == 0) {
 46                         value += 3150;
 47                         if(me!=plyer){value-=300;}
 48                     }
 49                 }
 50                 if ((getLine(p, i, 1) == 3 - plyer||getLine(p, i, 1) == -1) && getLine(p, i, -4) == 0) {
 51                     value += 500;
 52                 }
 53                 continue;
 54             }
 55             // 活三 遠3位置 1110*
 56             if (getLine(p, i, -1) == 0 && getLine(p, i, -2) == plyer
 57                     && getLine(p, i, -3) == plyer && getLine(p, i, -4) == plyer) {
 58                 value += 350;
 59                 continue;
 60             }
 61             // 死三 11*1
 62             if (getLine(p, i, -1) == plyer && getLine(p, i, -2) == plyer
 63                     && getLine(p, i, 1) == plyer) {
 64                 value += 600;
 65                 if (getLine(p, i, -3) == 0 && getLine(p, i, 2) == 0) {
 66                     value += 3150;
 67                     continue;
 68                 }
 69                 if ((getLine(p, i, -3) == 3 - plyer||getLine(p, i, -3) == -1) && (getLine(p, i, 2) == 3 - plyer||getLine(p, i, 2) == -1)) {
 70                     continue;
 71                 } else {
 72                     value += 700;
 73                     continue;
 74                 }
 75             }
 76             //活二的個數   
 77             if (getLine(p, i, -1) == plyer && getLine(p, i, -2) == plyer
 78                     && getLine(p, i, -3) != 3-plyer&&getLine(p,i,1)!=3-plyer) {
 79                 numoftwo++;
 80             }
 81             //其余散棋
 82             int numOfplyer = 0; // 因為方向會算兩次?
 83             for (int k = -4; k <= 0; k++) { // ++++* +++*+ ++*++ +*+++ *++++
 84                 int temp = 0;
 85                 for (int l = 0; l <= 4; l++) {
 86                     if (getLine(p, i, k + l) == plyer) {
 87                         temp++;
 88                     } else
 89                         if (getLine(p, i, k + l) == 3 - plyer
 90                                 || getLine(p, i, k + l) == -1) {
 91                         temp = 0;
 92                         break;
 93                     }
 94                 }
 95                 numOfplyer += temp;
 96             }
 97             value += numOfplyer * 15;
 98             if (numOfplyer != 0) {
 99             }
100         }
101         if(numoftwo>=2){
102             value+=3000;
103             if(me!=plyer){
104                 value-=100;
105                 }
106             }
107         return value;
108     }
109 
110 int getLine(Point p, int i, int j) { // i:方向 j:相對p的順序值(以p為0) p:當前點
111         int x = p.x, y = p.y;
112         switch (i) {
113             case 1 :
114                 x = x + j;
115                 break;
116             case 2 :
117                 x = x + j;
118                 y = y + j;
119                 break;
120             case 3 :
121                 y = y + j;
122                 break;
123             case 4 :
124                 x = x - j;
125                 y = y + j;
126                 break;
127             case 5 :
128                 x = x - j;
129                 break;
130             case 6 :
131                 x = x - j;
132                 y = y - j;
133                 break;
134             case 7 :
135                 y = y - j;
136                 break;
137             case 8 :
138                 x = x + j;
139                 y = y - j;
140         }
141         if (x < 0 || y < 0 || x > 14 || y > 14) { // 越界處理
142             return -1;
143         }
144         return box.getFlag(x,y);
145     }

END 

 2015.9.21 10:53


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM