根据上一个问题:
以下是改进的总结:
Match类的昂贵调用构造函数enum struct Type和enum struct DiagonalsRandom类转换为模板,以允许int或unsigned值作为参数我怎样才能改进这个游戏?
#include <iostream>
#include <cctype>
#include <array>
#include <random>
enum struct Player : char
{
none = '-',
first = 'X',
second = 'O'
};
std::ostream& operator<<(std::ostream& os, Player const& p)
{
return os << std::underlying_type<Player>::type(p);
}
// TicTacToe Class takes care for the logic and the drawing of the game.
template<std::size_t DIM> // main reason for template is avoiding global variables
class TicTacToe
{
public:
TicTacToe();
bool isFull() const;
void draw() const;
bool isWinner(Player player) const;
bool applyMove(Player player, std::size_t row, std::size_t column);
private:
std::size_t mRemain = DIM * DIM;
std::array<Player, DIM * DIM> mGrid;
};
template<std::size_t DIM>
TicTacToe<DIM>::TicTacToe()
{
mGrid.fill(Player::none);
}
template<std::size_t DIM>
bool TicTacToe<DIM>::applyMove(Player player, std::size_t row, std::size_t column)
{
std::size_t position = row + DIM * column;
if ((position > mGrid.size()) || (mGrid[position] != Player::none))
{
return true;
}
--mRemain;
mGrid[position] = player;
return false;
}
template<std::size_t DIM>
bool TicTacToe<DIM>::isFull() const
{
return (mRemain == 0);
}
template<std::size_t DIM>
bool TicTacToe<DIM>::isWinner(Player player) const
{
std::array<bool, 2 * (DIM + 1)> win;
win.fill(true);
int j = 0;
for (auto i : mGrid)
{
int x = j++;
for (std::size_t k = 0; k < DIM; ++k)
{
if (x % DIM == k)
{
win[k] &= i == player;
}
if (x / DIM == k)
{
win[DIM + k] &= i == player;
}
if ((k == 0 && (x / DIM - x % DIM == k)) // Diagonals -> LeftTop RightBottom
|| (k == 1 && (x / DIM + x % DIM == DIM - k))) // Diagonals -> RightTop leftBottom
{
win[2 * DIM + k] &= i == player;
}
}
}
for (auto i : win)
{
if (i)
{
return true;
}
}
return false;
}
template<std::size_t DIM>
void TicTacToe<DIM>::draw() const
{
std::cout << ' ';
for (std::size_t i = 1; i <= DIM; ++i)
{
std::cout << " " << i;
}
int j = 0;
char A = 'A';
for (auto i : mGrid)
{
if (j == 0)
{
std::cout << "\n " << A++;
j = DIM;
}
--j;
std::cout << ' ' << i << ' ';
}
std::cout << "\n\n";
}
template<typename T>
class Random
{
public:
Random(const T& min, const T& max)
: mUnifomDistribution(min, max)
{}
T operator()()
{
return mUnifomDistribution(mEngine);
}
private:
std::default_random_engine mEngine{ std::random_device()() };
template <typename U>
static auto dist() -> typename std::enable_if<std::is_integral<U>::value, std::uniform_int_distribution<U>>::type;
template <typename U>
static auto dist() -> typename std::enable_if<std::is_floating_point<U>::value, std::uniform_real_distribution<U>>::type;
decltype(dist<T>()) mUnifomDistribution;
};
// Game class represent the game loop for the tic tac toe game
// it simply takes inputs by switching users to check for whom is the winner.
class Game
{
public:
void run();
private:
void showResult() const;
void turn();
static const std::size_t mDim = 4;
static const std::size_t mNumberOfPlayers = 2;
TicTacToe<mDim> mGame;
std::array<Player, mNumberOfPlayers> mPlayers{ { Player::first, Player::second } };
int mPlayer = 1;
Random<int> getRandom{ 0, mDim - 1 };
};
void Game::run()
{
while (!mGame.isWinner(mPlayers[mPlayer]) && !mGame.isFull())
{
mPlayer ^= 1;
mGame.draw();
turn();
}
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()
{
char row = 0;
char column = 0;
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();
column = getRandom();
pending = mGame.applyMove(mPlayers[mPlayer], row, column);
break;
}
}
std::cout << "\n\n";
}
int main()
{
Game game;
game.run();
}发布于 2015-07-14 07:54:04
这是个很酷的主意!我喜欢你可以改变棋盘的尺寸。听起来是个有趣的游戏。这是我的建议。
使用正确的工具
我不太明白TicTacToe作为模板而不是普通类的意义。如果它只是一个类,则可以将板的维度传递给构造函数。拥有一个模板,其中唯一的变化是一些内部存储的大小似乎并不是最好的模板使用对我来说。
applyMove()方法在失败时返回true,如果成功返回false。这是违反直觉的。我可不这么想。
我很难理解您的isWinner()方法。这是一个非常奇怪的方式来测试获胜的条件。我认为,手动遍历每一行和每一列的简单实现将更容易理解和维护。至少,在你有一个评论会是很好的。(另外,您确定您已经在win数组中分配了足够的空间吗?在我看来,它需要的是3 * DIM,而不是2 * (DIM + 1)。看起来,您确实需要3个单独的数组,您只需将它们全部变成一个数组,并且使用数组的不同部分来表示行、列和对角线。
游戏
在Game类中,mPlayer看起来可能是一个static const std::array,因为它从未改变,并且对每个实例都是相同的。
在Game::run()中,这是:
mPlayer ^= 1;太聪明了。这是一种不明显的东西,所以最好避免。只有我们更简单:
mPlayer = (mPlayer + 1) % 2;在Game::turn()中,您有一个受折磨的for循环:
for (bool pending = true; pending;)它最好写成一个while循环:
bool pending = true;
while (pending)
// ... etc.https://codereview.stackexchange.com/questions/96613
复制相似问题