Previous question:
Tic-Tac-Toe in C++11 - follow-up 2
Here, AI is being implemented.
#include <iostream>
#include <cctype>
#include <algorithm>
#include <functional>
#include <array>
#include <ctime>
template <typename T>
T getRandom(T min, T max)
{
return (rand() % (max + 1 - min) + min);
}
enum struct Player : char
{
none = '-',
first = 'X',
second = 'O'
};
std::ostream& operator<<(std::ostream& os, Player p)
{
return os << static_cast<char>(p);
}
enum struct Type : int
{
row = 0,
column = 1,
diagonal = 2
};
template<int mesh>
class TicTacToe
{
public:
TicTacToe();
TicTacToe(const TicTacToe &t) = delete;
TicTacToe(const TicTacToe &&t) = delete;
bool isFull() const;
void draw() const;
bool isWinner(Player player) const;
bool applyMove(Player player, unsigned row, unsigned column);
private:
unsigned remaining = mesh * mesh;
std::array<Player, mesh * mesh> mGrid;
};
// utility functor to compute matching condition
template<int mesh>
struct Match
{
Match(Type t, int i) : mCategory(t), mNumber(i){}
bool operator() (int number) const
{
switch (mCategory)
{
case Type::row:
return (std::abs(number / mesh) == mNumber);
case Type::column:
return (number % mesh == mNumber);
case Type::diagonal:
if (mNumber == 0)
return ((std::abs(number / mesh) - number % mesh) == mNumber);
if (mNumber == 1)
return ((std::abs(number / mesh) + number % mesh) == mNumber + mesh - 2);
}
return false;
}
Type mCategory;
int mNumber;
};
template<int mesh>
TicTacToe<mesh>::TicTacToe()
{
mGrid.fill(Player::none);
}
template<int mesh>
bool TicTacToe<mesh>::applyMove(Player player, unsigned row, unsigned column)
{
unsigned position = row + mesh * column;
if ((position > mGrid.size()) || (mGrid[position] != Player::none))
return true;
--remaining;
mGrid[position] = player;
return false;
}
template<int mesh>
bool TicTacToe<mesh>::isFull() const
{
return (remaining == 0) ? true : false;
}
template<int mesh>
bool TicTacToe<mesh>::isWinner(Player player) const
{
// check for row or column wins
std::array<bool, 2 * (mesh + 1)> win;
win.fill(true);
int j = 0;
// checking condition loop
std::for_each(mGrid.begin(), mGrid.end(),
[&](Player i)
{
int x = j++;
for (auto k = 0; k < mesh; ++k)
{
if (Match<mesh>(Type::column, k)(x))
win[k] &= i == player;
if (Match<mesh>(Type::row, k)(x))
win[mesh + k] &= i == player;
if (Match<mesh>(Type::diagonal, k)(x))
{
if (k < 2)
win[2 * mesh + k] &= i == player;
}
}
});
for (auto i : win)
{
if (i)
return true;
}
return false;
}
template<int mesh>
void TicTacToe<mesh>::draw() const
{
//Creating a onscreen grid
std::cout << ' ';
for (auto i = 1; i <= mesh; ++i)
std::cout << " " << i;
int j = 0;
char A = 'A';
for (auto i : mGrid)
{
if (j == 0)
{
std::cout << "\n " << A++;
j = mesh;
}
--j;
std::cout << ' ' << i << ' ';
}
std::cout << "\n\n";
}
class Game
{
public:
Game() = default;
void run();
private:
void showResult() const;
void turn(int);
static const unsigned mMesh = 3;
TicTacToe<mMesh> mGame;
std::array<Player, 2> mPlayers{ { Player::first, Player::second } };
int mPlayer = 1;
};
void Game::run()
{
std::cout << "Please choose Game mod: \n"
<< " 1 - Player vs Player\n"
<< " 2 - player vs Computer\n\n> ";
int choice;
std::cin >> choice;
while (!mGame.isWinner(mPlayers[mPlayer]) && !mGame.isFull())
{
mPlayer ^= 1;
mGame.draw();
turn(choice);
}
showResult();
}
void Game::showResult() const
{
mGame.draw();
if (mGame.isWinner(mPlayers[mPlayer]))
{
std::cout << "\n" << mPlayers[mPlayer] << " is the Winner!\n";
}
else
{
std::cout << "\nTie game!\n";
}
}
void Game::turn(int n)
{
char row = 0;
char column = 0;
if (n == 1)
{
std::cout << "\n" << mPlayers[mPlayer] << ": Please play. \n";
for (bool pending = true; pending;)
{
std::cout << "Row(1,2,3,...): ";
std::cin >> row;
std::cout << mPlayers[mPlayer] << ": Column(A,B,C,...): ";
std::cin >> column;
column = std::toupper(column) - 'A';
row -= '1';
pending = column < 0 || row < 0 || mGame.applyMove(mPlayers[mPlayer], row, column);
if (pending)
std::cout << "Invalid position. Try again.\n";
}
}
else
{
for (bool pending = true; pending;)
{
switch (mPlayers[mPlayer])
{
case Player::first:
std::cout << "\n" << mPlayers[mPlayer] << ": Please play. \n";
std::cout << "Row(1,2,3,...): ";
std::cin >> row;
std::cout << mPlayers[mPlayer] << ": Column(A,B,C,...): ";
std::cin >> column;
column = std::toupper(column) - 'A';
row -= '1';
pending = column < 0 || row < 0 || mGame.applyMove(mPlayers[mPlayer], row, column);
if (pending)
std::cout << "Invalid position. Try again.\n";
break;
case Player::second:
row = getRandom(0, int(mMesh - 1));
column = getRandom(0, int(mMesh - 1));
pending = mGame.applyMove(mPlayers[mPlayer], row, column);
break;
}
}
}
std::cout << "\n\n";
}
int main()
{
srand(static_cast<unsigned int>(time(0)));
Game game;
game.run();
}