Convert winnability check to property
This commit is contained in:
parent
3254818410
commit
c5e68601a4
|
@ -78,14 +78,6 @@ Item {
|
|||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (GameState.drawNextCard()) {
|
||||
if (GameState.isWinnable()) {
|
||||
console.log("Still winnable");
|
||||
} else {
|
||||
console.warn("Game is lost");
|
||||
}
|
||||
}
|
||||
}
|
||||
onClicked: GameState.drawNextCard()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,13 +50,7 @@ Row {
|
|||
isFaceDown: parent.colSlot ? !parent.colSlot.revealed : false
|
||||
onClicked: {
|
||||
if (parent.colSlot && parent.colSlot.revealed) {
|
||||
if (GameState.autoMoveColumnCard(parent.columnId, parent.cardId)) {
|
||||
if (GameState.isWinnable()) {
|
||||
console.log("Still winnable");
|
||||
} else {
|
||||
console.log("Game is lost");
|
||||
}
|
||||
}
|
||||
GameState.autoMoveColumnCard(parent.columnId, parent.cardId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,13 +26,7 @@ Row {
|
|||
// Only auto-move the last card in the throwaway pile
|
||||
// cards below it are shown, but shouldn't have a click effect
|
||||
if (reversedIndex == 0) {
|
||||
if (GameState.autoMoveThrownCard()) {
|
||||
if (GameState.isWinnable()) {
|
||||
console.log("Still winnable");
|
||||
} else {
|
||||
console.log("Game is lost");
|
||||
}
|
||||
}
|
||||
GameState.autoMoveThrownCard();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
#include "gamestate.h"
|
||||
#include <QDebug>
|
||||
#include <QSet>
|
||||
#include <optional>
|
||||
#include <qlist.h>
|
||||
#include <qqmllist.h>
|
||||
#include <queue>
|
||||
#include <random>
|
||||
|
||||
GameState::GameState(QObject* parent, bool preDealCards) : QObject{parent}, m_columns(7), m_foundation(4), m_gameWon(false) {
|
||||
GameState::GameState(QObject* parent, bool preDealCards, bool enableWinnabilitySim) :
|
||||
QObject{parent}, m_columns(7), m_foundation(4), m_moveAmt(0), m_gameWon(false), m_prelimWin(false), m_isWinnable({std::nullopt, 0}),
|
||||
m_enableWinnabilitySim(enableWinnabilitySim) {
|
||||
assert(connect(this, &GameState::foundationChanged, this, &GameState::onFoundationChanged));
|
||||
assert(connect(this, &GameState::throwawayPileChanged, this, &GameState::onThrowawayPileChanged));
|
||||
assert(connect(this, &GameState::moveAmountChanged, this, &GameState::onMoveAmountChanged));
|
||||
|
||||
if (preDealCards)
|
||||
dealCards();
|
||||
|
@ -363,8 +367,12 @@ void GameState::onThrowawayPileChanged() {
|
|||
prelimWinCheck();
|
||||
}
|
||||
|
||||
GameState* GameState::clone() const {
|
||||
GameState* newGameState = new GameState(nullptr, false);
|
||||
void GameState::onMoveAmountChanged() {
|
||||
checkWinnable();
|
||||
}
|
||||
|
||||
GameState* GameState::clone(bool enableWinnabilitySim) const {
|
||||
GameState* newGameState = new GameState(nullptr, false, enableWinnabilitySim);
|
||||
|
||||
// Deep copy the necessary data
|
||||
|
||||
|
@ -435,7 +443,8 @@ void GameState::cleanupBoard(bool emitChanges) {
|
|||
m_moveAmt = 0;
|
||||
|
||||
// Note that we don't need to reset gameWon/prelimWin from here, as it's
|
||||
// auto-checked from onFoundationChanged, which the emits trigger
|
||||
// auto-checked from onFoundationChanged, which the emits trigger. Similarly
|
||||
// isWinnable status will run from onMoveAmountChanged slot.
|
||||
|
||||
if (emitChanges) {
|
||||
emit drawPileChanged();
|
||||
|
@ -871,6 +880,13 @@ bool GameState::gameWon() const {
|
|||
}
|
||||
|
||||
std::pair<std::optional<bool>, int> GameState::isWinnable() const {
|
||||
return m_isWinnable;
|
||||
}
|
||||
|
||||
std::pair<std::optional<bool>, int> GameState::checkWinnable() {
|
||||
if (!m_enableWinnabilitySim)
|
||||
return {std::nullopt, 0};
|
||||
|
||||
qDebug() << "--- Simulating winning scenario ---";
|
||||
QElapsedTimer timer;
|
||||
|
||||
|
@ -889,5 +905,11 @@ std::pair<std::optional<bool>, int> GameState::isWinnable() const {
|
|||
qInstallMessageHandler(originalHandler);
|
||||
|
||||
qDebug() << "--- Simulation end (result:" << res << ", took:" << elapsedTime << "ms) ---";
|
||||
|
||||
if (m_isWinnable != res) {
|
||||
m_isWinnable = res;
|
||||
emit isWinnableChanged();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <qelapsedtimer.h>
|
||||
#include <qqmlintegration.h>
|
||||
#include <qqmllist.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
// Limits for checking winnability
|
||||
#define MAX_EVAL_TIME 100 // Evaluation time limit (ms)
|
||||
|
@ -24,9 +25,10 @@ class GameState : public QObject {
|
|||
Q_PROPERTY(int moveAmount READ moveAmount NOTIFY moveAmountChanged)
|
||||
Q_PROPERTY(bool gameWon READ gameWon NOTIFY gameWonChanged)
|
||||
Q_PROPERTY(bool preliminaryWin READ preliminaryWin NOTIFY preliminaryWinChanged)
|
||||
Q_PROPERTY(std::pair<std::optional<bool>, int> isWinnable READ isWinnable NOTIFY isWinnableChanged)
|
||||
|
||||
public:
|
||||
explicit GameState(QObject* parent = nullptr, bool preDealCards = true);
|
||||
explicit GameState(QObject* parent = nullptr, bool preDealCards = true, bool enableWinnabilitySim = true);
|
||||
~GameState();
|
||||
|
||||
// Getters
|
||||
|
@ -37,12 +39,12 @@ class GameState : public QObject {
|
|||
int moveAmount() const;
|
||||
bool preliminaryWin() const;
|
||||
bool gameWon() const;
|
||||
std::pair<std::optional<bool>, int> isWinnable() const;
|
||||
|
||||
// General functions
|
||||
Q_INVOKABLE void dealCards();
|
||||
Q_INVOKABLE void setupWinningDeck();
|
||||
Q_INVOKABLE bool drawNextCard();
|
||||
Q_INVOKABLE std::pair<std::optional<bool>, int> isWinnable() const; // TODO: Implement as Q_PROPERTY instead
|
||||
|
||||
// Manual moves (from X to Y)
|
||||
Q_INVOKABLE bool moveThrownCardToColumn(int columnId);
|
||||
|
@ -62,10 +64,12 @@ class GameState : public QObject {
|
|||
void moveAmountChanged();
|
||||
void gameWonChanged();
|
||||
void preliminaryWinChanged();
|
||||
void isWinnableChanged();
|
||||
|
||||
private slots:
|
||||
void onFoundationChanged();
|
||||
void onThrowawayPileChanged();
|
||||
void onMoveAmountChanged();
|
||||
|
||||
private:
|
||||
QList<PlayingCard*> m_drawPile;
|
||||
|
@ -75,8 +79,10 @@ class GameState : public QObject {
|
|||
int m_moveAmt;
|
||||
bool m_gameWon;
|
||||
bool m_prelimWin;
|
||||
std::pair<std::optional<bool>, int> m_isWinnable;
|
||||
bool m_enableWinnabilitySim;
|
||||
|
||||
GameState* clone() const;
|
||||
GameState* clone(bool enableWinnabilitySim = false) const;
|
||||
void cleanupBoard(bool emitChanges);
|
||||
QString generateStateHash() const;
|
||||
|
||||
|
@ -91,7 +97,7 @@ class GameState : public QObject {
|
|||
|
||||
bool winCheck();
|
||||
bool prelimWinCheck();
|
||||
|
||||
std::pair<std::optional<bool>, int> checkWinnable();
|
||||
std::pair<std::optional<bool>, int> canWinThroughSimulation(QSet<QString>& visitedStates, QElapsedTimer timer) const;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue