Use unique ptr for nicer memory freeing in win simulation func
This also switches from using `QQueue` here to `std::queue` as `QQueue` doesn't seem to play well with holding unique pointers.
This commit is contained in:
parent
b209fbc94b
commit
88cdf28cfc
|
@ -1,9 +1,9 @@
|
||||||
#include "gamestate.h"
|
#include "gamestate.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QQueue>
|
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <qlist.h>
|
#include <qlist.h>
|
||||||
#include <qqmllist.h>
|
#include <qqmllist.h>
|
||||||
|
#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) : QObject{parent}, m_columns(7), m_foundation(4), m_gameWon(false) {
|
||||||
|
@ -623,22 +623,22 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
||||||
|
|
||||||
// Go over all possible moves using BFS
|
// Go over all possible moves using BFS
|
||||||
|
|
||||||
QQueue<std::pair<GameState*, int>> stateQueue; // BFS queue (game state, depth)
|
std::queue<std::pair<std::unique_ptr<GameState>, int>> stateQueue; // BFS queue (game state, depth)
|
||||||
|
|
||||||
// Clone the current state as the root of BFS
|
// Clone the current state as the root of BFS
|
||||||
GameState* initialState = this->clone();
|
auto initialState = std::unique_ptr<GameState>(this->clone());
|
||||||
stateQueue.enqueue({initialState, 0});
|
stateQueue.emplace(std::move(initialState), 0);
|
||||||
visitedStates.insert(initialState->generateStateHash());
|
visitedStates.insert(stateQueue.front().first->generateStateHash());
|
||||||
|
|
||||||
int lastDepth = 0;
|
int lastDepth = 0;
|
||||||
while (!stateQueue.isEmpty()) {
|
while (!stateQueue.empty()) {
|
||||||
auto [currentState, depth] = stateQueue.dequeue();
|
auto [currentState, depth] = std::move(stateQueue.front());
|
||||||
|
stateQueue.pop();
|
||||||
auto currentHash = currentState->generateStateHash();
|
auto currentHash = currentState->generateStateHash();
|
||||||
lastDepth = depth;
|
lastDepth = depth;
|
||||||
|
|
||||||
// Limit evaluation time (ensures this doesn't block the game)
|
// Limit evaluation time (ensures this doesn't block the game)
|
||||||
if (timer.hasExpired(MAX_EVAL_TIME)) {
|
if (timer.hasExpired(MAX_EVAL_TIME)) {
|
||||||
delete currentState;
|
|
||||||
return std::make_pair(std::nullopt, depth);
|
return std::make_pair(std::nullopt, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,22 +653,18 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
||||||
if (!currentState->isFoundationMoveValid(*topSlot->card(), foundationId))
|
if (!currentState->isFoundationMoveValid(*topSlot->card(), foundationId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GameState* newState = currentState->clone();
|
auto newState = std::unique_ptr<GameState>(currentState->clone());
|
||||||
assert(newState->moveColumnCardToFoundation(columnId, foundationId));
|
assert(newState->moveColumnCardToFoundation(columnId, foundationId));
|
||||||
QString stateHash = newState->generateStateHash();
|
QString stateHash = newState->generateStateHash();
|
||||||
assert(currentHash != stateHash);
|
assert(currentHash != stateHash);
|
||||||
|
|
||||||
if (newState->m_gameWon) {
|
if (newState->m_gameWon) {
|
||||||
delete newState;
|
|
||||||
delete currentState;
|
|
||||||
return std::make_pair(true, depth + 1); // Return depth if game won
|
return std::make_pair(true, depth + 1); // Return depth if game won
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!visitedStates.contains(stateHash)) {
|
if (!visitedStates.contains(stateHash)) {
|
||||||
visitedStates.insert(stateHash);
|
visitedStates.insert(stateHash);
|
||||||
stateQueue.enqueue({newState, depth + 1});
|
stateQueue.push({std::move(newState), depth + 1});
|
||||||
} else {
|
|
||||||
delete newState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -682,22 +678,18 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
||||||
if (!currentState->isFoundationMoveValid(*topCard, foundationId))
|
if (!currentState->isFoundationMoveValid(*topCard, foundationId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GameState* newState = currentState->clone();
|
auto newState = std::unique_ptr<GameState>(currentState->clone());
|
||||||
assert(newState->moveThrownCardToFoundation(foundationId));
|
assert(newState->moveThrownCardToFoundation(foundationId));
|
||||||
QString stateHash = newState->generateStateHash();
|
QString stateHash = newState->generateStateHash();
|
||||||
assert(currentHash != stateHash);
|
assert(currentHash != stateHash);
|
||||||
|
|
||||||
if (newState->m_gameWon) {
|
if (newState->m_gameWon) {
|
||||||
delete newState;
|
|
||||||
delete currentState;
|
|
||||||
return std::make_pair(true, depth + 1);
|
return std::make_pair(true, depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!visitedStates.contains(stateHash)) {
|
if (!visitedStates.contains(stateHash)) {
|
||||||
visitedStates.insert(stateHash);
|
visitedStates.insert(stateHash);
|
||||||
stateQueue.enqueue({newState, depth + 1});
|
stateQueue.push({std::move(newState), depth + 1});
|
||||||
} else {
|
|
||||||
delete newState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -706,22 +698,18 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
||||||
if (!currentState->isColumnMoveValid(*topCard, toColumnId))
|
if (!currentState->isColumnMoveValid(*topCard, toColumnId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GameState* newState = currentState->clone();
|
auto newState = std::unique_ptr<GameState>(currentState->clone());
|
||||||
assert(newState->moveThrownCardToColumn(toColumnId));
|
assert(newState->moveThrownCardToColumn(toColumnId));
|
||||||
QString stateHash = newState->generateStateHash();
|
QString stateHash = newState->generateStateHash();
|
||||||
assert(currentHash != stateHash);
|
assert(currentHash != stateHash);
|
||||||
|
|
||||||
if (newState->m_gameWon) {
|
if (newState->m_gameWon) {
|
||||||
delete newState;
|
|
||||||
delete currentState;
|
|
||||||
return std::make_pair(true, depth + 1);
|
return std::make_pair(true, depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!visitedStates.contains(stateHash)) {
|
if (!visitedStates.contains(stateHash)) {
|
||||||
visitedStates.insert(stateHash);
|
visitedStates.insert(stateHash);
|
||||||
stateQueue.enqueue({newState, depth + 1});
|
stateQueue.push({std::move(newState), depth + 1});
|
||||||
} else {
|
|
||||||
delete newState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -730,22 +718,18 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
||||||
// (The condition also handles the case where there's only one card in the throwaway pile,
|
// (The condition also handles the case where there's only one card in the throwaway pile,
|
||||||
// which means drawing would just result in flipping and re-drawing the same card.)
|
// which means drawing would just result in flipping and re-drawing the same card.)
|
||||||
if (!(currentState->m_drawPile.isEmpty() && currentState->m_throwawayPile.size() <= 1)) {
|
if (!(currentState->m_drawPile.isEmpty() && currentState->m_throwawayPile.size() <= 1)) {
|
||||||
GameState* newState = currentState->clone();
|
auto newState = std::unique_ptr<GameState>(currentState->clone());
|
||||||
assert(newState->drawNextCard());
|
assert(newState->drawNextCard());
|
||||||
QString stateHash = newState->generateStateHash();
|
QString stateHash = newState->generateStateHash();
|
||||||
assert(currentHash != stateHash);
|
assert(currentHash != stateHash);
|
||||||
|
|
||||||
if (newState->m_gameWon) {
|
if (newState->m_gameWon) {
|
||||||
delete newState;
|
|
||||||
delete currentState;
|
|
||||||
return std::make_pair(true, depth + 1);
|
return std::make_pair(true, depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!visitedStates.contains(stateHash)) {
|
if (!visitedStates.contains(stateHash)) {
|
||||||
visitedStates.insert(stateHash);
|
visitedStates.insert(stateHash);
|
||||||
stateQueue.enqueue({newState, depth + 1});
|
stateQueue.push({std::move(newState), depth + 1});
|
||||||
} else {
|
|
||||||
delete newState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,28 +750,22 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
|
||||||
if (!currentState->isColumnMoveValid(*fromSlot->card(), toColumnId))
|
if (!currentState->isColumnMoveValid(*fromSlot->card(), toColumnId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GameState* newState = currentState->clone();
|
auto newState = std::unique_ptr<GameState>(currentState->clone());
|
||||||
assert(newState->moveColumnCardToColumn(fromColumnId, toColumnId, fromCardIndex));
|
assert(newState->moveColumnCardToColumn(fromColumnId, toColumnId, fromCardIndex));
|
||||||
QString stateHash = newState->generateStateHash();
|
QString stateHash = newState->generateStateHash();
|
||||||
assert(currentHash != stateHash);
|
assert(currentHash != stateHash);
|
||||||
|
|
||||||
if (newState->m_gameWon) {
|
if (newState->m_gameWon) {
|
||||||
delete newState;
|
|
||||||
delete currentState;
|
|
||||||
return std::make_pair(true, depth + 1);
|
return std::make_pair(true, depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!visitedStates.contains(stateHash)) {
|
if (!visitedStates.contains(stateHash)) {
|
||||||
visitedStates.insert(stateHash);
|
visitedStates.insert(stateHash);
|
||||||
stateQueue.enqueue({newState, depth + 1});
|
stateQueue.push({std::move(newState), depth + 1});
|
||||||
} else {
|
|
||||||
delete newState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete currentState; // Cleanup current state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_pair(false, lastDepth); // No solution
|
return std::make_pair(false, lastDepth); // No solution
|
||||||
|
|
Loading…
Reference in a new issue