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:
ItsDrike 2024-12-07 21:03:34 +01:00
parent b209fbc94b
commit 88cdf28cfc
Signed by: ItsDrike
GPG key ID: FA2745890B7048C0

View file

@ -1,9 +1,9 @@
#include "gamestate.h"
#include <QDebug>
#include <QQueue>
#include <QSet>
#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) {
@ -623,22 +623,22 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
// 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
GameState* initialState = this->clone();
stateQueue.enqueue({initialState, 0});
visitedStates.insert(initialState->generateStateHash());
auto initialState = std::unique_ptr<GameState>(this->clone());
stateQueue.emplace(std::move(initialState), 0);
visitedStates.insert(stateQueue.front().first->generateStateHash());
int lastDepth = 0;
while (!stateQueue.isEmpty()) {
auto [currentState, depth] = stateQueue.dequeue();
while (!stateQueue.empty()) {
auto [currentState, depth] = std::move(stateQueue.front());
stateQueue.pop();
auto currentHash = currentState->generateStateHash();
lastDepth = depth;
// Limit evaluation time (ensures this doesn't block the game)
if (timer.hasExpired(MAX_EVAL_TIME)) {
delete currentState;
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))
continue;
GameState* newState = currentState->clone();
auto newState = std::unique_ptr<GameState>(currentState->clone());
assert(newState->moveColumnCardToFoundation(columnId, foundationId));
QString stateHash = newState->generateStateHash();
assert(currentHash != stateHash);
if (newState->m_gameWon) {
delete newState;
delete currentState;
return std::make_pair(true, depth + 1); // Return depth if game won
}
if (!visitedStates.contains(stateHash)) {
visitedStates.insert(stateHash);
stateQueue.enqueue({newState, depth + 1});
} else {
delete newState;
stateQueue.push({std::move(newState), depth + 1});
}
}
}
@ -682,22 +678,18 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
if (!currentState->isFoundationMoveValid(*topCard, foundationId))
continue;
GameState* newState = currentState->clone();
auto newState = std::unique_ptr<GameState>(currentState->clone());
assert(newState->moveThrownCardToFoundation(foundationId));
QString stateHash = newState->generateStateHash();
assert(currentHash != stateHash);
if (newState->m_gameWon) {
delete newState;
delete currentState;
return std::make_pair(true, depth + 1);
}
if (!visitedStates.contains(stateHash)) {
visitedStates.insert(stateHash);
stateQueue.enqueue({newState, depth + 1});
} else {
delete newState;
stateQueue.push({std::move(newState), depth + 1});
}
}
@ -706,22 +698,18 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
if (!currentState->isColumnMoveValid(*topCard, toColumnId))
continue;
GameState* newState = currentState->clone();
auto newState = std::unique_ptr<GameState>(currentState->clone());
assert(newState->moveThrownCardToColumn(toColumnId));
QString stateHash = newState->generateStateHash();
assert(currentHash != stateHash);
if (newState->m_gameWon) {
delete newState;
delete currentState;
return std::make_pair(true, depth + 1);
}
if (!visitedStates.contains(stateHash)) {
visitedStates.insert(stateHash);
stateQueue.enqueue({newState, depth + 1});
} else {
delete newState;
stateQueue.push({std::move(newState), depth + 1});
}
}
}
@ -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,
// which means drawing would just result in flipping and re-drawing the same card.)
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());
QString stateHash = newState->generateStateHash();
assert(currentHash != stateHash);
if (newState->m_gameWon) {
delete newState;
delete currentState;
return std::make_pair(true, depth + 1);
}
if (!visitedStates.contains(stateHash)) {
visitedStates.insert(stateHash);
stateQueue.enqueue({newState, depth + 1});
} else {
delete newState;
stateQueue.push({std::move(newState), depth + 1});
}
}
@ -766,28 +750,22 @@ std::pair<std::optional<bool>, int> GameState::canWinThroughSimulation(QSet<QStr
if (!currentState->isColumnMoveValid(*fromSlot->card(), toColumnId))
continue;
GameState* newState = currentState->clone();
auto newState = std::unique_ptr<GameState>(currentState->clone());
assert(newState->moveColumnCardToColumn(fromColumnId, toColumnId, fromCardIndex));
QString stateHash = newState->generateStateHash();
assert(currentHash != stateHash);
if (newState->m_gameWon) {
delete newState;
delete currentState;
return std::make_pair(true, depth + 1);
}
if (!visitedStates.contains(stateHash)) {
visitedStates.insert(stateHash);
stateQueue.enqueue({newState, depth + 1});
} else {
delete newState;
stateQueue.push({std::move(newState), depth + 1});
}
}
}
}
delete currentState; // Cleanup current state
}
return std::make_pair(false, lastDepth); // No solution