From d30b4adb9646da26bace320a0abfaace6c05368e Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 11 Mar 2019 11:02:41 -0500 Subject: Move simulate code to a new static function. The code running the simulation is now disconnected from the Simulation class, with appropriate members being copied into a SimmedMoveConstants field (read-only, thread-safe) and a SimmedMoveMessage (the class for communicating per-sim data to and from external threads). The newly added static function, Simulate::simulateOnePosition(), should now be thread-safe and usable as a run function for a thread. --- sim.cpp | 208 +++++++++++++++++++++++++++++++++------------------------------- sim.h | 1 + 2 files changed, 107 insertions(+), 102 deletions(-) diff --git a/sim.cpp b/sim.cpp index 964ca42..a5f6558 100644 --- a/sim.cpp +++ b/sim.cpp @@ -270,9 +270,6 @@ void Simulator::simulate(int plies) UVcout << "simulating " << (*moveIt).move << ":" << endl; #endif - Game game = constants.game; - double residual = 0; - moveIt.levels.setNumberLevels(constants.levelCount + 1); SimmedMoveMessage message; @@ -282,131 +279,138 @@ void Simulator::simulate(int plies) message.levels = moveIt.levels; message.xmlIndent = m_xmlIndent; - int levelNumber = 1; - for (LevelList::iterator levelIt = message.levels.begin(); levelNumber <= constants.levelCount + 1 && levelIt != message.levels.end() && !game.currentPosition().gameOver(); ++levelIt, ++levelNumber) - { - const int decimal = levelNumber == constants.levelCount + 1? constants.decimalTurns : constants.playerCount; - if (decimal == 0) - continue; + simulateOnePosition(message, constants); + incorporateMessage(message); + } + + if (isLogging()) + { + m_xmlIndent = m_xmlIndent.substr(0, m_xmlIndent.length() - 1); + m_logfileStream << m_xmlIndent << "" << endl; + } +} + +void Simulator::simulateOnePosition(SimmedMoveMessage &message, const SimmedMoveConstants &constants) +{ + Game game = constants.game; + double residual = 0; + + int levelNumber = 1; + for (LevelList::iterator levelIt = message.levels.begin(); levelNumber <= constants.levelCount + 1 && levelIt != message.levels.end() && !game.currentPosition().gameOver(); ++levelIt, ++levelNumber) + { + const int decimal = levelNumber == constants.levelCount + 1? constants.decimalTurns : constants.playerCount; + if (decimal == 0) + continue; + + (*levelIt).setNumberScores(decimal); - (*levelIt).setNumberScores(decimal); + int playerNumber = 1; + for (auto &scoresIt : (*levelIt).statistics) + { + if (game.currentPosition().gameOver()) + break; + ++playerNumber; + const int playerId = game.currentPosition().currentPlayer().id(); - int playerNumber = 1; - for (auto &scoresIt : (*levelIt).statistics) + if (constants.isLogging) { - if (game.currentPosition().gameOver()) - break; - ++playerNumber; - const int playerId = game.currentPosition().currentPlayer().id(); + message.logStream << message.xmlIndent << "" << endl; + message.xmlIndent += MARK_UV('\t'); + } - if (constants.isLogging) - { - message.logStream << message.xmlIndent << "" << endl; - message.xmlIndent += MARK_UV('\t'); - } + Move move = Move::createNonmove(); - Move move = Move::createNonmove(); + if (playerId == constants.startPlayerId && levelNumber == 1) + move = message.move; + else if (constants.ignoreOppos && playerId != constants.startPlayerId) + move = Move::createPassMove(); + else + move = game.currentPosition().staticBestMove(); - if (playerId == constants.startPlayerId && levelNumber == 1) - move = message.move; - else if (constants.ignoreOppos && playerId != constants.startPlayerId) - move = Move::createPassMove(); - else - move = game.currentPosition().staticBestMove(); + int deadwoodScore = 0; + if (game.currentPosition().doesMoveEndGame(move)) + { + LetterString deadwood; + deadwoodScore = game.currentPosition().deadwood(&deadwood); + // account for deadwood in this move rather than a separate + // UnusedTilesBonus move. + move.score += deadwoodScore; + } - int deadwoodScore = 0; - if (game.currentPosition().doesMoveEndGame(move)) - { - LetterString deadwood; - deadwoodScore = game.currentPosition().deadwood(&deadwood); - // account for deadwood in this move rather than a separate - // UnusedTilesBonus move. - move.score += deadwoodScore; - } + scoresIt.score.incorporateValue(move.score); + scoresIt.bingos.incorporateValue(move.isBingo? 1.0 : 0.0); - scoresIt.score.incorporateValue(move.score); - scoresIt.bingos.incorporateValue(move.isBingo? 1.0 : 0.0); + if (constants.isLogging) + { + message.logStream << message.xmlIndent << game.currentPosition().currentPlayer().rack().xml() << endl; + message.logStream << message.xmlIndent << move.xml() << endl; + } - if (constants.isLogging) - { - message.logStream << message.xmlIndent << game.currentPosition().currentPlayer().rack().xml() << endl; - message.logStream << message.xmlIndent << move.xml() << endl; - } + // record future-looking residuals + bool isFinalTurnForPlayerOfSimulation = false; - // record future-looking residuals - bool isFinalTurnForPlayerOfSimulation = false; + if (levelNumber == constants.levelCount) + isFinalTurnForPlayerOfSimulation = playerNumber > constants.decimalTurns; + else if (levelNumber == constants.levelCount + 1) + isFinalTurnForPlayerOfSimulation = playerNumber <= constants.decimalTurns; - if (levelNumber == constants.levelCount) - isFinalTurnForPlayerOfSimulation = playerNumber > constants.decimalTurns; - else if (levelNumber == constants.levelCount + 1) - isFinalTurnForPlayerOfSimulation = playerNumber <= constants.decimalTurns; + const bool isVeryFinalTurnOfSimulation = (constants.decimalTurns == 0 && levelNumber == constants.levelCount && playerNumber == constants.playerCount) || (levelNumber == constants.levelCount + 1 && playerNumber == constants.decimalTurns); - const bool isVeryFinalTurnOfSimulation = (constants.decimalTurns == 0 && levelNumber == constants.levelCount && playerNumber == constants.playerCount) || (levelNumber == constants.levelCount + 1 && playerNumber == constants.decimalTurns); + if (isFinalTurnForPlayerOfSimulation && !(constants.ignoreOppos && playerId != constants.startPlayerId)) + { + double residualAddend = game.currentPosition().calculatePlayerConsideration(move); + if (constants.isLogging) + message.logStream << message.xmlIndent << "" << endl; - if (isFinalTurnForPlayerOfSimulation && !(constants.ignoreOppos && playerId != constants.startPlayerId)) + if (isVeryFinalTurnOfSimulation) { - double residualAddend = game.currentPosition().calculatePlayerConsideration(move); - if (constants.isLogging) - message.logStream << message.xmlIndent << "" << endl; - - if (isVeryFinalTurnOfSimulation) - { - // experimental -- do shared resource considerations - // matter in a plied simulation? - - const double sharedResidual = game.currentPosition().calculateSharedConsideration(move); - residualAddend += sharedResidual; + // experimental -- do shared resource considerations + // matter in a plied simulation? - if (constants.isLogging && sharedResidual != 0) - message.logStream << message.xmlIndent << "" << endl; - } + const double sharedResidual = game.currentPosition().calculateSharedConsideration(move); + residualAddend += sharedResidual; - if (playerId == constants.startPlayerId) - residual += residualAddend; - else - residual -= residualAddend; + if (constants.isLogging && sharedResidual != 0) + message.logStream << message.xmlIndent << "" << endl; } - // commiting the move will account for deadwood again - // so avoid double counting from above. - move.score -= deadwoodScore; - game.setCandidate(move); + if (playerId == constants.startPlayerId) + residual += residualAddend; + else + residual -= residualAddend; + } - game.commitCandidate(!isVeryFinalTurnOfSimulation); + // commiting the move will account for deadwood again + // so avoid double counting from above. + move.score -= deadwoodScore; + game.setCandidate(move); - if (constants.isLogging) - { - message.xmlIndent = message.xmlIndent.substr(0, message.xmlIndent.length() - 1); - message.logStream << message.xmlIndent << "" << endl; - } + game.commitCandidate(!isVeryFinalTurnOfSimulation); + + if (constants.isLogging) + { + message.xmlIndent = message.xmlIndent.substr(0, message.xmlIndent.length() - 1); + message.logStream << message.xmlIndent << "" << endl; } } + } - message.residual = residual; - int spread = game.currentPosition().spread(constants.startPlayerId); - message.gameSpread = spread; + message.residual = residual; + int spread = game.currentPosition().spread(constants.startPlayerId); + message.gameSpread = spread; - if (game.currentPosition().gameOver()) - { - message.bogowin = false; - message.wins = spread > 0? 1 : spread == 0? 0.5 : 0; - } - else - { - message.bogowin = true; - if (game.currentPosition().currentPlayer().id() == constants.startPlayerId) - message.wins = QUACKLE_STRATEGY_PARAMETERS->bogowin((int)(spread + residual), game.currentPosition().bag().size() + QUACKLE_PARAMETERS->rackSize(), 0); - else - message.wins = 1.0 - QUACKLE_STRATEGY_PARAMETERS->bogowin((int)(-spread - residual), game.currentPosition().bag().size() + QUACKLE_PARAMETERS->rackSize(), 0); - } - - incorporateMessage(message); + if (game.currentPosition().gameOver()) + { + message.bogowin = false; + message.wins = spread > 0? 1 : spread == 0? 0.5 : 0; } - - if (isLogging()) + else { - m_xmlIndent = m_xmlIndent.substr(0, m_xmlIndent.length() - 1); - m_logfileStream << m_xmlIndent << "" << endl; + message.bogowin = true; + if (game.currentPosition().currentPlayer().id() == constants.startPlayerId) + message.wins = QUACKLE_STRATEGY_PARAMETERS->bogowin((int)(spread + residual), game.currentPosition().bag().size() + QUACKLE_PARAMETERS->rackSize(), 0); + else + message.wins = 1.0 - QUACKLE_STRATEGY_PARAMETERS->bogowin((int)(-spread - residual), game.currentPosition().bag().size() + QUACKLE_PARAMETERS->rackSize(), 0); } } diff --git a/sim.h b/sim.h index 0b3a7fb..ff618ef 100644 --- a/sim.h +++ b/sim.h @@ -275,6 +275,7 @@ public: // simulate one iteration void simulate(int plies); + static void simulateOnePosition(SimmedMoveMessage &message, const SimmedMoveConstants &constants); // Incoporate the results of a single simulation into the // cumulative results -- cgit v1.2.3