首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >AL *N Tic Tac Tac Toe游戏-2

AL *N Tic Tac Tac Toe游戏-2
EN

Code Review用户
提问于 2015-07-11 20:12:29
回答 1查看 556关注 0票数 8

根据上一个问题:

AL*Tic Tac脚趾游戏

以下是改进的总结:

  • 删除Match类的昂贵调用构造函数
  • 删除不再需要的enum struct Typeenum struct Diagonals
  • Random类转换为模板,以允许intunsigned值作为参数

我怎样才能改进这个游戏?

代码语言:javascript
复制
#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();
}
EN

回答 1

Code Review用户

回答已采纳

发布于 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()中,这是:

代码语言:javascript
复制
mPlayer ^= 1;

太聪明了。这是一种不明显的东西,所以最好避免。只有我们更简单:

代码语言:javascript
复制
mPlayer = (mPlayer + 1) % 2;

Game::turn()中,您有一个受折磨的for循环:

代码语言:javascript
复制
for (bool pending = true; pending;)

它最好写成一个while循环:

代码语言:javascript
复制
bool pending = true;
while (pending)
// ... etc.
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/96613

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档