Previous question:
Tic-Tac-Toe in C++11 - follow-up
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();
}
enum struct
. \$\endgroup\$Line
) and give TicTacToe an iterator over all rows, all columns and all diagonals (all lines). Row, column and diagonal again can have an iterator over its fields. You then can check for win withfor (auto line : TicTacToe.lines()) { bool win = True; for (auto field : line.fields()) { if {field.player() != player) { win = false; break; } } if (win) return true; } return false; }
\$\endgroup\$