diff --git a/gamestate.cpp b/gamestate.cpp index e6a5847..df8d25d 100644 --- a/gamestate.cpp +++ b/gamestate.cpp @@ -146,6 +146,60 @@ bool GameState::moveColumnCardToFoundation(int columnId, PlayingCard::Suit found } +bool GameState::autoMoveThrownCard() +{ + // NOTE: This method is very similar to moveThrownCardToFoundation, + // consider reducing the repetitino here somehow. + + if (m_throwawayPile.isEmpty()) { + // Consider raising an exception here instead + qWarning() << "Attempted to move thrown card to foundation with empty throwaway pile"; + return false; + } + + // We'll be moving the last card in the foundation pile (maybe) + PlayingCard *cardToMove = m_throwawayPile.last(); + + // Try moving the card into the foundation + if (!tryAutoMoveCard(cardToMove)) + return false; + + // We succeeded, the card is now in the appropriate foundation pile, + // let's remove the card from the throwaway pile. + m_throwawayPile.removeLast(); + + emit throwawayPileChanged(); + return true; +} + +bool GameState::autoMoveColumnCard(int columnId) +{ + // NOTE: This method is very similar to moveColumnCardToFoundation, + // consider reducing the repetitino here somehow. + + assert(columnId >= 0 && columnId < 7); + + if (m_columns[columnId].isEmpty()) { + // Consider raising an exception here instead + qWarning() << "Attempted to move card to foundation from an empty column"; + return false; + } + + // We'll be moving the top card in the column (maybe) + PlayingCard *cardToMove = m_columns[columnId].first()->card(); + + if (!tryAutoMoveCard(cardToMove)) + return false; + + // We succeeded, the card is now in the appropriate foundation pile, + // let's remove the column slot. + m_columns[columnId].removeFirst(); + ensureColumnRevealed(columnId); + + emit columnsChanged(); + return true; +} + void GameState::onFoundationChanged() { // Check if the game is won (can only happen on a foundation pile change) @@ -199,6 +253,24 @@ bool GameState::tryMoveCardToFoundation(PlayingCard::Suit foundationId, PlayingC return true; } +bool GameState::tryAutoMoveCard(PlayingCard *cardToMove) +{ + // 1. Try moving the card to the foundation + for (PlayingCard::Suit suit : {PlayingCard::Suit::Clubs, PlayingCard::Suit::Diamonds, PlayingCard::Suit::Hearts, PlayingCard::Suit::Spades}) + if (cardToMove->suit() == suit && tryMoveCardToFoundation(suit, cardToMove)) + return true; + + // 2. Try moving the card to another column + for (int columnId = 0; columnId < m_columns.size(); ++columnId) + if (isMoveToColumnLegal(cardToMove, columnId)) { + moveCardToColumn(columnId); + return true; + } + + // No available auto-move + return false; +} + bool GameState::isMoveToColumnLegal(PlayingCard *cardToMove, int columnId) { assert(columnId >= 0 && columnId < 7); diff --git a/gamestate.h b/gamestate.h index 54cd9f4..689d955 100644 --- a/gamestate.h +++ b/gamestate.h @@ -17,18 +17,26 @@ class GameState : public QObject public: explicit GameState(QObject *parent = nullptr); + // Getters QList drawPile() const; QList throwawayPile() const; QList> columns() const; QList> foundation() const; bool gameWon() const; + // General functions void dealCards(); void drawNextCard(); + + // Manual moves (from X to Y) bool moveCardToColumn(int columnId); bool moveThrownCardToFoundation(PlayingCard::Suit foundationId); bool moveColumnCardToFoundation(int columnId, PlayingCard::Suit foundationId); + // Automatic moves (from X to auto) + bool autoMoveThrownCard(); + bool autoMoveColumnCard(int columnId); + signals: void drawPileChanged(); void throwawayPileChanged(); @@ -47,6 +55,7 @@ private: bool m_gameWon; bool tryMoveCardToFoundation(PlayingCard::Suit foundationId, PlayingCard* cardToMove); + bool tryAutoMoveCard(PlayingCard* cardToMove); bool isMoveToColumnLegal(PlayingCard* cardToMove, int columnId); void ensureColumnRevealed(int columnId); };