2015-09-25 68 views
1

我有一個任務,用人類玩家和AI玩家編寫NIM遊戲。這場比賽是玩「Misere」(最後一個必須拿一根棍子輸掉)。人工智能應該是使用Minimax算法,但它的動作讓它失去更快,我不知道爲什麼。現在我已經死了好幾天了。 Minimax算法的要點是不會丟失,如果它處於失敗位置,則會延遲儘可能多的移動,對吧?NIM遊戲和使用Minimax算法的人工智能玩家 - AI讓人失去移動

考慮以下幾點:

NIMBoard板=新NIMBoard(34,2);

  • 34 =二進制編碼棍棒的位置,2樁2根
  • 2 =

所以我們這個方案開始樁的數量,較棒的*字符:

Row 0: ** 
Row 1: ** 

在這個特定的電路板情況下,Minimax算法總是會提出「從第1行中移除2根支桿」的舉動。這顯然是一個糟糕的舉動,因爲它在第0排中留下了2根棍子,其中人類選手可以從第0排中挑選1根棍子並贏得比賽。

AI玩家應該選擇從一堆中選擇一根。這使得本作的人類玩家:

Row 0: * 
Row 1: ** 

所以無論是哪個移動現在人類球員做,當計算機發出後的下一步行動,人類玩家將總是輸。顯然這是一個更好的策略,但爲什麼算法沒有提出這一舉措?

public class Minimax 
{ 

    public Move nextMove; 

    public int evaluateComputerMove(NIMBoard board, int depth) 
    { 
     int maxValue = -2; 
     int calculated; 
     if(board.isFinal()) 
     { 
      return -1; 
     } 
     for(Move n : this.generateSuccessors(board)) 
     { 
      NIMBoard newBoard = new NIMBoard(board.getPos(), board.getNumPiles()); 
      newBoard.parseMove(n); 
      calculated = this.evaluateHumanMove(newBoard, depth + 1); 
      if(calculated > maxValue) 
      { 
       maxValue = calculated; 
       if(depth == 0) 
       { 
        System.out.println("Setting next move"); 
        this.nextMove = n; 
       } 
      } 

     } 

     if(maxValue == -2) 
     { 
      return 0; 
     } 
     return maxValue; 
    } 

    public int evaluateHumanMove(NIMBoard board, int depth) 
    { 
     int minValue = 2; 
     int calculated; 
     if(board.isFinal()) 
     { 
      return 1; 
     } 
     for(Move n : this.generateSuccessors(board)) 
     { 
      NIMBoard newBoard = new NIMBoard(board.getPos(), board.getNumPiles()); 
      newBoard.parseMove(n); 
      calculated = this.evaluateComputerMove(newBoard, depth + 1); 
      // minValue = Integer.min(this.evaluateComputerMove(newBoard, depth + 1), minValue); 
      if(calculated < minValue) 
      { 
       minValue = calculated; 
      } 
     } 
     if(minValue == 2) 
     { 
      return 0; 
     } 

     return minValue; 
    } 

    public ArrayList<Move> generateSuccessors(NIMBoard start) 
    { 
     ArrayList<Move> successors = new ArrayList<Move>(); 
     for(int i = start.getNumPiles() - 1; i >= 0; i--) 
     { 
      for(long j = start.getCountForPile(i); j > 0; j--) 
      { 
       Move newMove = new Move(i, j); 
       successors.add(newMove); 
      } 
     } 

     return successors; 
    } 
} 

public class NIMBoard 
{ 
    /** 
    * We use 4 bits to store the number of sticks which gives us these 
    * maximums: 
    * - 16 piles 
    * - 15 sticks per pile 
    */ 
    private static int PILE_BIT_SIZE = 4; 
    private long pos; 
    private int numPiles; 
    private long pileMask; 

    /** 
    * Instantiate a new NIM board 
    * @param pos Number of sticks in each pile 
    * @param numPiles Number of piles 
    */ 
    public NIMBoard(long pos, int numPiles) 
    { 
     super(); 
     this.pos  = pos; 
     this.numPiles = numPiles; 

     this.pileMask = (long) Math.pow(2, NIMBoard.PILE_BIT_SIZE) - 1; 
    } 

    /** 
    * Is this an endgame board? 
    * @return true if there's only one stick left 
    */ 
    public boolean isFinal() 
    { 
     return this.onePileHasOnlyOneStick(); 
    } 

    /** 
    * Figure out if the board has a pile with only one stick in it 
    * @return true if yes 
    */ 
    public boolean onePileHasOnlyOneStick() 
    {   
     int count = 0; 

     for(int i = 0; i < this.numPiles; i++) 
     { 
      count += this.getCountForPile(i); 
     } 

     if(count > 1) 
     { 
      return false; 
     } 

     return true; 
    } 


    public int getNumPiles() 
    { 
     return this.numPiles; 
    } 

    public long getPos() 
    { 
     return this.pos; 
    } 


    public long getCountInPile(int pile) 
    { 
     return this.pos & (this.pileMask << (pile * NIMBoard.PILE_BIT_SIZE)); 
    } 

    public long getCountForPile(int pile) 
    { 
     return this.getCountInPile(pile) >> (pile * NIMBoard.PILE_BIT_SIZE); 
    } 

    public void parseMove(Move move) 
    { 
     this.pos = this.pos - (move.getCount() << (move.getPile() * NIMBoard.PILE_BIT_SIZE)); 
    } 

    @Override 
    public String toString() 
    { 
     String tmp = ""; 
     for(int i = 0; i < this.numPiles; i++) 
     { 
      tmp += "Row " + i + "\t"; 
      for(int j = 0; j < this.getCountForPile(i); j++) 
      { 
       tmp += "*"; 
      } 
      tmp += System.lineSeparator(); 
     } 

     return tmp.trim(); 
    } 

} 
+0

很抱歉的壞格式化,我似乎已經打破堆棧溢出解析器: - \ 下面是我用導出AI玩家的下一步行動: '極小極小=新極小();' 換行符 ' int result = minimax.evaluateComputerMove(board,0);' newline 'return minimax.nextMove;' – RedShift

+0

您確定在人類儘可能好的時候從空中角度挑選最大值的最大值移動,也就是讓另一個玩家進入的最壞位置,而不是最小值,這可能是最糟糕的舉動嗎? – Davislor

+0

我這麼認爲,你是說'if(calculated> maxValue)'是不夠的,我需要比較其他東西或比較更多? – RedShift

回答

3

,你猜是人工智能更好的移動此舉實際上不是一個好舉措。在該板的情況下,人類玩家將從第1排拿下兩根棍子,並且電腦仍然卡住最後一根棍子。這並不能保證你的程序工作正常,但我認爲你應該嘗試一些不同的測試用例。舉例來說,如果你給出了你認爲人類玩家會失敗的情況,那麼看看AI的作用。

0

你不應該爲人類球員有不同的功能。你應該假設兩個玩家都使用最好的策略,而且由於你正在實施它,所以這兩個玩家應該是相同的代碼。

該算法的思想不是將狀態ID分配給當前狀態,等於最小狀態ID,它不會覆蓋任何可能結束狀態的狀態ID。如果您可以進行移動並且達到ID爲0,1和3的狀態,那麼當前狀態應該具有狀態ID 2. 任何丟失狀態都應該具有ID 0.

如果您的當前狀態具有狀態ID 0,不管你做什麼動作。否則,你可以找到一個移動,將棋盤移動到ID爲0的狀態,這意味着其他玩家將失去。