Add preliminary win detection
This commit is contained in:
parent
e46d153604
commit
3254818410
|
@ -8,6 +8,7 @@
|
|||
|
||||
GameState::GameState(QObject* parent, bool preDealCards) : QObject{parent}, m_columns(7), m_foundation(4), m_gameWon(false) {
|
||||
assert(connect(this, &GameState::foundationChanged, this, &GameState::onFoundationChanged));
|
||||
assert(connect(this, &GameState::throwawayPileChanged, this, &GameState::onThrowawayPileChanged));
|
||||
|
||||
if (preDealCards)
|
||||
dealCards();
|
||||
|
@ -354,24 +355,12 @@ bool GameState::autoMoveColumnCard(int columnId, int cardIndex) {
|
|||
}
|
||||
|
||||
void GameState::onFoundationChanged() {
|
||||
// Check if the game is won (can only happen on a foundation pile change)
|
||||
bool gameWon = true;
|
||||
for (const QList<PlayingCard*>& foundationPile : std::as_const(m_foundation)) {
|
||||
// The piles need to contain all 13 card values each, otherwise the game isn't won
|
||||
if (foundationPile.size() != 13) {
|
||||
gameWon = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
prelimWinCheck();
|
||||
winCheck();
|
||||
}
|
||||
|
||||
if (gameWon == m_gameWon)
|
||||
return;
|
||||
|
||||
if (gameWon)
|
||||
qDebug() << "The game was won!";
|
||||
|
||||
m_gameWon = gameWon;
|
||||
emit gameWonChanged();
|
||||
void GameState::onThrowawayPileChanged() {
|
||||
prelimWinCheck();
|
||||
}
|
||||
|
||||
GameState* GameState::clone() const {
|
||||
|
@ -406,6 +395,7 @@ GameState* GameState::clone() const {
|
|||
}
|
||||
|
||||
newGameState->m_gameWon = m_gameWon;
|
||||
newGameState->m_prelimWin = m_prelimWin;
|
||||
|
||||
assert(this->generateStateHash() == newGameState->generateStateHash());
|
||||
return newGameState;
|
||||
|
@ -444,7 +434,7 @@ void GameState::cleanupBoard(bool emitChanges) {
|
|||
|
||||
m_moveAmt = 0;
|
||||
|
||||
// Note that we don't need to reset gameWon 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
|
||||
|
||||
if (emitChanges) {
|
||||
|
@ -498,9 +488,6 @@ QString GameState::generateStateHash() const {
|
|||
}
|
||||
|
||||
stateHash.removeLast();
|
||||
stateHash += " ; ";
|
||||
|
||||
stateHash += m_gameWon ? "t" : "f";
|
||||
|
||||
return stateHash;
|
||||
}
|
||||
|
@ -635,8 +622,63 @@ void GameState::incrementMoveAmt() {
|
|||
emit moveAmountChanged();
|
||||
}
|
||||
|
||||
bool GameState::prelimWinCheck() {
|
||||
// Check if the game is preliminarily won:
|
||||
// This occurs when there are no cards in the draw or throwaway piles,
|
||||
// and all cards in the tableau are revealed. Such games are essentially won,
|
||||
// as the cards only need to be moved to the foundation piles.
|
||||
bool prelimWin = m_drawPile.isEmpty() && m_throwawayPile.isEmpty();
|
||||
|
||||
if (prelimWin) {
|
||||
for (const auto& column : std::as_const(m_columns)) {
|
||||
for (const ColumnSlot* card : column) {
|
||||
if (!card->isRevealed()) {
|
||||
prelimWin = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!prelimWin)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (prelimWin == m_prelimWin)
|
||||
return m_prelimWin;
|
||||
|
||||
if (prelimWin)
|
||||
qDebug() << "Preliminary win detected! The game is essentially won.";
|
||||
|
||||
m_prelimWin = prelimWin;
|
||||
emit preliminaryWinChanged();
|
||||
|
||||
return m_prelimWin;
|
||||
}
|
||||
|
||||
bool GameState::winCheck() {
|
||||
// Check if the game is won (can only happen on a foundation pile change)
|
||||
bool gameWon = true;
|
||||
for (const QList<PlayingCard*>& foundationPile : std::as_const(m_foundation)) {
|
||||
// The piles need to contain all 13 card values each, otherwise the game isn't won
|
||||
if (foundationPile.size() != 13) {
|
||||
gameWon = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gameWon == m_gameWon)
|
||||
return m_gameWon;
|
||||
|
||||
if (gameWon)
|
||||
qDebug() << "The game was won!";
|
||||
|
||||
m_gameWon = gameWon;
|
||||
emit gameWonChanged();
|
||||
|
||||
return m_gameWon;
|
||||
}
|
||||
|
||||
std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QString>& visitedStates, QElapsedTimer timer) const {
|
||||
if (m_gameWon)
|
||||
if (m_prelimWin)
|
||||
return std::make_pair(true, 0); // Already won at depth 0
|
||||
|
||||
// Go over all possible moves using BFS
|
||||
|
@ -676,9 +718,8 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
|||
QString stateHash = newState->generateStateHash();
|
||||
assert(currentHash != stateHash);
|
||||
|
||||
if (newState->m_gameWon) {
|
||||
if (newState->prelimWinCheck())
|
||||
return std::make_pair(true, depth + 1); // Return depth if game won
|
||||
}
|
||||
|
||||
if (!visitedStates.contains(stateHash)) {
|
||||
visitedStates.insert(stateHash);
|
||||
|
@ -701,9 +742,8 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
|||
QString stateHash = newState->generateStateHash();
|
||||
assert(currentHash != stateHash);
|
||||
|
||||
if (newState->m_gameWon) {
|
||||
if (newState->prelimWinCheck())
|
||||
return std::make_pair(true, depth + 1);
|
||||
}
|
||||
|
||||
if (!visitedStates.contains(stateHash)) {
|
||||
visitedStates.insert(stateHash);
|
||||
|
@ -721,9 +761,8 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
|||
QString stateHash = newState->generateStateHash();
|
||||
assert(currentHash != stateHash);
|
||||
|
||||
if (newState->m_gameWon) {
|
||||
if (newState->prelimWinCheck())
|
||||
return std::make_pair(true, depth + 1);
|
||||
}
|
||||
|
||||
if (!visitedStates.contains(stateHash)) {
|
||||
visitedStates.insert(stateHash);
|
||||
|
@ -741,9 +780,8 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
|||
QString stateHash = newState->generateStateHash();
|
||||
assert(currentHash != stateHash);
|
||||
|
||||
if (newState->m_gameWon) {
|
||||
if (newState->prelimWinCheck())
|
||||
return std::make_pair(true, depth + 1);
|
||||
}
|
||||
|
||||
if (!visitedStates.contains(stateHash)) {
|
||||
visitedStates.insert(stateHash);
|
||||
|
@ -773,9 +811,8 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
|||
QString stateHash = newState->generateStateHash();
|
||||
assert(currentHash != stateHash);
|
||||
|
||||
if (newState->m_gameWon) {
|
||||
if (newState->prelimWinCheck())
|
||||
return std::make_pair(true, depth + 1);
|
||||
}
|
||||
|
||||
if (!visitedStates.contains(stateHash)) {
|
||||
visitedStates.insert(stateHash);
|
||||
|
@ -825,6 +862,10 @@ int GameState::moveAmount() const {
|
|||
return m_moveAmt;
|
||||
}
|
||||
|
||||
bool GameState::preliminaryWin() const {
|
||||
return m_prelimWin;
|
||||
}
|
||||
|
||||
bool GameState::gameWon() const {
|
||||
return m_gameWon;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ class GameState : public QObject {
|
|||
Q_PROPERTY(QVariantList foundation READ foundation NOTIFY foundationChanged)
|
||||
Q_PROPERTY(int moveAmount READ moveAmount NOTIFY moveAmountChanged)
|
||||
Q_PROPERTY(bool gameWon READ gameWon NOTIFY gameWonChanged)
|
||||
Q_PROPERTY(bool preliminaryWin READ preliminaryWin NOTIFY preliminaryWinChanged)
|
||||
|
||||
public:
|
||||
explicit GameState(QObject* parent = nullptr, bool preDealCards = true);
|
||||
|
@ -34,6 +35,7 @@ class GameState : public QObject {
|
|||
QVariantList columns() const;
|
||||
QVariantList foundation() const;
|
||||
int moveAmount() const;
|
||||
bool preliminaryWin() const;
|
||||
bool gameWon() const;
|
||||
|
||||
// General functions
|
||||
|
@ -59,9 +61,11 @@ class GameState : public QObject {
|
|||
void foundationChanged();
|
||||
void moveAmountChanged();
|
||||
void gameWonChanged();
|
||||
void preliminaryWinChanged();
|
||||
|
||||
private slots:
|
||||
void onFoundationChanged();
|
||||
void onThrowawayPileChanged();
|
||||
|
||||
private:
|
||||
QList<PlayingCard*> m_drawPile;
|
||||
|
@ -70,6 +74,7 @@ class GameState : public QObject {
|
|||
QList<QList<PlayingCard*>> m_foundation;
|
||||
int m_moveAmt;
|
||||
bool m_gameWon;
|
||||
bool m_prelimWin;
|
||||
|
||||
GameState* clone() const;
|
||||
void cleanupBoard(bool emitChanges);
|
||||
|
@ -84,6 +89,9 @@ class GameState : public QObject {
|
|||
void ensureColumnRevealed(int columnId);
|
||||
void incrementMoveAmt();
|
||||
|
||||
bool winCheck();
|
||||
bool prelimWinCheck();
|
||||
|
||||
std::pair<std::optional<bool>, int> canWinThroughSimulation(QSet<QString>& visitedStates, QElapsedTimer timer) const;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue