#include <iostream>
#include <array>
#include <vector>
#include <algorithm>
#include <ctime>
#include <limits> // Added for numeric_limits
enum GameState : int
{
Running,
Draw,
Win
};
enum Symbol : char
{
Tic = 'X',
Tac = 'O',
Space = ' '
};
class TicTacToe
{
public:
std::array<Symbol, 9> board;
TicTacToe() {
[Link](Symbol::Space);
}
void print_board() const {
for (int i = 0; i < 9; ++i) {
if (i % 3)
std::cout << " | ";
std::cout << static_cast<char>(board[i]);
if (i == 2 || i == 5)
std::cout << "\n---------\n";
}
std::cout << "\n\n";
}
static Symbol swap(const Symbol symbol) {
return symbol == Symbol::Tic ? Symbol::Tac : Symbol::Tic;
}
GameState evalState(const Symbol symbol) {
if (match(symbol, 0, 1, 2) || match(symbol, 3, 4, 5) || match(symbol, 6, 7,
8) || // Horizontal checks
match(symbol, 0, 3, 6) || match(symbol, 1, 4, 7) || match(symbol, 2, 5,
8) || // Vertical Checks
match(symbol, 0, 4, 8) || match(symbol, 2, 4, 6)) // Diagonal Checks
return GameState::Win;
if (std::count([Link](), [Link](), Symbol::Space) == 0)
return GameState::Draw;
return GameState::Running;
}
private:
bool match(const Symbol symbol, int i1, int i2, int i3) {
return board[i1] == symbol && board[i2] == symbol && board[i3] == symbol;
}
};
class Player
{
public:
std::string name;
virtual int getMove(TicTacToe&, const Symbol) = 0;
};
class PlayerHuman : public Player
{
public:
virtual int getMove(TicTacToe& game, const Symbol symbol) override {
int move = -1;
while (move == -1) {
std::cout << "Enter your move for " << static_cast<char>(symbol) << "
(1-9)\n";
std::cin >> move;
if (!std::[Link]()) {
std::[Link]();
std::[Link](std::numeric_limits<std::streamsize>::max(), '\n');
}
--move;
if (move < 0 || move > 8 || [Link][move] != Symbol::Space) {
std::cerr << "Invalid input\n\n";
move = -1;
}
}
return move;
};
};
class PlayerAI : public Player
{
public:
char maxDepth = 10;
virtual int getMove(TicTacToe& game, const Symbol symbol) override {
std::cout << "AI thinking for " << static_cast<char>(symbol) << "\n";
// select random move if board is empty
if (std::count([Link](), [Link](), Symbol::Space) == 9)
return std::rand() % 9;
// calculate best move with minimax
int bestScore = -1000;
std::vector<char> moves;
for (char i = 0; i < 9; i++) {
if ([Link][i] == Symbol::Space) {
[Link][i] = symbol;
char score = minimax(game, 0, false, symbol);
if (score > bestScore) {
bestScore = score;
[Link]();
}
if (score == bestScore)
moves.push_back(i);
[Link][i] = Symbol::Space;
}
}
return moves[std::rand() % [Link]()];
protected:
int minimax(TicTacToe& game, int depth, bool isMax, const Symbol symbol) {
Symbol opponent = TicTacToe::swap(symbol);
GameState state = [Link](symbol);
if (state == GameState::Win)
return 10 - depth;
if ([Link](opponent) == GameState::Win)
return -10 + depth;
if (state == GameState::Draw || depth >= maxDepth)
return 0;
int best = isMax ? -1000 : 1000;
for (char i = 0; i < 9; i++) {
if ([Link][i] == Symbol::Space) {
[Link][i] = isMax ? symbol : opponent;
int score = minimax(game, depth + 1, !isMax, symbol);
best = isMax ? std::max(best, score) : std::min(best, score);
[Link][i] = Symbol::Space;
}
}
return best;
}
};
class Game
{
private:
TicTacToe game;
public:
void Play(Player& player1, Player& player2) {
int index = std::rand() % 2;
Symbol symbol = Symbol::Tic;
GameState state = GameState::Running;
while (state == GameState::Running) {
Player& player = index ? player1 : player2;
game.print_board();
int move = [Link](game, symbol);
std::cout << [Link] << " selected " << move + 1 << "\n\n";
[Link][move] = symbol;
state = [Link](symbol);
if (state != GameState::Running) {
game.print_board();
if (state == GameState::Win)
std::cout << [Link] << " Won!";
else
std::cout << "It's a Draw.";
}
else
{
index = !index;
symbol = TicTacToe::swap(symbol);
}
}
}
};
int main() {
std::srand(static_cast<unsigned int>(time(0)));
PlayerHuman player1;
[Link] = "human";
PlayerAI player2;
[Link] = "AI";
Game game;
[Link](player1, player2);
}