/** * @file GameState.h * * @author Yuzhe Guo * @date 2025.11.16 */ #ifndef UNO_GAME_GAMESTATE_H #define UNO_GAME_GAMESTATE_H #include "CardTile.h" #include "Player.h" #include #include namespace UNO::GAME { /** * 玩家状态 */ class PlayerState { protected: std::string name_; size_t remainingCardCount_; bool isUno_; PlayerState(std::string name, size_t remainingCardCount, bool isUno); public: virtual ~PlayerState() = default; /** * @return 玩家名字 */ [[nodiscard]] std::string getName() const; /** * @return 玩家是否处于 UNO 状态 */ [[nodiscard]] bool getIsUno() const; /** * @return 玩家手中剩余的牌的数量 */ [[nodiscard]] size_t getRemainingCardCount() const; /** * 设置 Uno 状态 * @param x Uno 状态 */ void setIsUno(bool x); /** * 摸牌 * @param n 摸的张数 * @param cards 摸到的牌 */ void virtual draw(size_t n, const std::vector &cards); /** * 出一张牌 */ Card virtual play(const Card &card); }; /** * (供客户端使用)玩家状态 */ class ClientPlayerState final : public PlayerState { public: ClientPlayerState(std::string name, size_t remainingCardCount, bool isUno); /** * 摸牌 * @param n 摸的张数 * @param cards 摸的牌 */ void draw(size_t n, const std::vector &cards) override; /** * 出一张牌 */ Card play(const Card &card) override; }; /** * (供服务端使用)玩家状态 */ class ServerPlayerState final : public PlayerState { private: HandCard handCard_; public: explicit ServerPlayerState(std::string name, size_t remainingCardCount, bool isUno); /** * 获得当前手牌 * @return 当前手牌的集合 */ [[nodiscard]] const std::multiset &getCards() const; /** * 摸牌 * @param x 摸的张数 * @param cards 摸的牌 */ void draw(size_t x, const std::vector &cards) override; /** * 打出一张牌 * @param card 要打出的手牌 * @return 打出的手牌 */ Card play(const Card &card) override; /** * @return 手牌是否为空 */ [[nodiscard]] bool isEmpty() const; }; /** * 游戏状态 */ enum class GameStatus : uint8_t { WAITING_PLAYERS_TO_JOIN, WAITING_PLAYERS_TO_START, WAITING_PLAYERS_TO_NEXT_TURN, WAITING_PLAYERS_TO_NEXT_ROUND }; /** * 保证模板使用的是 PlayerState 的派生类 */ template concept PlayerStateTypeConcept = std::is_base_of_v; /** * 游戏具体状态 * @tparam PlayerStateType PlayerState 的派生类 */ template class GameState { protected: explicit GameState(GameStatus gameStatus); GameStatus gameStatus_; DiscardPile discardPile_; bool isReversed_; size_t drawCount_; std::vector players_; std::vector::iterator currentPlayer_; /** * 反转 */ void reverse(); /** * 下一个玩家 */ void nextPlayer(); public: virtual ~GameState() = default; /** * @return 所有玩家状态 */ [[nodiscard]] const std::vector &getPlayers() const; /** * @return 当前玩家对应的 iterator */ [[nodiscard]] std::vector::iterator getCurrentPlayer() const; /** * @return 弃牌堆 */ [[nodiscard]] const DiscardPile &getDiscardPile() const; /** * @return 是否反转 */ [[nodiscard]] bool getIsReversed() const; [[nodiscard]] size_t getDrawCount() const; /** * 向对局中添加玩家 * @param playerState 要添加的玩家状态 */ void addPlayer(PlayerStateType playerState); /** * 清空玩家 */ void clearPlayers(); /** * 由于用户出牌而改变状态 * @param card 用户出的牌 */ void virtual updateStateByCard(const Card &card); /** * 由于用户摸牌而改变状态 */ void virtual updateStateByDraw(); }; template GameState::GameState(GameStatus gameStatus) : gameStatus_(gameStatus), isReversed_(false), drawCount_(0), players_(), currentPlayer_(players_.begin()) { } template const std::vector &GameState::getPlayers() const { return this->players_; } template std::vector::iterator GameState::getCurrentPlayer() const { return this->currentPlayer_; } template const DiscardPile &GameState::getDiscardPile() const { return this->discardPile_; } template bool GameState::getIsReversed() const { return this->isReversed_; } template size_t GameState::getDrawCount() const { return this->drawCount_; } template void GameState::addPlayer(PlayerStateType playerState) { int currentPlayerIndex = this->currentPlayer_ - this->players_.begin(); this->players_.push_back(std::move(playerState)); this->currentPlayer_ = this->players_.begin() + currentPlayerIndex; } template void GameState::nextPlayer() { if (this->isReversed_ == false) { this->currentPlayer_ = std::next(this->currentPlayer_); if (this->currentPlayer_ == this->players_.end()) { this->currentPlayer_ = this->players_.begin(); } } else { if (this->currentPlayer_ == this->players_.begin()) { this->currentPlayer_ = this->players_.end(); } this->currentPlayer_ = std::prev(this->currentPlayer_); } } template void GameState::clearPlayers() { this->players_.clear(); this->currentPlayer_ = this->players_.begin(); } template void GameState::reverse() { this->isReversed_ ^= 1; } template void GameState::updateStateByCard(const Card &card) { if (this->discardPile_.isEmpty() == false && card.canBePlayedOn(this->discardPile_.getFront(), this->drawCount_) == false) { throw std::invalid_argument("Card cannot be played"); } this->currentPlayer_->play(card); if (card.getType() == CardType::DRAW2) { this->drawCount_ += 2; } if (card.getType() == CardType::WILDDRAWFOUR) { this->drawCount_ += 4; } if (card.getType() == CardType::REVERSE) { this->reverse(); } if (card.getType() == CardType::SKIP) { this->nextPlayer(); } this->nextPlayer(); this->discardPile_.add(card); } template void GameState::updateStateByDraw() { if (this->drawCount_ == 0) { this->drawCount_ = 1; } this->currentPlayer_->draw(this->drawCount_, {}); this->drawCount_ = 0; this->nextPlayer(); } class ClientGameState final : public GameState { private: Player player_; public: ClientGameState(GameStatus gameStatus, std::string name); /** * 获得当前手牌 * @return 当前手牌的集合 */ [[nodiscard]] const std::multiset &getCards() const; /** * 摸一张牌 * @param card 摸的牌 */ void draw(const Card &card); /** * 摸多张牌 * @param cards 摸的牌 */ void draw(const std::vector &cards); /** * 打出一张牌 * @param it 要打出的手牌的迭代器 * @return 打出的手牌 */ Card play(const std::multiset::iterator &it); /** * @return 手牌是否为空 */ [[nodiscard]] bool isEmpty() const; }; class ServerGameState final : public GameState { private: Deck deck_; public: ServerGameState(); /** * 开始游戏 */ void init(); /** * 由于用户摸牌而改变状态 */ void updateStateByDraw() override; }; } // namespace UNO::GAME #endif // UNO_GAME_GAMESTATE_H