2012-06-10 8 views
6

Actualmente estoy tratando de enseñarme el algoritmo Minimax y he intentado implementarlo en java en tic tac toe. Sin embargo, hay un error en mi algoritmo y no puedo descubrir qué lo está causando.Error en algoritmo Minimax para Tic Tac Toe

A continuación se muestra el código fuente completo (Lo siento por muro de texto!):

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; 
    } 
} 

Aquí está la salida al ejecutar el programa (Computer es un círculo):

[ ][ ][ ] 
[ ][ ][ ] 
[ ][ ][ ] 
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: 

Como Puede ver después del primer movimiento, la computadora piensa que no importa qué movimiento haga, puede obtener un empate (Puntuación = 0).

En el segundo movimiento puse una cruz en la columna 0, fila 1. Por alguna razón, la computadora piensa que hay dos movimientos posibles para llegar a un empate (Puntuación = 0) y solo cuatro movimientos para perder (Puntuación = -1). Luego realiza una jugada incorrecta pensando que obtendrá un empate.

Primero pensé que había algo mal con el método hasWon, pero probé las 8 formas de obtener tres seguidas y todas devuelven la verdad.

Sospecho que el problema existe en algún lugar de los métodos findBestMove, max o min, pero no he podido averiguar exactamente qué lo está causando.

Realmente agradecería que alguien pudiera decir lo que está causando el error o dar alguna sugerencia sobre cómo mejorar la depuración de mi algoritmo recursivo.

Respuesta

7

Me parece que mezcló partes de min y max. Actualmente, su max devuelve el valor del peor movimiento posible (para él) humano podría tomar, en lugar del movimiento óptimo que la computadora podría tomar. Del mismo modo, min devuelve el valor del peor movimiento que podría tomar la computadora , en lugar del movimiento óptimo para el oponente.

solucionar este problema mediante la conmutación de los PlayerSigns en min y max, y findBestMove debe llamar min, no max.

+0

¡Funciona ahora! ¡Gracias! – ScoobyD