Stumbleine75
Stumbleine75

Reputation: 401

Issues with AI for a tic tac toe game in C++

So I'm having a few issues with my tic tac toe game's AI. The AI is meant to be purely defensive, so it will always block you. It works usually works initially with my first case, so for example if I play the top left then the top middle, it will always take the top right. Other cases, like if I play two diagonals, may freeze the game, or allow me a second turn. These problems arose only after I implemented my faulty smart AI. Initially, the program was had a 'dumb AI' that chose random spots as you'll see at the bottom of the code. Moreover, what I think the problem is is that the some of the blocking conditions can conflict with each other and cause problems. How can I fix this? Relevant code is as follows: The coordinates are based of of the tic tac toe grid where the top left is (0,0) and the bottom right is (2,2)

void Game::AIGetNextMoveRand()
{
    //top row
    if(GetSquareState(0,0) == O && GetSquareState(1,0) == O)
    {
        do
        {
            AIMoveX = 2;
            AIMoveY = 0; 
        }
            while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);

    }
    else if(GetSquareState(2,0) == O && GetSquareState(1,0) == O)
    {

        do
        {
            AIMoveX = 0;
            AIMoveY = 0;
        }
            while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    //middle row
    else if(GetSquareState(0,1) == O && GetSquareState(1,1) == O)
    {
        do
        {
        AIMoveX = 2;
        AIMoveY = 1;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    else if(GetSquareState(2,1) == O && GetSquareState(1,1) == O)
    {
        do
        {
        AIMoveX = 0;
        AIMoveY = 1;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);    
    }
    // bottom row
    else if(GetSquareState(0,2) == O && GetSquareState(1,2) == O)
    {
        do
        {
            AIMoveX = 2;
            AIMoveY = 2;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    else if(GetSquareState(2,2) == O && GetSquareState(1,2) == O)
    {
        do
        {
        AIMoveX = 0;
        AIMoveY = 2;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    // vert 0
    else if(GetSquareState(0,0) == O && GetSquareState(0,1) == O)
    {
        do
        {
        AIMoveX = 0;
        AIMoveY = 2;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    else if(GetSquareState(0,2) == O && GetSquareState(0,1) == O)
    {
        do
        {
            AIMoveX = 0;
            AIMoveY = 0;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    // vert 1
    else if(GetSquareState(1,0) == O && GetSquareState(1,1) == O)
    {

        do
        {
            AIMoveX = 1;
            AIMoveY = 2;
        }
            while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    else if(GetSquareState(1,2) == O && GetSquareState(1,1) == O)
    {
        do
        {
            AIMoveX = 1;
            AIMoveY = 0;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    //vert 2
    else if(GetSquareState(2,2) == O && GetSquareState(2,1) == O)
    {
        do
        {
            AIMoveX  = 2;
            AIMoveY = 0;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    else if(GetSquareState(2,0) == O && GetSquareState(2,1) == O)
    {
        do
        {
            AIMoveX = 2;
            AIMoveY = 2;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    // diagonal 1
    else if(GetSquareState(0,0) == O && GetSquareState(1,1) == O)
    {

        do
        {
            AIMoveX = 2;
            AIMoveY = 2;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    else if(GetSquareState(2,2) == O && GetSquareState(1,1) == O)
    {

        do
        {
            AIMoveX = 0;
            AIMoveY = 0;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    //diagonal 2
    else if(GetSquareState(0,2) == O && GetSquareState(1,1) == O)
    {
        do
        {
            AIMoveX = 0;
            AIMoveY = 2;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    else if(GetSquareState(0,2) == O && GetSquareState(1,1) == O)
    {
        do
        {
            AIMoveX = 2;
            AIMoveY = 0;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    // mid 0
    else if(GetSquareState(0,0) == O && GetSquareState(2,0) == O)
    {

        do
        {
            AIMoveX = 1;
            AIMoveY = 0;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    //mid 1
    else if(GetSquareState(1,0) == O && GetSquareState(2,1) == O)
    {
        do
        {
            AIMoveX = 1;
            AIMoveY = 1;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    // mid 2
    else if(GetSquareState(0,2) == O && GetSquareState(2,2) == O)
    {
        do
        {
            AIMoveX = 1;
            AIMoveY = 2;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    //diag 1
    else if(GetSquareState(0,0) == O && GetSquareState(2,2) == O)
    {
        do
        {
            AIMoveX = 1;
            AIMoveY = 1;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }
    //diag 2
    else if(GetSquareState(2,0) == O && GetSquareState(0,2) == O)
    {

        do 
        {
            AIMoveX = 1;
            AIMoveY = 1;
        }
        while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
    }

    else 
    {
    do 
    {
        AIMoveX = rand() % 3;
        AIMoveY = rand() % 3;
    }while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);

    }
    }

void Game::DoAITurnRand()
{
    AIGetNextMoveRand();
    SetSquareState(AIMoveX,AIMoveY,activePlayer);
    EndTurn();
}

Upvotes: 0

Views: 822

Answers (1)

john
john

Reputation: 87944

The do while loops are garbage. Think about it if your rules have made a wrong choice (i.e. tried to play on a filled square), then making the same wrong choice again isn't going to help. In fact that's why you get the 'freezes'. You need to reorganize the logic of your code. I would suggest that if your AI selects an illegal square then you should fall back to your random picking code, then at least you would get a legal move.

Something like this

// no move selected yet
AIMoveX = -1;
AIMoveY = -1;

// AI rules
if(GetSquareState(0,0) == O && GetSquareState(1,0) == O)
{
    AIMoveX = 2;
    AIMoveY = 0; 
}
else if(GetSquareState(2,0) == O && GetSquareState(1,0) == O)
{
    AIMoveX = 0;
    AIMoveY = 0;
}
// lots more rules
...

// check for fallback to random move
if ((AIMoveX == -1 && AIMoveY == -1)             // if no rules applied
    || GetSquareState(AIMoveX,AIMoveY) != EMPTY) // or if the square is not empty
{
    // pick a random square
    do 
    {
        AIMoveX = rand() % 3;
        AIMoveY = rand() % 3;
    }
    while(GetSquareState(AIMoveX,AIMoveY) != EMPTY);
}

I've initially set AIMoveX and AIMoveY to -1 to indicate that no rule has been picked yet. If they are still -1 after going through all the rules then I know that no rules got picked and I have to make a random choice. If a rule did get picked but it picked a non-empty square then I also make a random choice. I test for both of those conditions at the end of my code.

You have to think carefully about exactly what the code you write really does. Just putting a do while loop in one place because it worked in another place is not right.

Upvotes: 1

Related Questions