Search code examples
javatic-tac-toeminimax

Tic-Tac-Toe AI minimax function in Java


I have here my code for the minimax algorithm. It seems to be making smart decisions, but not always, which shouldn't be happening and I need help because I can't seem to find where I went wrong.

private char level2Move() {
  try {
    NBoard game_copy = (NBoard) game.clone();

    int bestScore = Integer.MIN_VALUE;
    char bestMove = '-';

    for (char move: game_copy.getValidMoves()) {
      game_copy.enterMove(move);
      int score = minimax(game_copy, true);
      game_copy.undoMove();
      if (score > bestScore) {
        bestScore = score;
        bestMove = move;
      }
    }

    return bestMove;
  } catch(CloneNotSupportedException e) {
    e.printStackTrace();
  }

  return '-';
}
public int minimax(NBoard game, boolean isMaximizing) {
  int bestScore;

  switch (game.getGameState()) {
  case NBoard.X_WIN:
    return - 10;
  case NBoard.O_WIN:
    return 10;
  case NBoard.DRAW:
    return 0;
  }

  if (isMaximizing) {
    bestScore = Integer.MIN_VALUE;
    for (char move: game.getValidMoves()) {
      if (game.getGameState() == NBoard.ONGOING) {
        game.enterMove(move);
        int score = minimax(game, false);
        game.undoMove();
        bestScore = Math.max(score, bestScore);
      }
    }
  } else {
    bestScore = Integer.MAX_VALUE;
    for (char move: game.getValidMoves()) {
      if (game.getGameState() == NBoard.ONGOING) {
        game.enterMove(move);
        int score = minimax(game, true);
        game.undoMove();
        bestScore = Math.min(score, bestScore);
      }
    }
  }
  return bestScore;
}

Board input (move) are letters A-I, like so...

A|B|C
D|E|F
G|H|I

I seem to have not used "score" yet and am thinking of adding depth, but I can't think of how that would help.


Solution

  • I fixed it. Simply changed the boolean values in the minimax function.

    private char level2Move() {
      try {
        NBoard game_copy = (NBoard) game.clone();
    
        int bestScore = Integer.MIN_VALUE;
        char bestMove = '-';
    
        for (char move: game_copy.getValidMoves()) {
          game_copy.enterMove(move);
          int score = minimax(game_copy, false);       // here
          game_copy.undoMove();
          if (score > bestScore) {
            bestScore = score;
            bestMove = move;
          }
        }
    
        return bestMove;
      } catch(CloneNotSupportedException e) {
        e.printStackTrace();
      }
    
      return '-';
    }
    
    public int minimax(NBoard game, boolean isMaximizing) {
      int bestScore;
    
      switch (game.getGameState()) {
      case NBoard.X_WIN:
        return - 10;
      case NBoard.O_WIN:
        return 10;
      case NBoard.DRAW:
        return 0;
      }
    
      if (isMaximizing) {
        bestScore = Integer.MIN_VALUE;
        for (char move: game.getValidMoves()) {
          if (game.getGameState() == NBoard.ONGOING) {
            game.enterMove(move);
            int score = minimax(game, false);          // here
            game.undoMove();
            bestScore = Math.max(score, bestScore);
          }
        }
      } else {
        bestScore = Integer.MAX_VALUE;
        for (char move: game.getValidMoves()) {
          if (game.getGameState() == NBoard.ONGOING) {
            game.enterMove(move);
            int score = minimax(game, true);           // here
            game.undoMove();
            bestScore = Math.min(score, bestScore);
          }
        }
      }
      return bestScore;
    }