Rework various gamestate methods
This commit is contained in:
parent
4ea9a04836
commit
4dbcd700c0
297
gamestate.cpp
297
gamestate.cpp
|
@ -5,15 +5,8 @@
|
|||
GameState::GameState(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
// Initialize the foundation piles (4 suits)
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
m_foundation.append(QList<PlayingCard*>());
|
||||
}
|
||||
|
||||
// Initialize the columns (7 piles)
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
m_columns.append(QList<ColumnSlot*>());
|
||||
}
|
||||
m_foundation.resize(4);
|
||||
m_columns.resize(7);
|
||||
|
||||
dealCards();
|
||||
}
|
||||
|
@ -46,6 +39,7 @@ void GameState::dealCards()
|
|||
}
|
||||
|
||||
// Use the remaining cards as the draw pile
|
||||
assert(index == 28);
|
||||
m_drawPile = deck.mid(index);
|
||||
|
||||
// Reset the foundation & throwaway pile
|
||||
|
@ -86,9 +80,10 @@ void GameState::drawNextCard()
|
|||
emit throwawayPileChanged();
|
||||
}
|
||||
|
||||
bool GameState::moveCardToColumn(int columnId)
|
||||
bool GameState::moveThrownCardToColumn(int columnId)
|
||||
{
|
||||
assert(columnId >= 0 && columnId < 7);
|
||||
auto columnStack = m_columns[columnId];
|
||||
|
||||
if (m_throwawayPile.isEmpty()) {
|
||||
qWarning() << "Attempted to move thrown card to column with empty throwaway pile";
|
||||
|
@ -96,18 +91,18 @@ bool GameState::moveCardToColumn(int columnId)
|
|||
}
|
||||
|
||||
// We'll be moving the last card in the throwaway pile (maybe)
|
||||
PlayingCard *cardToMove = m_throwawayPile.last();
|
||||
PlayingCard* cardToMove = m_throwawayPile.last();
|
||||
qDebug() << "Attempting to move thrown card " << cardToMove->toString() << " to column " << columnId;
|
||||
|
||||
if (!isMoveToColumnLegal(cardToMove, columnId)) {
|
||||
if (!isColumnMoveValid(*cardToMove, columnId)) {
|
||||
qDebug() << "> Moving aborted, illegal move";
|
||||
return false;
|
||||
}
|
||||
|
||||
ColumnSlot *col = new ColumnSlot(cardToMove, true);
|
||||
m_columns[columnId].append(col);
|
||||
// Success, perform the move
|
||||
ColumnSlot* col = new ColumnSlot(cardToMove, true);
|
||||
columnStack.append(col);
|
||||
m_throwawayPile.removeLast();
|
||||
ensureColumnRevealed(columnId);
|
||||
qDebug() << "> Moving complete";
|
||||
|
||||
emit throwawayPileChanged();
|
||||
|
@ -115,35 +110,79 @@ bool GameState::moveCardToColumn(int columnId)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GameState::moveThrownCardToFoundation(PlayingCard::Suit foundationId)
|
||||
bool GameState::moveThrownCardToFoundation(int foundationId)
|
||||
{
|
||||
assert(foundationId >= 0 && foundationId < 4);
|
||||
auto foundationStack = m_foundation[foundationId];
|
||||
|
||||
if (m_throwawayPile.isEmpty()) {
|
||||
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();
|
||||
PlayingCard* cardToMove = m_throwawayPile.last();
|
||||
qDebug() << "Attempting to move thrown card " << cardToMove->toString() << " to foundation " << foundationId;
|
||||
|
||||
// Try moving the card into the foundation
|
||||
if (!tryMoveCardToFoundation(foundationId, cardToMove)) {
|
||||
if (!isFoundationMoveValid(*cardToMove, foundationId)) {
|
||||
qDebug() << "> Moving aborted, illegal move";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We succeeded, the card is now in the appropriate foundation pile,
|
||||
// let's remove the card from the throwaway pile.
|
||||
// Succeess, perform the move
|
||||
foundationStack.prepend(cardToMove);
|
||||
m_throwawayPile.removeLast();
|
||||
qDebug() << "> Moving complete";
|
||||
|
||||
emit throwawayPileChanged();
|
||||
emit foundationChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameState::moveColumnCardToFoundation(int columnId, PlayingCard::Suit foundationId)
|
||||
bool GameState::moveColumnCardToColumn(int fromColumnId, int toColumnId, int fromCardIndex)
|
||||
{
|
||||
assert(fromColumnId >= 0 && fromColumnId < 7);
|
||||
assert(toColumnId >= 0 && toColumnId < 7);
|
||||
auto fromColumnStack = m_columns[fromColumnId];
|
||||
auto toColumnStack = m_columns[toColumnId];
|
||||
|
||||
if (fromColumnStack.isEmpty()) {
|
||||
qWarning() << "Attempted to move card(s) to column from an empty column";
|
||||
return false;
|
||||
}
|
||||
|
||||
ColumnSlot* col = fromColumnStack[fromCardIndex];
|
||||
if (!col->isRevealed()) {
|
||||
qWarning() << "Attempted to card(s) to column from unrevealed column slot";
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayingCard* cardToMove = col->card();
|
||||
qDebug() << "Attempting to move card " << cardToMove->toString() << " from column " << fromColumnId << " to column " << toColumnId;
|
||||
|
||||
// Try moving the card
|
||||
if (!isColumnMoveValid(*cardToMove, toColumnId)) {
|
||||
qDebug() << "> Moving aborted, illegal move";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Success, move the card
|
||||
toColumnStack.append(col);
|
||||
fromColumnStack.removeLast();
|
||||
ensureColumnRevealed(fromColumnId);
|
||||
qDebug() << "> Moving complete";
|
||||
|
||||
emit columnsChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameState::moveColumnCardToFoundation(int columnId, int foundationId)
|
||||
{
|
||||
assert(columnId >= 0 && columnId < 7);
|
||||
assert(foundationId >= 0 && foundationId < 4);
|
||||
auto columnStack = m_columns[columnId];
|
||||
auto foundationStack = m_foundation[foundationId];
|
||||
|
||||
if (m_columns[columnId].isEmpty()) {
|
||||
qWarning() << "Attempted to move card to foundation from an empty column";
|
||||
|
@ -151,84 +190,123 @@ bool GameState::moveColumnCardToFoundation(int columnId, PlayingCard::Suit found
|
|||
}
|
||||
|
||||
// We'll be moving the last card in the column (maybe)
|
||||
PlayingCard *cardToMove = m_columns[columnId].last()->card();
|
||||
ColumnSlot* col = columnStack.last();
|
||||
PlayingCard* cardToMove = col->card();
|
||||
qDebug() << "Attempting to move card " << cardToMove->toString() << " from column " << columnId << " to foundation " << foundationId;
|
||||
|
||||
// Try moving the card into the foundation
|
||||
if (!tryMoveCardToFoundation(foundationId, cardToMove)) {
|
||||
if (!isFoundationMoveValid(*cardToMove, foundationId)) {
|
||||
qDebug() << "> Moving aborted, illegal move";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We succeeded, the card is now in the appropriate foundation pile,
|
||||
// let's remove the column slot.
|
||||
m_columns[columnId].removeLast();
|
||||
// Success, move the card
|
||||
foundationStack.prepend(cardToMove);
|
||||
columnStack.removeLast();
|
||||
col->deleteLater();
|
||||
ensureColumnRevealed(columnId);
|
||||
qDebug() << "> Moving complete";
|
||||
|
||||
emit columnsChanged();
|
||||
emit columnsChanged(); // CRASH (not if I remove the delete col line though)
|
||||
emit foundationChanged();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
PlayingCard* cardToMove = m_throwawayPile.last();
|
||||
qDebug() << "Attempting auto-move of thrown card " << cardToMove->toString();
|
||||
|
||||
// Try moving the card into the foundation
|
||||
if (!tryAutoMoveCard(cardToMove)) {
|
||||
if (!tryAutoMoveSingleCard(*cardToMove)) {
|
||||
qDebug() << "> Moving failed, no available move found";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We succeeded, the card is now in the appropriate foundation pile,
|
||||
// let's remove the card from the throwaway pile.
|
||||
// We succeeded, the card was moved, remove it from throwaway pile
|
||||
m_throwawayPile.removeLast();
|
||||
qDebug() << "> Moving complete";
|
||||
|
||||
emit throwawayPileChanged();
|
||||
|
||||
|
||||
// We don't know which pile the card was moved to, to be safe, emit
|
||||
// a change from both
|
||||
// NOTE: consider returning what changed from tryAutoMoveSingleCard
|
||||
emit columnsChanged();
|
||||
emit foundationChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameState::autoMoveColumnCard(int columnId)
|
||||
bool GameState::autoMoveColumnCard(int columnId, int cardIndex)
|
||||
{
|
||||
// NOTE: This method is very similar to moveColumnCardToFoundation,
|
||||
// consider reducing the repetitino here somehow.
|
||||
|
||||
assert(columnId >= 0 && columnId < 7);
|
||||
auto columnStack = m_columns[columnId];
|
||||
|
||||
if (m_columns[columnId].isEmpty()) {
|
||||
// Consider raising an exception here instead
|
||||
qWarning() << "Attempted to move card to foundation from an empty column";
|
||||
if (columnStack.isEmpty()) {
|
||||
qWarning() << "Attempted to move card(s) to foundation from an empty column";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We'll be moving the last card in the column (maybe)
|
||||
PlayingCard *cardToMove = m_columns[columnId].last()->card();
|
||||
qDebug() << "Attempting auto-move of column card " << cardToMove->toString();
|
||||
ColumnSlot* col = columnStack[cardIndex];
|
||||
if (!col->isRevealed()) {
|
||||
qWarning() << "Attempted to card(s) to column from unrevealed column slot";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tryAutoMoveCard(cardToMove)) {
|
||||
if (cardIndex == columnStack.size() - 1) {
|
||||
// This is a single card move (last card)
|
||||
PlayingCard* cardToMove = col->card();
|
||||
qDebug() << "Attempting auto-move of column " << columnId << " card " << cardToMove->toString();
|
||||
if (!tryAutoMoveSingleCard(*cardToMove, columnId)) {
|
||||
qDebug() << "> Moving failed, no available move found";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We succeeded, the card is now in the appropriate foundation pile,
|
||||
// let's remove the column slot.
|
||||
m_columns[columnId].removeLast();
|
||||
// We succeeded, the card was moved, remove it from the original column
|
||||
columnStack.removeLast();
|
||||
col->deleteLater();
|
||||
ensureColumnRevealed(columnId);
|
||||
qDebug() << "> Moving complete";
|
||||
|
||||
emit columnsChanged();
|
||||
|
||||
// we don't know where the card was moved, it could've been the foundation too
|
||||
// to be safe, emit a change signal for it too
|
||||
// NOTE: consider returning what changed from tryAutoMoveSingleCard
|
||||
emit foundationChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is a multiple cards move
|
||||
qDebug() << "Attempting auto-move of column " << columnId << " card range " << cardIndex << " to " << columnStack.size() - 1;
|
||||
QList<PlayingCard*> selectedCards;
|
||||
for (int i = cardIndex; i < m_columns.size(); ++i) {
|
||||
ColumnSlot* col = columnStack[i];
|
||||
selectedCards.append(col->card());
|
||||
}
|
||||
|
||||
if (!tryAutoMoveMultipleCards(selectedCards, cardIndex)) {
|
||||
qDebug() << "> Moving failed, no available move found";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We succeeded, the cards were moved,
|
||||
// now remove the moved cards from the column
|
||||
while (columnStack.size() > cardIndex) {
|
||||
ColumnSlot* curSlot = columnStack.takeLast();
|
||||
curSlot->deleteLater();
|
||||
}
|
||||
|
||||
emit columnsChanged();
|
||||
return true;
|
||||
}
|
||||
|
@ -255,26 +333,82 @@ void GameState::onFoundationChanged()
|
|||
emit gameWonChanged();
|
||||
}
|
||||
|
||||
bool GameState::tryMoveCardToFoundation(PlayingCard::Suit foundationId, PlayingCard* cardToMove)
|
||||
bool GameState::tryAutoMoveSingleCard(PlayingCard &cardToMove, int skipColumnId)
|
||||
{
|
||||
assert(foundationId >= PlayingCard::Suit::Clubs && foundationId <= PlayingCard::Suit::Spades);
|
||||
// 1. Try moving the card to the foundation
|
||||
const int foundationId = static_cast<int>(cardToMove.suit());
|
||||
if (isFoundationMoveValid(cardToMove, foundationId)) {
|
||||
m_foundation[foundationId].prepend(&cardToMove);
|
||||
qDebug() << "* Auto-moved card " << cardToMove.toString() << " to foundation " << foundationId;
|
||||
return true;
|
||||
}
|
||||
|
||||
qDebug() << "* Trying to move card " << cardToMove->toString() << " to foundation " << foundationId;
|
||||
// 2. Try moving the card to another column
|
||||
for (int columnId = 0; columnId < m_columns.size(); ++columnId) {
|
||||
if (columnId == skipColumnId)
|
||||
continue;
|
||||
|
||||
if (cardToMove->suit() != foundationId) {
|
||||
qDebug() << "* Move attempt failed (wrong suit)";
|
||||
if (isColumnMoveValid(cardToMove, columnId)) {
|
||||
ColumnSlot* col = new ColumnSlot(&cardToMove, true);
|
||||
m_columns[columnId].append(col);
|
||||
qDebug() << "* Auto-moved card " << cardToMove.toString() << " to column " << columnId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No available auto-move
|
||||
qDebug() << "* Auto-move failed, no available moves";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameState::tryAutoMoveMultipleCards(const QList<PlayingCard*>& cards, int skipColumnId)
|
||||
{
|
||||
assert(cards.size() > 1);
|
||||
|
||||
// If we can move the first (selected) card to another column,
|
||||
// we can also move the rest of the cards below to that column,
|
||||
// so we only need to care about the first card.
|
||||
// (Foundation moves are impossible with multiple card movements).
|
||||
|
||||
PlayingCard* firstCard = cards.first();
|
||||
|
||||
for (int columnId = 0; columnId < m_columns.size(); ++columnId) {
|
||||
if (columnId == skipColumnId)
|
||||
continue;
|
||||
|
||||
if (isColumnMoveValid(*firstCard, columnId)) {
|
||||
for (auto card : cards) {
|
||||
ColumnSlot* col = new ColumnSlot(card, true);
|
||||
m_columns[columnId].append(col);
|
||||
qDebug() << "* Auto-moved card " << card->toString() << " to column " << columnId;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "* Auto-move failed, no available moves";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameState::isFoundationMoveValid(const PlayingCard& cardToMove, int foundationId)
|
||||
{
|
||||
assert(foundationId >= 0 && foundationId < 4);
|
||||
const auto foundationSuit = static_cast<PlayingCard::Suit>(foundationId);
|
||||
const auto foundationStack = m_foundation[foundationId];
|
||||
|
||||
// The card must match the suit of the foundation
|
||||
if (cardToMove.suit() != foundationSuit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayingCard::Value requiredValue;
|
||||
if (m_foundation[foundationId].isEmpty()) {
|
||||
if (foundationStack.isEmpty()) {
|
||||
// If the pile is empty, only an ace can go in
|
||||
requiredValue = PlayingCard::Value::Ace;
|
||||
} else {
|
||||
// Otherwise it's the next card by value, unless we're already at king
|
||||
PlayingCard::Value curValue = m_foundation[foundationId].first()->value();
|
||||
PlayingCard::Value curValue = foundationStack.first()->value();
|
||||
if (curValue == PlayingCard::Value::King) {
|
||||
qDebug() << "* Move attempt failed (expected King)";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -283,59 +417,32 @@ bool GameState::tryMoveCardToFoundation(PlayingCard::Suit foundationId, PlayingC
|
|||
requiredValue = static_cast<PlayingCard::Value>(static_cast<int>(curValue) + 1);
|
||||
}
|
||||
|
||||
if (cardToMove->value() != requiredValue) {
|
||||
qDebug() << "* Move attempt failed (expected value: " << requiredValue << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_foundation[foundationId].push_front(cardToMove);
|
||||
qDebug() << "* Moved card " << cardToMove->toString() << " to foundation " << foundationId;
|
||||
|
||||
emit foundationChanged();
|
||||
return true;
|
||||
return cardToMove.value() == requiredValue;
|
||||
}
|
||||
|
||||
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)) {
|
||||
qDebug() << "* Auto-moved card " << cardToMove->toString() << " to foundation " << suit;
|
||||
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);
|
||||
qDebug() << "* Auto-moved card " << cardToMove->toString() << " to column " << columnId;
|
||||
return true;
|
||||
}
|
||||
|
||||
// No available auto-move
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameState::isMoveToColumnLegal(PlayingCard *cardToMove, int columnId)
|
||||
{
|
||||
bool GameState::isColumnMoveValid(const PlayingCard& cardToMove, int columnId) {
|
||||
assert(columnId >= 0 && columnId < 7);
|
||||
const auto columnStack = m_columns[columnId];
|
||||
|
||||
if (m_columns[columnId].isEmpty()) {
|
||||
if (columnStack.isEmpty()) {
|
||||
// Column is empty: only a King can be placed in an empty column
|
||||
return cardToMove->value() == PlayingCard::Value::King;
|
||||
return cardToMove.value() == PlayingCard::Value::King;
|
||||
}
|
||||
|
||||
// Compare against the last card in the column
|
||||
PlayingCard* columnCard = m_columns[columnId].last()->card();
|
||||
const PlayingCard& columnCard = *columnStack.last()->card();
|
||||
|
||||
// The card's value must be one less than the card in the column
|
||||
if (cardToMove->value() != columnCard->value() - 1)
|
||||
if (cardToMove.value() != columnCard.value() - 1) {
|
||||
qDebug() << "* Move attempt failed (wrong value)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// The card must be of opposite color
|
||||
return PlayingCard::areOppositeColors(*cardToMove, *columnCard);
|
||||
return PlayingCard::areOppositeColors(cardToMove, columnCard);
|
||||
}
|
||||
|
||||
|
||||
void GameState::ensureColumnRevealed(int columnId)
|
||||
{
|
||||
assert(columnId >= 0 && columnId < 7);
|
||||
|
|
24
gamestate.h
24
gamestate.h
|
@ -28,17 +28,18 @@ public:
|
|||
bool gameWon() const;
|
||||
|
||||
// General functions
|
||||
void dealCards();
|
||||
void drawNextCard();
|
||||
Q_INVOKABLE void dealCards();
|
||||
Q_INVOKABLE void drawNextCard();
|
||||
|
||||
// Manual moves (from X to Y)
|
||||
bool moveCardToColumn(int columnId);
|
||||
bool moveThrownCardToFoundation(PlayingCard::Suit foundationId);
|
||||
bool moveColumnCardToFoundation(int columnId, PlayingCard::Suit foundationId);
|
||||
Q_INVOKABLE bool moveThrownCardToColumn(int columnId);
|
||||
Q_INVOKABLE bool moveThrownCardToFoundation(int foundationId);
|
||||
Q_INVOKABLE bool moveColumnCardToColumn(int fromColumnId, int toColumnId, int fromCardIndex);
|
||||
Q_INVOKABLE bool moveColumnCardToFoundation(int columnId, int foundationId);
|
||||
|
||||
// Automatic moves (from X to auto)
|
||||
bool autoMoveThrownCard();
|
||||
bool autoMoveColumnCard(int columnId);
|
||||
Q_INVOKABLE bool autoMoveThrownCard();
|
||||
Q_INVOKABLE bool autoMoveColumnCard(int columnId, int cardIndex);
|
||||
|
||||
signals:
|
||||
void drawPileChanged();
|
||||
|
@ -57,9 +58,12 @@ private:
|
|||
QList<QList<PlayingCard*>> m_foundation;
|
||||
bool m_gameWon;
|
||||
|
||||
bool tryMoveCardToFoundation(PlayingCard::Suit foundationId, PlayingCard* cardToMove);
|
||||
bool tryAutoMoveCard(PlayingCard* cardToMove);
|
||||
bool isMoveToColumnLegal(PlayingCard* cardToMove, int columnId);
|
||||
bool tryAutoMoveSingleCard(PlayingCard& cardToMove, int skipColumnId = -1);
|
||||
bool tryAutoMoveMultipleCards(const QList<PlayingCard*>& cards, int skipColumnId);
|
||||
|
||||
bool isFoundationMoveValid(const PlayingCard& cardToMove, int foundationId);
|
||||
bool isColumnMoveValid(const PlayingCard& cardToMove, int columnId);
|
||||
|
||||
void ensureColumnRevealed(int columnId);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue