#include <algorithm>
#include <iostream>
#include <list>
#include <vector>

using namespace std;

enum state { PLAYING, XWIN, OWIN, DRAW };

constexpr const int infinity = 10;

state checkWin(const int (&gameBoard)[9])
{
    for (int i = 0; i != 3; ++i) {
        if (gameBoard[0 + 3 * i] + gameBoard[1 + 3 * i] + gameBoard[2 + 3 * i] == 3) {
            return XWIN;
        } else if (gameBoard[0 + 3 * i] + gameBoard[1 + 3 * i] + gameBoard[2 + 3 * i] == -3) {
            return OWIN;
        }
        if (gameBoard[0 + i] + gameBoard[3 + i] + gameBoard[6 + i] == 3) {
            return XWIN;
        } else if (gameBoard[0 + i] + gameBoard[3 + i] + gameBoard[6 + i] == -3) {
            return OWIN;
        }
    }
    if (gameBoard[0] + gameBoard[4] + gameBoard[8] == 3
        || gameBoard[2] + gameBoard[4] + gameBoard[6] == 3) {
        return XWIN;
    }
    if (gameBoard[0] + gameBoard[4] + gameBoard[8] == -3
        || gameBoard[2] + gameBoard[4] + gameBoard[6] == -3) {
        return OWIN;
    }
    if (std::find(gameBoard, gameBoard + 9, 0) != gameBoard + 9) {
        return PLAYING;
    }
    return DRAW;
}

void generate_moves(const int (&gameBoard)[9], list<int> &moves)
{
    for (int i = 0; i < 9; i++) {
        if (gameBoard[i] == 0) {
            moves.push_back(i);
        }
    }
}

int evaluate_position(const int (&gameBoard)[9], int playerTurn)
{
    state currentGameState = checkWin(gameBoard);
    if (currentGameState != PLAYING) {
        if ((playerTurn == 1 && currentGameState == XWIN) || (playerTurn == -1 && currentGameState == OWIN))
            return +infinity;
        else if ((playerTurn == -1 && currentGameState == XWIN) || (playerTurn == 1 && currentGameState == OWIN))
            return -infinity;
        else if (currentGameState == DRAW)
            return 0;
    }
    return -1;
}

int MaxMove(int (&gameBoard)[9], int playerTurn)
{
    int pos_val = evaluate_position(gameBoard, playerTurn);
    if (pos_val != -1) return pos_val;

    int bestScore = -infinity;
    list<int> movesList;
    generate_moves(gameBoard, movesList);

    while (!movesList.empty()) {
        gameBoard[movesList.front()] = playerTurn;
        int score = -MaxMove(gameBoard, -playerTurn);
        if (score > bestScore) {
            bestScore = score;
        }
        gameBoard[movesList.front()] = 0;
        movesList.pop_front();
    }
    return bestScore;
}

int MiniMax(int (&gameBoard)[9], int playerTurn)
{
    int bestScore = -infinity;
    list<int> movesList;
    vector<int> bestMoves;
    generate_moves(gameBoard, movesList);

    while (!movesList.empty()) {
        gameBoard[movesList.front()] = playerTurn;
        int score = -MaxMove(gameBoard, -playerTurn);
        if (score > bestScore) {
            bestScore = score;
            bestMoves.clear();
            bestMoves.push_back(movesList.front());
        } else if (score == bestScore) {
            bestMoves.push_back(movesList.front());
        }
        gameBoard[movesList.front()] = 0;
        movesList.pop_front();
    }

#if 0
    int index = bestMoves.size();
    if (index > 0) {
        time_t secs;
        time(&secs);
        srand((uint32_t)secs);
        index = rand() % index;
    }
    return bestMoves[index];
#else
    return bestMoves.front();
#endif
}

void print(const int (&gameBoard)[9])
{
    const char cs[] = {'O', '.', 'X'};

    std::cout << "--------" << std::endl;
    for (int i = 0; i != 3; ++i) {
        for (int j = 0; j != 3; ++j) {
            std::cout << cs[1 + gameBoard[3 * i + j]];
        }
        std::cout << std::endl;
    }
}

int main()
{
    int gameBoard[9] {};

    int player = 1;
    while (checkWin(gameBoard) == PLAYING) {
        gameBoard[MiniMax(gameBoard, player)] = player;
        print(gameBoard);
        player *= -1;
    }
    return 0;
}
