2012-06-10 26 views
6

Tôi hiện đang cố gắng tự dạy thuật toán Minimax và tôi đã cố gắng thực hiện nó bằng java trong tic tac toe. Có một lỗi trong thuật toán của tôi tuy nhiên và tôi không thể tìm ra nguyên nhân gây ra nó.Lỗi trong thuật toán Minimax cho Tic Tac Toe

Dưới đây là toàn bộ mã nguồn (Xin lỗi vì bức tường của văn bản!):

public class TicTacToe { 
    private static boolean gameEnded = false; 
    private static boolean player = true; 
    private static Scanner in = new Scanner(System.in); 
    private static Board board = new Board(); 

    public static void main(String[] args){ 
     System.out.println(board); 
     while(!gameEnded){ 
      Position position = null; 
      if(player){ 
       position = makeMove(); 
       board = new Board(board, position, PlayerSign.Cross); 
      }else{ 
       board = findBestMove(board); 
      }    
      player = !player; 
       System.out.println(board); 
       evaluateGame(); 
     } 
    } 

    private static Board findBestMove(Board board) { 
     ArrayList<Position> positions = board.getFreePositions(); 
     Board bestChild = null; 
     int previous = Integer.MIN_VALUE; 
     for(Position p : positions){ 
      Board child = new Board(board, p, PlayerSign.Circle); 
      int current = max(child); 
      System.out.println("Child Score: " + current); 
      if(current > previous){ 
       bestChild = child; 
       previous = current; 
      } 
     } 
     return bestChild; 
    } 

    public static int max(Board board){ 
     GameState gameState = board.getGameState(); 
     if(gameState == GameState.CircleWin) 
      return 1; 
     else if(gameState == GameState.CrossWin) 
      return -1; 
     else if(gameState == GameState.Draw) 
      return 0; 
     ArrayList<Position> positions = board.getFreePositions(); 
     int best = Integer.MIN_VALUE; 
     for(Position p : positions){ 
      Board b = new Board(board, p, PlayerSign.Cross); 
      int move = min(b); 
      if(move > best) 
       best = move; 
     }  
     return best; 
    } 

    public static int min(Board board){ 
     GameState gameState = board.getGameState(); 
     if(gameState == GameState.CircleWin) 
      return 1; 
     else if(gameState == GameState.CrossWin) 
      return -1; 
     else if(gameState == GameState.Draw) 
      return 0; 
     ArrayList<Position> positions = board.getFreePositions(); 
     int best = Integer.MAX_VALUE; 
     for(Position p : positions){ 
      Board b = new Board(board, p, PlayerSign.Circle); 
      int move = max(b); 
      if(move < best) 
       best = move; 
     } 
     return best; 
    } 

    public static void evaluateGame(){ 
     GameState gameState = board.getGameState(); 
     gameEnded = true; 
     switch(gameState){ 
      case CrossWin : 
       System.out.println("Game Over! Cross Won!"); 
       break; 
      case CircleWin : 
       System.out.println("Game Over! Circle Won!"); 
       break; 
      case Draw : 
       System.out.println("Game Over! Game Drawn!"); 
       break; 
      default : gameEnded = false; 
       break; 
     } 
    } 

    public static Position makeMove(){ 
     Position position = null; 
     while(true){ 
      System.out.print("Select column(x-axis). 0, 1 or 2: "); 
      int column = getColOrRow(); 
      System.out.print("Select row(y-axis). 0, 1 or 2: "); 
      int row = getColOrRow(); 
      position = new Position(column, row); 
      if(board.isMarked(position)) 
       System.out.println("Position already marked!"); 
      else break; 
     } 
     return position; 
    } 

    private static int getColOrRow(){ 
     int ret = -1; 
     while(true){ 
      try{ 
       ret = Integer.parseInt(in.nextLine()); 
      } catch (NumberFormatException e){} 
      if(ret < 0 | ret > 2) 
       System.out.print("\nIllegal input... please re-enter: "); 
      else break; 
     } 
     return ret; 
    } 
} 

public enum PlayerSign{ 
    Cross, Circle 
} 

public enum GameState { 
    Incomplete, CrossWin, CircleWin, Draw 
} 

public final class Position { 
    public final int column; 
    public final int row; 

    public Position(int column, int row){ 
     this.column = column; 
     this.row = row; 
    } 
} 

public class Board { 
    private char[][] board; //e = empty, x = cross, o = circle. 

    public Board(){ 
     board = new char[3][3]; 
     for(int y = 0; y < 3; y++) 
      for(int x = 0; x < 3; x++) 
       board[x][y] = 'e'; //Board initially empty 
    } 

    public Board(Board from, Position position, PlayerSign sign){ 
     board = new char[3][3]; 
     for(int y = 0; y < 3; y++) 
      for(int x = 0; x < 3; x++) 
       board[x][y] = from.board[x][y]; 
     board[position.column][position.row] = sign==PlayerSign.Cross ? 'x':'o'; 
    } 

