Search code examples
javaartificial-intelligencetic-tac-toeminmax

MinMax algorithm is not counting the human player


I am using MinMax algorithm for Tictactoe, and it works correctly, but it's trying to win, without blocking my turns, so it's very easy to win the AI. I added the depth parameter, but the result is the same. Here you can see my code:

public int playMiniMax(Tableau t, char maximizingPlayer,int depth) {
    int bestMove = 0;
    if (t.getWinner() == 'x') {
        return 1;
    } else if (t.getWinner() == 'd') {
        return 0;
    } else if (t.getWinner() == 'o') {
        return -1;
    }
    else if (depth == 0) {
        return 0;
    }
    else {
        int bestValue;
        int i;
        if (maximizingPlayer == 'x') {
            bestValue = Integer.MIN_VALUE;

            for(i = 1; i <= 9; ++i) {
                if (t.chooseCase(i, 'x')) {
                    bestValue = Math.max(bestValue, this.playMiniMax(t, 'o',depth-1));
                    bestMove = i;
                    t.resetCase(i);
                }
            }
        } else {
            bestValue = Integer.MAX_VALUE;

            for(i = 1; i <= 9; ++i) {
                if (t.chooseCase(i, 'o')) {
                    bestValue = Math.min(bestValue, this.playMiniMax(t, 'x',depth-1));
                    bestMove = i;
                    t.resetCase(i);
                }
            }
        }

        return bestMove;
    }
}

The "bestMove" is the best case(from 1 to 9) proposed by minmax, and in my main I am calling it like this:

 int movei = playerAI.playMiniMax(t, 'x',3);
            correctCase = t.chooseCase(movei, 'x');



public class Tableau {
    private char[][] charArray;
    private boolean gameIsOver = false;
    private char winner = 'n';

Tableau(char[][] _charArray) {
    this.charArray = _charArray;
    this.moves = new Stack();
    this.ceateTable();
}

public void ceateTable() {
    for(int i = 0; i < this.charArray.length; ++i) {
        for(int j = 0; j < this.charArray.length; ++j) {
            if (j != 2 && j != 5) {
                System.out.print(this.charArray[i][j] + "|");
            } else {
                System.out.println(this.charArray[i][j] + "|");
            }
        }
    }

}

public boolean chooseCase(int number, char player) {
    int c = 1;

    for(int i = 0; i < this.charArray.length; ++i) {
        for(int j = 0; j < this.charArray.length; ++j) {
            if (c == number && this.charArray[i][j] == '-') {
                this.charArray[i][j] = player;
                return true;
            }

            ++c;
        }
    }

    return false;
}

public void resetCase(int number) {
    int c = 1;

    for(int i = 0; i < this.charArray.length; ++i) {
        for(int j = 0; j < this.charArray.length; ++j) {
            if (c == number) {
                this.charArray[i][j] = '-';
            }

            ++c;
        }
    }

}

public boolean checkWin(char player) {
    if ((this.charArray[0][0] != player || this.charArray[0][1] != player || this.charArray[0][2] != player) && (this.charArray[1][0] != player || this.charArray[1][1] != player || this.charArray[1][2] != player) && (this.charArray[2][0] != player || this.charArray[2][1] != player || this.charArray[2][2] != player) && (this.charArray[0][0] != player || this.charArray[1][0] != player || this.charArray[2][0] != player) && (this.charArray[0][1] != player || this.charArray[1][1] != player || this.charArray[2][1] != player) && (this.charArray[0][2] != player || this.charArray[1][2] != player || this.charArray[2][2] != player) && (this.charArray[0][0] != player || this.charArray[1][1] != player || this.charArray[2][2] != player) && (this.charArray[0][2] != player || this.charArray[1][1] != player || this.charArray[2][0] != player)) {
        return false;
    } else {
        System.out.println("Dear " + player + " you won!");
        this.gameIsOver = true;
        this.winner = player;
        return true;
    }
}

public boolean isFull() {
    for(int i = 0; i < this.charArray.length; ++i) {
        for(int j = 0; j < this.charArray.length; ++j) {
            if (this.charArray[i][j] == '-') {
                return false;
            }
        }
    }

    return true;
}

public boolean checkDraw(char player) {
    if (!this.checkWin(player) && this.isFull()) {
        System.out.println("It's a draw!!!");
        this.winner = 'd';
        return true;
    } else {
        return false;
    }
}

public boolean checkLoose(char player) {
    return player == 'x' ? this.checkWin('o') : false;
}

public boolean isGameIsOver() {
    return this.gameIsOver;
}

public void setGameIsOver(boolean gameIsOver) {
    this.gameIsOver = gameIsOver;
}

public char getWinner() {
    return this.winner;
}

public void setWinner(char winner) {
    this.winner = winner;
}

}


Solution

  • The problem relies on the ChooseCase function, because it is just evaluating where the board is empty, so it would choose the first or the last empy space ( depending on minimizing or maximizing). You need to adjust your Evaluation Function to be aware of the board state so it can makes a better choice, taking this exmple: https://kartikkukreja.wordpress.com/2013/03/30/heuristic-function-for-tic-tac-toe/

    In Java should look like this:

    private final int possibleWins = 8;
    private final int[][] threeInARow = {
            { 0, 1, 2 },
            { 3, 4, 5 },
            { 6, 7, 8 },
            { 0, 3, 6 },
            { 1, 4, 7 },
            { 2, 5, 8 },
            { 0, 4, 8 },
            { 2, 4, 6 }
    };
    private final int[][] heuristicArray = {
            { 0, -10, -100, -1000 },
            { 10, 0, 0, 0 },
            { 100, 0, 0, 0 },
            { 1000, 0, 0, 0 }
    };
    
    public int evaluatePosition(char[] board, char player) {
        char opponent = (player == 'X') ? 'O' : 'X', piece;
        int players, others, t = 0, i, j;
    
        for (i = 0; i < 8; i++)  {
            players = others = 0;
            for (j = 0; j < 3; j++)  {
                piece = board[threeInARow[i][j]];
                if (piece == player)
                    players++;
                else if (piece == opponent)
                    others++;
            }
            t += heuristicArray[players][others];
        }
        return t;
    }
    

    Also check the previous question: How to create an evaluation function for a TIC-TAC-TOE variant game