2012-06-10 8 views
6

Ich versuche derzeit, mir selbst den Minimax-Algorithmus zu lehren, und ich habe versucht, es in Java im Tic Tac Toe zu implementieren. Es gibt jedoch einen Fehler in meinem Algorithmus und ich kann nicht herausfinden, was das verursacht.Fehler in Minimax-Algorithmus für Tic Tac Toe

Im Folgenden finden Sie die komplette Quellcode (Sorry für die Wand des Textes!):

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

Hier ist die Ausgabe, wenn ich das Programm ausführen (Computer ist Kreis):

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

Wie Sie kann nach dem ersten Zug sehen, dass der Computer denkt, dass egal, welche Bewegung es macht, kann es eine Ziehung bekommen (Score = 0).

Im zweiten Zug setze ich ein Kreuz auf Spalte 0, Zeile 1. Aus irgendeinem Grund denkt der Computer dann, dass es zwei mögliche Züge gibt, um ein Unentschieden zu erreichen (Score = 0) und nur vier Züge zu verlieren (Score = -1). Es macht dann einen falschen Zug und denkt, dass es ein Unentschieden bekommen wird.

Zuerst dachte ich, dass etwas mit der hasWon-Methode nicht stimmt, aber ich habe alle acht Möglichkeiten getestet, drei hintereinander zu bekommen, und alle kehren wahr zurück.

Ich vermute, dass das Problem irgendwo in den Methoden findBestMove, max oder min existiert, aber ich konnte nicht genau herausfinden, was es verursacht.

Ich würde es wirklich schätzen, wenn jemand sagen könnte, was den Fehler verursacht oder irgendwelche Vorschläge gibt, wie ich meinen rekursiven Algorithmus besser debuggen kann.

Antwort

7

Sieht für mich wie Sie Teile von min und max verwechselt. Derzeit gibt max den Wert des schlechtest möglichen Zuges zurück (für ihn) könnte der Mensch nehmen, anstatt den optimalen Zug zu nehmen, den der Computer nehmen könnte. Ebenso gibt min den Wert des schlechtesten Zuges zurück, den der Computer annehmen konnte, anstatt den optimalen Zug für den Gegner.

Fix dies durch die PlayerSigns in min Schalt- und max und findBestMove sollte min nennen, nicht max.

+0

Es funktioniert jetzt! Vielen Dank! – ScoobyD

Verwandte Themen