14#include <gameList.hpp>
15#include <jaffarCommon/bitwise.hpp>
16#include <jaffarCommon/deserializers/contiguous.hpp>
17#include <jaffarCommon/hash.hpp>
18#include <jaffarCommon/json.hpp>
19#include <jaffarCommon/logger.hpp>
20#include <jaffarCommon/serializers/contiguous.hpp>
22#include <unordered_set>
49 Runner(std::unique_ptr<Game>& game,
const nlohmann::json& config) :
_game(std::move(game))
52 auto configRemaining = config;
54 _hashStepTolerance = jaffarCommon::json::popNumber<uint32_t>(configRemaining,
"Hash Step Tolerance");
58 _inputHistoryConfig = jaffarCommon::json::popObject(configRemaining,
"Store Input History");
61 _allowedInputSetsJs = jaffarCommon::json::popArray<nlohmann::json>(configRemaining,
"Allowed Input Sets");
64 _showAllowedInputs = jaffarCommon::json::popBoolean(configRemaining,
"Show Allowed Inputs");
67 _showEmptyInputSlots = jaffarCommon::json::popBoolean(configRemaining,
"Show Empty Input Slots");
73 _candidateInputSetsJs = jaffarCommon::json::popArray<nlohmann::json>(configRemaining,
"Candidate Input Sets");
76 auto frameskipJs = jaffarCommon::json::popObject(configRemaining,
"Frameskip");
77 _frameskipRate = jaffarCommon::json::popNumber<size_t>(frameskipJs,
"Rate");
80 jaffarCommon::json::checkEmpty(frameskipJs,
"Runner Configuration > Frameskip");
86 jaffarCommon::json::checkEmpty(configRemaining,
"Runner Configuration");
100 if (
_isInitialized ==
true) JAFFAR_THROW_LOGIC(
"This runner instance was already initialized");
103 if (
_game->isInitialized() ==
false)
_game->initialize();
134 __INLINE__
void setInputHistoryBacking(
const std::shared_ptr<void>& backing,
const uint32_t shardId,
const uint32_t numShards)
160 auto inputSet = std::make_unique<InputSet>();
163 const auto& conditions = jaffarCommon::json::getArray<nlohmann::json>(inputSetJs,
"Conditions");
166 const auto& inputsJs = jaffarCommon::json::getArray<std::string>(inputSetJs,
"Inputs");
169 for (
const auto& condition : conditions) inputSet->addCondition(
_game->parseCondition(condition));
172 for (
const auto& input : inputsJs) inputSet->addInput(
registerInput(input));
178 inputSet->setStopInputEvaluationFlag(jaffarCommon::json::getBoolean(inputSetJs,
"Stop Input Evaluation"));
195 const auto inputHash = jaffarCommon::hash::hashString(input);
222 std::vector<InputSet::inputIndex_t>
getInputsFromInputSets(
const std::vector<std::unique_ptr<InputSet>>& inputSets)
const
225 std::vector<InputSet::inputIndex_t> possibleInputs;
228 for (
const auto& inputSet : inputSets)
229 if (inputSet->evaluate() ==
true)
231 possibleInputs.insert(possibleInputs.end(), inputSet->getInputIndexes().begin(), inputSet->getInputIndexes().end());
234 bool stopEvaluating = inputSet->getStopInputEvaluationFlag();
237 if (stopEvaluating)
break;
241 return possibleInputs;
257 _game->getAdditionalAllowedInputs(allowedInputs);
259 return allowedInputs;
277 const auto inputHash = jaffarCommon::hash::hashString(input);
281 if (it ==
_inputHashMap.end()) JAFFAR_THROW_LOGIC(
"[ERROR] Input '%s' provided but has not been registered as allowed input first.\n", input.c_str());
282 const auto inputIndex = it->second;
295 const auto inputHash = jaffarCommon::hash::hashString(inputString);
315 _game->advanceState(inputIdx);
325 _game->advanceState(frameskipInputIdx);
364 _game->serializeState(serializer);
374 _game->deserializeState(deserializer);
386 jaffarCommon::serializer::Contiguous s;
388 return s.getOutputSize();
398 __INLINE__
void serializeHotState(jaffarCommon::serializer::Base& serializer)
const {
_game->serializeState(serializer); }
411 jaffarCommon::serializer::Contiguous s;
413 return s.getOutputSize();
432 MetroHash128 hashEngine;
438 hashEngine.Update(hashStepToleranceStage);
441 _game->computeHash(hashEngine);
443 jaffarCommon::hash::hash_t result;
444 hashEngine.Finalize(
reinterpret_cast<uint8_t*
>(&result));
475 const auto hash = jaffarCommon::hash::hashToString(
computeHash());
481 jaffarCommon::logger::log(
"[J+] + Input History Type: %s (cold %lu B, full %lu B)\n", inputHistory::getType(
_inputHistoryConfig).c_str(),
485 jaffarCommon::logger::log(
"[J+] + Current Input Count: %lu\n",
_stepCount);
486 jaffarCommon::logger::log(
"[J+] + Hash: %s\n", hash.c_str());
487 jaffarCommon::logger::log(
"[J+] + Hash Step Tolerance Stage: %u / %u\n", hashStepToleranceStage,
_hashStepTolerance);
499 jaffarCommon::logger::log(
"[J+] + Allowed Inputs:\n");
501 size_t currentInputIdx = 0;
502 for (
const auto inputIdx : possibleInputs)
504 jaffarCommon::logger::log(
"[J+] + '%s'\n",
_inputStringMap.at(inputIdx).c_str());
509 for (; currentInputIdx <
_largestInputSetSize; currentInputIdx++) jaffarCommon::logger::log(
"[J+] + ----- \n");
527 static std::unique_ptr<Runner>
getRunner(
const nlohmann::json& emulatorConfig,
const nlohmann::json& gameConfig,
const nlohmann::json& runnerConfig)
533 auto r = std::make_unique<Runner>(game, runnerConfig);
Abstract base class for a JaffarPlus game.
static std::unique_ptr< Game > getGame(const nlohmann::json &emulatorConfig, const nlohmann::json &gameConfig)
Factory that constructs the concrete game matching the given configuration.
Abstract strategy for remembering the input path that produced each search state.
Owns a Game instance and advances it according to configured inputs.
std::unique_ptr< InputSet > parseInputSet(const nlohmann::json &inputSetJs)
Builds an InputSet from its JSON description.
std::unique_ptr< InputHistory > _inputHistory
The configured strategy instance (built in initialize()).
void setStepCount(const size_t stepCount)
Sets the step counter directly (its input-count value).
InputSet::inputIndex_t _frameskipCustomInputIdx
Resolved index of the custom frameskip input, computed once at initialize() so advanceState() does no...
bool _showAllowedInputs
Whether allowed inputs are printed in printInfo.
InputSet::inputIndex_t registerInput(const std::string &input)
Registers an input string and returns its numeric index.
bool _isInitialized
Whether the runner has been initialized.
uint32_t getHashStepToleranceStage() const
Returns the current hash-step-tolerance stage (current input count modulo tolerance + 1).
const auto getAllowedInputs() const
Returns the inputs currently allowed for the game's state.
bool _showEmptyInputSlots
Whether placeholders are printed for unused input slots.
std::shared_ptr< void > _historyBacking
Shared backing (e.g. the one trie), injected by the engine or owned privately.
size_t _frameskipRate
Number of frames to skip after each applied input.
std::string getInputStringFromIndex(const InputSet::inputIndex_t input)
Returns the input string registered for an input index.
size_t getStateSize() const
Computes the size in bytes of the serialized runner state.
void initialize()
Initializes the runner: initializes the game, parses input sets, and registers inputs.
void printInfo() const
Logs runner state information.
uint32_t _hashStepTolerance
Hash step tolerance, used to derive the hash-step-tolerance stage.
std::unique_ptr< Game > _game
The owned game instance driven by the runner.
uint32_t _historyShardId
This runner's contention-free free-list shard (its worker thread id).
jaffarPlus::InputSet::inputIndex_t getInputIndex(const std::string &input) const
Looks up the index registered for an input string.
std::map< InputSet::inputIndex_t, std::string > _inputStringMap
Maps an input index back to its input string.
Runner(std::unique_ptr< Game > &game, const nlohmann::json &config)
Constructs a runner that takes ownership of game and reads its settings from config.
std::vector< InputSet::inputIndex_t > getInputsFromInputSets(const std::vector< std::unique_ptr< InputSet > > &inputSets) const
Collects the input indexes of every input set whose conditions currently evaluate to true.
size_t getHistorySize() const
Returns the serialized size of the cold input-history "path", in bytes.
std::vector< std::unique_ptr< InputSet > > _allowedInputSets
Parsed allowed input sets.
static std::unique_ptr< Runner > getRunner(const nlohmann::json &emulatorConfig, const nlohmann::json &gameConfig, const nlohmann::json &runnerConfig)
Creates a runner from the emulator, game and runner configurations.
void advanceState(const InputSet::inputIndex_t inputIdx)
Advances the game by one input, then by the configured number of frameskip frames.
size_t _stepCount
Inputs applied so far (the state's depth).
const nlohmann::json & getInputHistoryConfig() const
The configured "Store Input History" object (used by the engine to build the shared backing).
void setSearchStep(const size_t searchStep)
Sets the step counter from a search step.
void setInputHistoryBacking(const std::shared_ptr< void > &backing, const uint32_t shardId, const uint32_t numShards)
Injects the shared input-history backing (e.g.
size_t _largestInputSetSize
Largest input set size detected (used for output formatting).
jaffarCommon::hash::hash_t computeHash() const
Computes a hash of the current runner state.
Game * getGame() const
Returns a pointer to the owned game instance.
std::vector< nlohmann::json > _allowedInputSetsJs
JSON descriptions of the allowed input sets (parsed at initialization).
std::map< jaffarCommon::hash::hash_t, InputSet::inputIndex_t > _inputHashMap
Maps an input string's hash to its index.
void serializeState(jaffarCommon::serializer::Base &serializer) const
Serializes the runner state: the game state, the input history, and the input counter.
std::string getInputHistoryString() const
Builds a newline-separated string of the recorded input history.
void deserializeHotState(jaffarCommon::deserializer::Base &deserializer)
Restores only the hot game+emulator state from deserializer.
std::string _frameskipCustomInput
The custom input string to apply on skipped frames.
bool isInitialized() const
Returns whether the runner has been initialized.
bool _frameskipUseCustomInput
Whether skipped frames use the custom input; false repeats the applied input.
void deserializeHistory(jaffarCommon::deserializer::Base &deserializer)
Restores only the cold input-history "path" from deserializer.
const auto getCandidateInputs() const
Returns the inputs currently allowed by the candidate input sets.
bool _bypassHashCalculation
Whether computeHash returns the game's direct hash instead of hashing via MetroHash128.
size_t getHotStateSize() const
Returns the serialized size of the hot game+emulator state, in bytes.
bool _testCandidateInputs
Whether candidate input sets are parsed and tested.
void serializeHistory(jaffarCommon::serializer::Base &serializer) const
Serializes only the cold input-history "path" (written once at state creation, read at solution time)...
InputHistory * getInputHistory() const
The input-history strategy in use (for the StateDb's per-slot manager operations).
uint32_t _historyNumShards
Shard count of the backing (engine: threadCount+1; standalone: 2).
bool isInputRegistered(const std::string &inputString)
Reports whether an input string has been registered.
void serializeHotState(jaffarCommon::serializer::Base &serializer) const
Serializes only the hot game+emulator state (what the search reads every step) into serializer.
void pushInput(const InputSet::inputIndex_t inputIdx)
Records an applied input into the input history at the current step, and advances the step counter.
InputSet::inputIndex_t _maxInputIndex
One past the highest registered input index; determines the input-history encoding width.
nlohmann::json _inputHistoryConfig
The "Store Input History" config object (selects None/Raw/Trie).
std::vector< nlohmann::json > _candidateInputSetsJs
JSON descriptions of the candidate input sets (parsed at initialization).
std::vector< std::unique_ptr< InputSet > > _candidateInputSets
Parsed candidate input sets.
void deserializeState(jaffarCommon::deserializer::Base &deserializer)
Restores the runner state: the game state, the input history, and the input counter.
size_t getStepCount() const
Returns the current step counter (number of inputs applied / the state's depth).
Abstract base for a JaffarPlus game: wraps an emulator, registers game properties,...
Builds the configured InputHistory strategy from the "Store Input History" config object.