    public ArrayList<Position> getFreePositions(){ 
     ArrayList<Position> retArr = new ArrayList<Position>();  
     for(int y = 0; y < 3; y++) 
      for(int x = 0; x < 3; x++) 
       if(board[x][y] == 'e') 
        retArr.add(new Position(x, y)); 
     return retArr; 
    } 

    public GameState getGameState(){  
     if(hasWon('x')) 
      return GameState.CrossWin; 
     else if(hasWon('o')) 
      return GameState.CircleWin; 
     else if(getFreePositions().size() == 0) 
      return GameState.Draw; 
     else return GameState.Incomplete; 
    } 

    private boolean hasWon(char sign){ //8 ways to win. 
     if(board[1][1] == sign){ 
      if(board[0][0] == sign && board[2][2] == sign) 
       return true; 
      if(board[0][2] == sign && board[2][0] == sign) 
       return true; 
      if(board[1][0] == sign && board[1][2] == sign) 
       return true; 
      if(board[0][1] == sign && board[2][1] == sign) 
       return true; 
      } 
      if(board[0][0] == sign){ 
       if(board[0][1] == sign && board[0][2] == sign) 
        return true; 
       if(board[1][0] == sign && board[2][0] == sign) 
        return true; 
      } 
      if(board[2][2] == sign){ 
       if(board[1][2] == sign && board[0][2] == sign) 
        return true; 
       if(board[2][1] == sign && board[2][0] == sign) 
        return true; 
      } 
      return false; 
    } 

    public boolean isMarked(Position position){ 
     if(board[position.column][position.row] != 'e') 
      return true; 
     return false; 
    } 

    public String toString(){ 
     String retString = "\n"; 
     for(int y = 0; y < 3; y++){ 
      for(int x = 0; x < 3; x++){ 
       if(board[x][y] == 'x' || board[x][y] == 'o') 
        retString += "["+board[x][y]+"]"; 
       else 
        retString += "[ ]"; 
      } 
      retString += "\n"; 
     }  
     return retString; 
    } 
} 

Đây là kết quả khi tôi chạy chương trình (máy tính là hình tròn):

[ ][ ][ ] 
[ ][ ][ ] 
[ ][ ][ ] 
Select column(x-axis). 0, 1 or 2: 1 
Select row(y-axis). 0, 1 or 2: 1 
[ ][ ][ ] 
[ ][x][ ] 
[ ][ ][ ] 
Child Score: 0 
Child Score: 0 
Child Score: 0 
Child Score: 0 
Child Score: 0 
Child Score: 0 
Child Score: 0 
Child Score: 0 
[o][ ][ ] 
[ ][x][ ] 
[ ][ ][ ] 
Select column(x-axis). 0, 1 or 2: 0 
Select row(y-axis). 0, 1 or 2: 1 
[o][ ][ ] 
[x][x][ ] 
[ ][ ][ ] 
Child Score: -1 
Child Score: 0 
Child Score: 0 
Child Score: -1 
Child Score: -1 
Child Score: -1 
[o][ ][o] 
[x][x][ ] 
[ ][ ][ ] 
Select column(x-axis). 0, 1 or 2: 

Như bạn có thể thấy sau lần di chuyển đầu tiên, máy tính nghĩ rằng không có vấn đề gì di chuyển nó làm cho nó có thể có được một trận hòa (Điểm = 0).

Khi di chuyển lần thứ hai, tôi đặt dấu chéo trên cột 0, hàng 1. Vì lý do nào đó, máy tính sau đó nghĩ rằng có hai bước di chuyển có thể đạt được điểm số (Điểm = 0) và chỉ có bốn động tác để mất (Điểm số = -1). Sau đó nó làm cho một động thái không chính xác nghĩ rằng nó sẽ có được một trận hòa.

Lần đầu tiên tôi nghĩ rằng có điều gì đó sai với phương pháp hasWon, nhưng tôi đã thử nghiệm tất cả 8 cách để nhận được ba liên tiếp và tất cả đều trở lại đúng sự thật.

Tôi nghi ngờ rằng sự cố tồn tại ở đâu đó trong phương thức findBestMove, max hoặc min, nhưng tôi đã không thể tìm ra chính xác nguyên nhân gây ra nó.

Tôi thực sự đánh giá cao nếu ai đó có thể cho biết điều gì gây ra lỗi hoặc đưa ra bất kỳ đề xuất nào về cách gỡ lỗi tốt hơn thuật toán đệ quy của tôi.

Trả lời

7

Có vẻ như tôi đã trộn lẫn các phần của minmax. Hiện tại, max của bạn trả về giá trị của động thái tồi tệ nhất có thể (đối với anh ta) là con người có thể mất, thay vì di chuyển tối ưu mà máy tính có thể thực hiện. Tương tự như vậy, min trả về giá trị của động thái tồi tệ nhất là máy tính có thể mất, thay vì di chuyển tối ưu cho đối thủ.

Khắc phục điều này bằng cách chuyển PlayerSigns trong minmaxfindBestMove phải gọi min, chứ không phải max.

+0

Nó hoạt động ngay bây giờ! Cảm ơn! – ScoobyD