2017-03-20 105 views
0

我目前正在爲棋盤遊戲Hex寫一個AI。我想用蒙特卡洛樹搜索來做到這一點,並且已經試圖實現它。然而,人工智能做出了令人難以置信的愚蠢(隨機)移動,我無法弄清楚爲什麼它不起作用。蒙特卡洛樹搜索不工作

import java.util.ArrayList; 
import java.util.Random; 

/** 
* Created by Robin on 18.03.2017. 
*/ 
public class TreeNode { 


    private static final Random random = new Random(); 
    private static final double epsion=10e-5; 
    protected double nvisits; 
    protected double totValue; 
    protected int move=-1; 

    private HexBoard board; 
    protected ArrayList<TreeNode>children ; 



    public TreeNode(HexBoard board){ 
     this.board =board; 
    } 


    //Copy-Constructor 
    public TreeNode(TreeNode treeNode){ 
     this.nvisits=treeNode.nvisits; 
     this.totValue=treeNode.totValue; 
     this.move=treeNode.move; 
     this.board = new HexBoard(treeNode.board); 

    } 

    public void update(double value){ 
     totValue+=value*board.color; 
     nvisits++; 
    } 



    public void expand(){ 
     assert(children==null); 
     children = new ArrayList<>(121-board.moveCount); 
     for(int i=0;i<121;i++){ 
      if(board.board[i]!=HexBoard.EMPTY) 
       continue; 

       TreeNode newNode = new TreeNode(board); 
       newNode.move =i; 
       children.add(newNode); 

     } 
    } 

    public void calculateIteration(){ 
     ArrayList<TreeNode>visited = new ArrayList<>(); 
     TreeNode current =this; 
     visited.add(current); 

     while(!current.isLeafNode()){ 
      current =current.select(); 
      board.makeMove(current.move); 
      visited.add(current); 
     } 

     //Found a leaf node 
     double value; 
     if(current.board.getWinner()==0){ 
      current.expand(); 
      TreeNode newNode =current.select(); 
      value =playOut(newNode.board); 
     }else{ 
      value =current.board.getWinner(); 
     } 

     //update all the nodes 

     for(int i=1;i<visited.size();i++){ 
      visited.get(i).update(value); 
      board.undoMove(visited.get(i).move); 
     } 
     visited.get(0).update(value); 
    } 

    public static int playOut(HexBoard board){ 
     int winner=0; 

     if(board.moveCount==121) { 
      winner=board.getWinner(); 

      return winner; 
     } 

     //Checking-Movecount vs actual stones on the board 


     final double left =121-board.moveCount; 
     double probibility =1/left; 
     double summe =0; 
     double p =random.nextDouble(); 

     int randomMove =0; 
     for(int i=0;i<121;i++){ 
      if(board.board[i]!=HexBoard.EMPTY) 
       continue; 

      summe+=probibility; 

      if(p<=summe && probibility!=0) { 
       randomMove = i; 
       break; 
      } 
     } 

     board.makeMove(randomMove); 
     winner =playOut(board); 
     board.undoMove(randomMove); 

     return winner; 
    } 


    public TreeNode select(){ 

     TreeNode bestNode=null; 
     double bestValue =-10000000; 
     for(TreeNode node : children){ 

      double uctvalue =(node.nvisits==0)?100000:(node.totValue/(node.nvisits)+Math.sqrt((Math.log(this.nvisits))/(2*node.nvisits))); 
      uctvalue+=epsion*random.nextDouble(); 

      if(uctvalue>bestValue){ 
       bestValue=uctvalue; 
       bestNode =node; 
      } 
     } 

     return bestNode; 
     /// 
    } 

    public boolean isLeafNode(){ 
     return (children==null); 
    } 
} 

我在方法calcualteIteration()中的實現是否正確?

我知道這可能不是看一個非常有吸引力的問題,但我希望得到任何幫助

+0

這太寬泛了。請進行一些調試以縮小這個問題的範圍,使其更簡單一些,以及[最小測試用例](https://stackoverflow.com/help/mcve)。 –

+0

你真的在跟蹤哪個球員做出哪些動作嗎?你在迭代中輪流輪流嗎?對我來說,看起來你只是讓現在的玩家在你的模擬中填滿整個棋盤,它假裝沒有對手。或者我錯過了什麼?此外,告訴我們您正在運行多少模擬以及如何最終決定在「真實」遊戲中玩什麼遊戲會很有用 –

+0

對不起,我應該澄清這一點。 board.makemove()函數在兩個玩家之間交替。我嘗試了100-50000次模擬中的所有事情,結果幾乎相同(壞隨機動作)。根節點的「最佳」兄弟是具有最高uct值的兄弟,並且將由AI – CheckersGuy

回答

1

OP中添加了問題後評論額外信息。該額外信息的重要部分是實現了makeMove()方法來檢查下一個播放器(確保更新板是正確的)。

鑑於這些信息,OP中select()的實現是不正確的,因爲它沒有考慮在計算UCT得分時哪個玩家要移動。 UCT得分包括一個「開發」部分(第一部分,計算所有先前模擬的平均得分)和一個「探索」部分(平方根下的部分,對於已經訪問過的節點,相對於其父母來說很少)。當對手被允許下一步移動時,該等式的開發部分應該被否定。如果沒有這樣做,AI將基本上認爲對手願意積極幫助AI,而不是假設對手會爲自己贏得勝利。

+1

謝謝。現在它運行得非常好只用5000次模擬測試,我無法獲勝:P – CheckersGuy

+0

最好的價值是最高勝率,而不是uct值(用於指導樹的進一步探索),特別是在引入隨機組件。其他實施者在大約1000-1500個播出後已經實現了完美的播放。 –