Is there any way to improve this code.
#include <iostream>
#include <cctype>
#include <algorithm>
#include <functional>
#include <array>
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
};
enum struct Lines : int
{
first = 0,
second = 1,
third = 2
};
class TicTacToe
{
public:
TicTacToe();
bool isFull() const;
void draw() const;
void turn(Player player);
bool check(Player player) const;
private:
bool applyMove(Player player, int position);
static const std::size_t mDim = 3;
std::array<Player, mDim * mDim> mGrid;
};
// utility functor to compute matching condition
template<int dim>
struct Match
{
Match(Type t, Lines i) : mCategory(t), mNumber(i){}
bool operator() (int number) const
{
switch (mCategory)
{
case Type::row:
return (std::abs(number / dim) == static_cast<int>(mNumber));
case Type::column:
return (number % dim == static_cast<int>(mNumber));
case Type::diagonal:
if (mNumber == Lines::first)
return ((std::abs(number / dim) - number % dim) == static_cast<int>(mNumber));
else
return ((std::abs(number / dim) + number % dim) == static_cast<int>(mNumber));
}
return false;
}
Type mCategory;
Lines mNumber;
};
TicTacToe::TicTacToe()
{
mGrid.fill(Player::none);
}
bool TicTacToe::applyMove(Player player, int position)
{
if (mGrid[position] != Player::none)
return false;
mGrid[position] = player;
return true;
}
bool TicTacToe::isFull() const
{
return 0 == std::count_if(mGrid.begin(), mGrid.end(),
[](Player i)
{
return i == Player::none;
});
}
bool TicTacToe::check(Player player) const
{
// check for row or column wins
std::array<bool, 8> win;
win.fill(true);
int j = 0;
// checking condition loop
std::for_each(mGrid.begin(), mGrid.end(),
[&](Player i)
{
int x = j++;
// columns
if (Match<mDim>(Type::column, Lines::first)(x))
win[0] &= i == player;;
if (Match<mDim>(Type::column, Lines::second)(x))
win[1] &= i == player;
if (Match<mDim>(Type::column, Lines::third)(x))
win[2] &= i == player;
// rows
if (Match<mDim>(Type::row, Lines::first)(x))
win[3] &= i == player;
if (Match<mDim>(Type::row, Lines::second)(x))
win[4] &= i == player;
if (Match<mDim>(Type::row, Lines::third)(x))
win[5] &= i == player;
// diagonals
if (Match<mDim>(Type::diagonal, Lines::first)(x))
win[6] &= i == player;
if (Match<mDim>(Type::diagonal, Lines::third)(x))
win[7] &= i == player;
});
for (auto i : win)
{
if (i)
return true;
}
return false;
}
void TicTacToe::draw() const
{
//Creating a onscreen grid
std::cout << ' ';
for (auto i = 1; i <= mDim; ++i)
std::cout << " " << i;
int j = 0;
char A = 'A';
for (auto i : mGrid)
{
if (Match<mDim>(Type::column, Lines::first)(j++))
std::cout << "\n " << A++;
std::cout << ' ' << i << ' ';
}
std::cout << "\n\n";
}
void TicTacToe::turn(Player player)
{
char row = 0;
char column = 0;
std::size_t position = 0;
bool applied = false;
std::cout << "\n" << player << ": Please play. \n";
while (!applied)
{
std::cout << "Row(1,2,3,...): ";
std::cin >> row;
std::cout << player << ": Column(A,B,C,...): ";
std::cin >> column;
position = mDim * (std::toupper(column) - 'A') + (row - '1');
if (position < mGrid.size())
{
applied = applyMove(player, position);
if (!applied)
std::cout << "Already Used. Try Again. \n";
}
else
{
std::cout << "Invalid position. Try again.\n";
}
}
std::cout << "\n\n";
}
class Game
{
public:
Game() = default;
void run();
private:
TicTacToe mTicTacToe;
std::array<Player, 2> mPlayers{ { Player::first, Player::second } };
int mPlayer = 1;
void resultScreen(bool winner);
std::function<void()> display = std::bind(&TicTacToe::draw, &mTicTacToe);
std::function<void(Player)> turn = std::bind(&TicTacToe::turn, &mTicTacToe, std::placeholders::_1);
std::function<bool(Player)> win = std::bind(&TicTacToe::check, &mTicTacToe, std::placeholders::_1);
std::function<bool()> full = std::bind(&TicTacToe::isFull, &mTicTacToe);
};
void Game::run()
{
while (!win(mPlayers[mPlayer]) && !full())
{
mPlayer ^= 1;
display();
turn(mPlayers[mPlayer]);
}
resultScreen(win(mPlayers[mPlayer]));
}
void Game::resultScreen(bool winner)
{
display();
if (winner)
{
std::cout << "\n" << mPlayers[mPlayer] << " is the Winner!\n";
}
else
{
std::cout << "\nTie game!\n";
}
}
int main()
{
Game game;
game.run();
}