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