2015-09-25 62 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的状态,这意味着其他玩家将失去。