Compare commits
3 commits
6ecfc64a79
...
ef16bf9188
Author | SHA1 | Date | |
---|---|---|---|
ItsDrike | ef16bf9188 | ||
ItsDrike | a52681c0b3 | ||
ItsDrike | 1ecfd36598 |
11
qml/Main.qml
11
qml/Main.qml
|
@ -53,7 +53,7 @@ ApplicationWindow {
|
||||||
Timer {
|
Timer {
|
||||||
id: gameTimer
|
id: gameTimer
|
||||||
interval: 1000
|
interval: 1000
|
||||||
running: true
|
running: GameState.gameWon === false
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: scoreBar.time += 1
|
onTriggered: scoreBar.time += 1
|
||||||
}
|
}
|
||||||
|
@ -62,12 +62,15 @@ ApplicationWindow {
|
||||||
id: scoreBar
|
id: scoreBar
|
||||||
|
|
||||||
moves: GameState.moveAmount
|
moves: GameState.moveAmount
|
||||||
|
score: GameState.score
|
||||||
time: 0
|
time: 0
|
||||||
|
|
||||||
height: Math.max(parent.height * 0.08, 50)
|
height: Math.max(parent.height * 0.08, 50)
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
onRestart: time = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the foundation piles, throwaway pile & the draw stack on the first row
|
// Show the foundation piles, throwaway pile & the draw stack on the first row
|
||||||
|
@ -123,5 +126,9 @@ ApplicationWindow {
|
||||||
cardHeight: app.cardHeight
|
cardHeight: app.cardHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
WinOverlay {}
|
WinOverlay {
|
||||||
|
onRestart: {
|
||||||
|
scoreBar.time = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,15 @@ Rectangle {
|
||||||
property int time: 0
|
property int time: 0
|
||||||
property int moves: 0
|
property int moves: 0
|
||||||
|
|
||||||
|
signal restart
|
||||||
|
|
||||||
color: "lightgray"
|
color: "lightgray"
|
||||||
|
|
||||||
RoundButton {
|
RoundButton {
|
||||||
onClicked: GameState.dealCards()
|
onClicked: {
|
||||||
|
GameState.dealCards()
|
||||||
|
scoreBarRoot.restart()
|
||||||
|
}
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: parent.width * 0.02
|
anchors.leftMargin: parent.width * 0.02
|
||||||
|
|
|
@ -10,6 +10,8 @@ Rectangle {
|
||||||
visible: GameState.gameWon === true
|
visible: GameState.gameWon === true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
signal restart
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: winText
|
id: winText
|
||||||
|
|
||||||
|
@ -23,7 +25,10 @@ Rectangle {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: "Restart"
|
text: "Restart"
|
||||||
onClicked: GameState.dealCards()
|
onClicked: {
|
||||||
|
GameState.dealCards();
|
||||||
|
winOverlay.restart();
|
||||||
|
}
|
||||||
anchors.top: winText.bottom
|
anchors.top: winText.bottom
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ void GameState::dealCards() {
|
||||||
emit columnsChanged();
|
emit columnsChanged();
|
||||||
emit foundationChanged();
|
emit foundationChanged();
|
||||||
emit moveAmountChanged();
|
emit moveAmountChanged();
|
||||||
|
emit scoreChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameState::setupWinningDeck() {
|
void GameState::setupWinningDeck() {
|
||||||
|
@ -102,6 +103,7 @@ void GameState::setupWinningDeck() {
|
||||||
emit columnsChanged();
|
emit columnsChanged();
|
||||||
emit foundationChanged();
|
emit foundationChanged();
|
||||||
emit moveAmountChanged();
|
emit moveAmountChanged();
|
||||||
|
emit scoreChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameState::drawNextCard() {
|
bool GameState::drawNextCard() {
|
||||||
|
@ -153,6 +155,7 @@ bool GameState::moveThrownCardToColumn(int columnId) {
|
||||||
qDebug() << "> Moving complete";
|
qDebug() << "> Moving complete";
|
||||||
|
|
||||||
incrementMoveAmt();
|
incrementMoveAmt();
|
||||||
|
increaseScore(5); // Score increase for moving to column
|
||||||
emit throwawayPileChanged();
|
emit throwawayPileChanged();
|
||||||
emit columnsChanged();
|
emit columnsChanged();
|
||||||
return true;
|
return true;
|
||||||
|
@ -183,6 +186,7 @@ bool GameState::moveThrownCardToFoundation(int foundationId) {
|
||||||
qDebug() << "> Moving complete";
|
qDebug() << "> Moving complete";
|
||||||
|
|
||||||
incrementMoveAmt();
|
incrementMoveAmt();
|
||||||
|
increaseScore(10); // Score increase for moving to foundation
|
||||||
emit throwawayPileChanged();
|
emit throwawayPileChanged();
|
||||||
emit foundationChanged();
|
emit foundationChanged();
|
||||||
return true;
|
return true;
|
||||||
|
@ -263,6 +267,7 @@ bool GameState::moveColumnCardToFoundation(int columnId, int foundationId) {
|
||||||
ensureColumnRevealed(columnId);
|
ensureColumnRevealed(columnId);
|
||||||
qDebug() << "> Moving complete";
|
qDebug() << "> Moving complete";
|
||||||
|
|
||||||
|
increaseScore(10); // Score increase for moving to foundation
|
||||||
incrementMoveAmt();
|
incrementMoveAmt();
|
||||||
emit columnsChanged();
|
emit columnsChanged();
|
||||||
emit foundationChanged();
|
emit foundationChanged();
|
||||||
|
@ -280,7 +285,8 @@ bool GameState::autoMoveThrownCard() {
|
||||||
qDebug() << "Attempting auto-move of thrown card " << cardToMove->toString();
|
qDebug() << "Attempting auto-move of thrown card " << cardToMove->toString();
|
||||||
|
|
||||||
// Try moving the card into the foundation
|
// Try moving the card into the foundation
|
||||||
if (!tryAutoMoveSingleCard(*cardToMove)) {
|
auto changed = tryAutoMoveSingleCard(*cardToMove);
|
||||||
|
if (!changed.has_value()) {
|
||||||
qDebug() << "> Moving failed, no available move found";
|
qDebug() << "> Moving failed, no available move found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -291,11 +297,17 @@ bool GameState::autoMoveThrownCard() {
|
||||||
|
|
||||||
emit throwawayPileChanged();
|
emit throwawayPileChanged();
|
||||||
|
|
||||||
// We don't know which pile the card was moved to, to be safe, emit
|
switch (changed.value().destinationType) {
|
||||||
// a change from both
|
case AutoMoveResult::DestinationType::Foundation:
|
||||||
// NOTE: consider returning what changed from tryAutoMoveSingleCard
|
increaseScore(10); // Score increase for moving to foundation
|
||||||
emit columnsChanged();
|
|
||||||
emit foundationChanged();
|
emit foundationChanged();
|
||||||
|
break;
|
||||||
|
case AutoMoveResult::DestinationType::Column:
|
||||||
|
increaseScore(5); // Score increase for moving to column
|
||||||
|
emit columnsChanged();
|
||||||
|
break;
|
||||||
|
default: assert(false); break;
|
||||||
|
}
|
||||||
incrementMoveAmt();
|
incrementMoveAmt();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -320,7 +332,8 @@ bool GameState::autoMoveColumnCard(int columnId, int cardIndex) {
|
||||||
// This is a single card move (last card)
|
// This is a single card move (last card)
|
||||||
PlayingCard* cardToMove = col->card();
|
PlayingCard* cardToMove = col->card();
|
||||||
qDebug() << "Attempting auto-move of column " << columnId << " card " << cardToMove->toString();
|
qDebug() << "Attempting auto-move of column " << columnId << " card " << cardToMove->toString();
|
||||||
if (!tryAutoMoveSingleCard(*cardToMove, columnId)) {
|
auto changed = tryAutoMoveSingleCard(*cardToMove, columnId);
|
||||||
|
if (!changed.has_value()) {
|
||||||
qDebug() << "> Moving failed, no available move found";
|
qDebug() << "> Moving failed, no available move found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -331,12 +344,14 @@ bool GameState::autoMoveColumnCard(int columnId, int cardIndex) {
|
||||||
ensureColumnRevealed(columnId);
|
ensureColumnRevealed(columnId);
|
||||||
qDebug() << "> Moving complete";
|
qDebug() << "> Moving complete";
|
||||||
|
|
||||||
|
// Columns always change, we're moving from them, if the move is to another column
|
||||||
|
// that's all, if it's to a foundation, also emit foundation change
|
||||||
emit columnsChanged();
|
emit columnsChanged();
|
||||||
|
if (changed.value().destinationType == AutoMoveResult::DestinationType::Foundation) {
|
||||||
// we don't know where the card was moved, it could've been the foundation too
|
increaseScore(10); // Score increase for moving to foundation
|
||||||
// to be safe, emit a change signal for it too
|
|
||||||
// NOTE: consider returning what changed from tryAutoMoveSingleCard
|
|
||||||
emit foundationChanged();
|
emit foundationChanged();
|
||||||
|
}
|
||||||
|
|
||||||
incrementMoveAmt();
|
incrementMoveAmt();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -350,7 +365,7 @@ bool GameState::autoMoveColumnCard(int columnId, int cardIndex) {
|
||||||
selectedCards.append(curCol->card());
|
selectedCards.append(curCol->card());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tryAutoMoveMultipleCards(selectedCards, columnId)) {
|
if (!tryAutoMoveMultipleCards(selectedCards, columnId).has_value()) {
|
||||||
qDebug() << "> Moving failed, no available move found";
|
qDebug() << "> Moving failed, no available move found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -415,6 +430,7 @@ GameState* GameState::clone(bool enableWinnabilitySim) const {
|
||||||
|
|
||||||
newGameState->m_gameWon = m_gameWon;
|
newGameState->m_gameWon = m_gameWon;
|
||||||
newGameState->m_prelimWin = m_prelimWin;
|
newGameState->m_prelimWin = m_prelimWin;
|
||||||
|
newGameState->m_score = m_score;
|
||||||
|
|
||||||
assert(this->generateStateHash() == newGameState->generateStateHash());
|
assert(this->generateStateHash() == newGameState->generateStateHash());
|
||||||
return newGameState;
|
return newGameState;
|
||||||
|
@ -452,6 +468,7 @@ void GameState::cleanupBoard(bool emitChanges) {
|
||||||
}
|
}
|
||||||
|
|
||||||
m_moveAmt = 0;
|
m_moveAmt = 0;
|
||||||
|
m_score = 0;
|
||||||
|
|
||||||
// Note that we don't need to reset gameWon/prelimWin from here, as it's
|
// Note that we don't need to reset gameWon/prelimWin from here, as it's
|
||||||
// auto-checked from onFoundationChanged, which the emits trigger. Similarly
|
// auto-checked from onFoundationChanged, which the emits trigger. Similarly
|
||||||
|
@ -512,13 +529,13 @@ QString GameState::generateStateHash() const {
|
||||||
return stateHash;
|
return stateHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameState::tryAutoMoveSingleCard(PlayingCard& cardToMove, int skipColumnId) {
|
std::optional<GameState::AutoMoveResult> GameState::tryAutoMoveSingleCard(PlayingCard& cardToMove, int skipColumnId) {
|
||||||
// 1. Try moving the card to the foundation
|
// 1. Try moving the card to the foundation
|
||||||
const int foundationId = static_cast<int>(cardToMove.suit());
|
const int foundationId = static_cast<int>(cardToMove.suit());
|
||||||
if (isFoundationMoveValid(cardToMove, foundationId)) {
|
if (isFoundationMoveValid(cardToMove, foundationId)) {
|
||||||
m_foundation[foundationId].prepend(&cardToMove);
|
m_foundation[foundationId].prepend(&cardToMove);
|
||||||
qDebug() << "* Auto-moved card " << cardToMove.toString() << " to foundation " << foundationId;
|
qDebug() << "* Auto-moved card " << cardToMove.toString() << " to foundation " << foundationId;
|
||||||
return true;
|
return AutoMoveResult{AutoMoveResult::DestinationType::Foundation, foundationId};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Try moving the card to another column
|
// 2. Try moving the card to another column
|
||||||
|
@ -530,16 +547,16 @@ bool GameState::tryAutoMoveSingleCard(PlayingCard& cardToMove, int skipColumnId)
|
||||||
ColumnSlot* col = new ColumnSlot(&cardToMove, true, this);
|
ColumnSlot* col = new ColumnSlot(&cardToMove, true, this);
|
||||||
m_columns[columnId].append(col);
|
m_columns[columnId].append(col);
|
||||||
qDebug() << "* Auto-moved card " << cardToMove.toString() << " to column " << columnId;
|
qDebug() << "* Auto-moved card " << cardToMove.toString() << " to column " << columnId;
|
||||||
return true;
|
return AutoMoveResult{AutoMoveResult::DestinationType::Column, columnId};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No available auto-move
|
// No available auto-move
|
||||||
qDebug() << "* Auto-move failed, no available moves";
|
qDebug() << "* Auto-move failed, no available moves";
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameState::tryAutoMoveMultipleCards(const QList<PlayingCard*>& cards, int skipColumnId) {
|
std::optional<GameState::AutoMoveResult> GameState::tryAutoMoveMultipleCards(const QList<PlayingCard*>& cards, int skipColumnId) {
|
||||||
assert(cards.size() > 1);
|
assert(cards.size() > 1);
|
||||||
|
|
||||||
// If we can move the first (selected) card to another column,
|
// If we can move the first (selected) card to another column,
|
||||||
|
@ -559,12 +576,12 @@ bool GameState::tryAutoMoveMultipleCards(const QList<PlayingCard*>& cards, int s
|
||||||
m_columns[columnId].append(col);
|
m_columns[columnId].append(col);
|
||||||
qDebug() << "* Auto-moved card " << card->toString() << " to column " << columnId;
|
qDebug() << "* Auto-moved card " << card->toString() << " to column " << columnId;
|
||||||
}
|
}
|
||||||
return true;
|
return AutoMoveResult{AutoMoveResult::DestinationType::Column, columnId};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "* Auto-move failed, no available moves";
|
qDebug() << "* Auto-move failed, no available moves";
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameState::isFoundationMoveValid(const PlayingCard& cardToMove, int foundationId) const {
|
bool GameState::isFoundationMoveValid(const PlayingCard& cardToMove, int foundationId) const {
|
||||||
|
@ -634,6 +651,7 @@ void GameState::ensureColumnRevealed(int columnId) {
|
||||||
|
|
||||||
// First slot in the column must always be revealed, reveal it
|
// First slot in the column must always be revealed, reveal it
|
||||||
col->reveal();
|
col->reveal();
|
||||||
|
increaseScore(5); // Score increase for revealing a card
|
||||||
qDebug() << "Revealed card " << col->card()->toString() << " in column " << columnId;
|
qDebug() << "Revealed card " << col->card()->toString() << " in column " << columnId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,6 +660,11 @@ void GameState::incrementMoveAmt() {
|
||||||
emit moveAmountChanged();
|
emit moveAmountChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameState::increaseScore(int amount) {
|
||||||
|
m_score += amount;
|
||||||
|
emit scoreChanged();
|
||||||
|
}
|
||||||
|
|
||||||
bool GameState::prelimWinCheck() {
|
bool GameState::prelimWinCheck() {
|
||||||
// Check if the game is preliminarily won:
|
// Check if the game is preliminarily won:
|
||||||
// This occurs when all cards in the tableau are revealed.
|
// This occurs when all cards in the tableau are revealed.
|
||||||
|
@ -903,6 +926,10 @@ QVariant GameState::isWinnable() const {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GameState::score() const {
|
||||||
|
return m_score;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<std::optional<bool>, int> GameState::checkWinnable() {
|
std::pair<std::optional<bool>, int> GameState::checkWinnable() {
|
||||||
if (!m_enableWinnabilitySim)
|
if (!m_enableWinnabilitySim)
|
||||||
return {std::nullopt, 0};
|
return {std::nullopt, 0};
|
||||||
|
|
|
@ -27,6 +27,7 @@ class GameState : public QObject {
|
||||||
Q_PROPERTY(bool gameWon READ gameWon NOTIFY gameWonChanged)
|
Q_PROPERTY(bool gameWon READ gameWon NOTIFY gameWonChanged)
|
||||||
Q_PROPERTY(bool preliminaryWin READ preliminaryWin NOTIFY preliminaryWinChanged)
|
Q_PROPERTY(bool preliminaryWin READ preliminaryWin NOTIFY preliminaryWinChanged)
|
||||||
Q_PROPERTY(QVariant isWinnable READ isWinnable NOTIFY isWinnableChanged)
|
Q_PROPERTY(QVariant isWinnable READ isWinnable NOTIFY isWinnableChanged)
|
||||||
|
Q_PROPERTY(int score READ score NOTIFY scoreChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GameState(QObject* parent = nullptr, bool preDealCards = true, bool enableWinnabilitySim = true);
|
explicit GameState(QObject* parent = nullptr, bool preDealCards = true, bool enableWinnabilitySim = true);
|
||||||
|
@ -41,6 +42,7 @@ class GameState : public QObject {
|
||||||
bool preliminaryWin() const;
|
bool preliminaryWin() const;
|
||||||
bool gameWon() const;
|
bool gameWon() const;
|
||||||
QVariant isWinnable() const;
|
QVariant isWinnable() const;
|
||||||
|
int score() const;
|
||||||
|
|
||||||
// General functions
|
// General functions
|
||||||
Q_INVOKABLE void dealCards();
|
Q_INVOKABLE void dealCards();
|
||||||
|
@ -66,6 +68,7 @@ class GameState : public QObject {
|
||||||
void gameWonChanged();
|
void gameWonChanged();
|
||||||
void preliminaryWinChanged();
|
void preliminaryWinChanged();
|
||||||
void isWinnableChanged();
|
void isWinnableChanged();
|
||||||
|
void scoreChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onFoundationChanged();
|
void onFoundationChanged();
|
||||||
|
@ -82,19 +85,30 @@ class GameState : public QObject {
|
||||||
bool m_prelimWin;
|
bool m_prelimWin;
|
||||||
std::pair<std::optional<bool>, int> m_isWinnable;
|
std::pair<std::optional<bool>, int> m_isWinnable;
|
||||||
bool m_enableWinnabilitySim;
|
bool m_enableWinnabilitySim;
|
||||||
|
int m_score;
|
||||||
|
|
||||||
|
struct AutoMoveResult {
|
||||||
|
enum class DestinationType {
|
||||||
|
Foundation,
|
||||||
|
Column
|
||||||
|
};
|
||||||
|
DestinationType destinationType;
|
||||||
|
int destinationId;
|
||||||
|
};
|
||||||
|
|
||||||
GameState* clone(bool enableWinnabilitySim = false) const;
|
GameState* clone(bool enableWinnabilitySim = false) const;
|
||||||
void cleanupBoard(bool emitChanges);
|
void cleanupBoard(bool emitChanges);
|
||||||
QString generateStateHash() const;
|
QString generateStateHash() const;
|
||||||
|
|
||||||
bool tryAutoMoveSingleCard(PlayingCard& cardToMove, int skipColumnId = -1);
|
std::optional<AutoMoveResult> tryAutoMoveSingleCard(PlayingCard& cardToMove, int skipColumnId = -1);
|
||||||
bool tryAutoMoveMultipleCards(const QList<PlayingCard*>& cards, int skipColumnId);
|
std::optional<AutoMoveResult> tryAutoMoveMultipleCards(const QList<PlayingCard*>& cards, int skipColumnId);
|
||||||
|
|
||||||
bool isFoundationMoveValid(const PlayingCard& cardToMove, int foundationId) const;
|
bool isFoundationMoveValid(const PlayingCard& cardToMove, int foundationId) const;
|
||||||
bool isColumnMoveValid(const PlayingCard& cardToMove, int columnId) const;
|
bool isColumnMoveValid(const PlayingCard& cardToMove, int columnId) const;
|
||||||
|
|
||||||
void ensureColumnRevealed(int columnId);
|
void ensureColumnRevealed(int columnId);
|
||||||
void incrementMoveAmt();
|
void incrementMoveAmt();
|
||||||
|
void increaseScore(int amount);
|
||||||
|
|
||||||
bool winCheck();
|
bool winCheck();
|
||||||
bool prelimWinCheck();
|
bool prelimWinCheck();
|
||||||
|
|
Loading…
Reference in a new issue