-1

I'm trying to implement a TicTacToe AI using a minimax algorithm.

When it's the AI's turn to play, I call ComputerTurn (which takes in the board state, an array of ints that tracks whether a square is X, O, or empty). ComputerTurn then calls minimax (the minimax algorithm) and win (which checks for 3 in a row).

When I run the script, the algorithm always decides to return the lowest legal play. IE, as long as the top left square (tile 0) is available, it will always return it first. If that square is taken, it will return the top middle (tile 1) etc.

I'm not sure what's happening here and my traditional debugging technique (Debug.Log or print) is causing Unity to crash at many points I want to look at.

void ComputerTurn(int[] board)
{
    int move = -1;
    int score = -2;
    int i;
    for (i = 0; i < 9; ++i)
    {
        if (board[i] == 0)
        {
            board[i] = 1;
            int tempScore = -minimax(board, -1);
            board[i] = 0;
            if (tempScore > score)
            {
                score = tempScore;
                move = i;
            }
        }
    }

    board[move] = 1;
    if (PlayerTurn == 1)
    {
        //Draw an O
        Board[move] = -1;
    }
    else
    {
        //Draw an X
        Board[move] = 1;
    }
    //Changes to player's turn
}

int minimax(int[] board, int player)
{
    int winner = win(board);
    if (winner != 0) return winner * player;

    int move = -1;
    int score = -2;//Losing moves are preferred to no move
    int i;
    for (i = 0; i < 9; ++i)
    {//For all moves,
        if (board[i] == 0)
        {//If legal,
            board[i] = player;//Try the move
            int thisScore = -minimax(board, player * -1);
            if (thisScore > score)
            {
                score = thisScore;
                move = i;
            }//Pick the one that's worst for the opponent
            board[i] = 0;//Reset board after try
        }
    }
    if (move == -1) return 0;
    return score;
}

int win(int[] board) 
{
    //determines if a player has won, returns 0 otherwise.
    int[,] wins = new int[8, 3] { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }, { 0, 3, 6 }, { 1, 4, 7 }, { 2, 5, 8 }, { 0, 4, 8 }, { 2, 4, 6 } };
    int i;
    for (i = 0; i< 8; ++i)
    {
        if (board[wins[i, 0]] != 0 &&
            board[wins[i, 0]] == board[wins[i, 1]] &&
            board[wins[i, 0]] == board[wins[i, 2]])
        {
            return board[wins[i, 2]];
        }
    }
    return 0;
}

2 Answers2

1

It does not always return the first empty cell. For example, try to feed it with [0, 0, 0, -1, 0, -1, 1, 0, 1] position: it will not return 0, it will choose 4 instead. Your implementation does not contain any errors.

The problem is in the algorithm. Since your weight function can only result in 1, 0 or -1, your program can only see if it is possible to win by that turn or not, but does not see any difference between strong moves (with high victory output) and weak ones (where winning is possible, but not likely). It filters out the loosing moves though, as you can see from the provided example.

rs232
  • 1,248
  • 8
  • 16
0

edit: how do I mark this as solved

I found out what was happening.

board[move] = 1;
if (PlayerTurn == 1)
{
    //Draw an O
    Board[move] = -1;
}
else
{
    //Draw an X
    Board[move] = 1;
}
//Changes to player's turn

Should actually be

Board[move] = 1;

if (PlayerTurn == 1)
{
    //Draw a Y
}
else
{
    //Draw an X
}

//Change turn

There was also an error in how I was doing my player turns. Thanks to anyone who looked over my question.