JaffarPlus
High-performance best-first search optimizer for tool-assisted speedruns
Loading...
Searching...
No Matches
driver.hpp
Go to the documentation of this file.
1#pragma once
2
9#include "engine.hpp"
10#include "game.hpp"
11#include "runner.hpp"
12#include <chrono>
13#include <cstdlib>
14#include <fstream>
15#include <limits>
16#include <vector>
17
18namespace jaffarPlus
19{
20
31class Driver final
32
33{
34public:
48
58 Driver(const std::string& configFilePath, const nlohmann::json& config) : _configFilePath(configFilePath)
59 {
60 // Getting job identifier from the system timer
61 auto currentTime = std::chrono::system_clock::now();
62 _jobId = std::chrono::duration_cast<std::chrono::seconds>(currentTime.time_since_epoch()).count();
63
64 // Mutable working copy of the root config; each recognized key is consumed (popped) below, so any
65 // leftover key at the end is unrecognized (a typo or unsupported option) and is reported by name.
66 auto configRemaining = config;
67
68 // Getting driver configuration
69 auto driverConfig = jaffarCommon::json::popObject(configRemaining, "Driver Configuration");
70
71 // Getting end win delay config
72 _endOnFirstWinState = jaffarCommon::json::popBoolean(driverConfig, "End On First Win State");
73
74 // Getting maximum number of steps (zero is not established)
75 _maxSteps = jaffarCommon::json::popNumber<uint32_t>(driverConfig, "Max Steps");
76
77 // For testing purposes, the maximum number of steps can be overriden via environment variables
78 if (auto* value = std::getenv("JAFFAR_DRIVER_OVERRIDE_DRIVER_MAX_STEP")) _maxSteps = std::stoul(value);
79
80 // Getting intermediate result configuration
81 auto saveIntermediateResultsJs = jaffarCommon::json::popObject(driverConfig, "Save Intermediate Results");
82 _saveIntermediateResultsEnabled = jaffarCommon::json::popBoolean(saveIntermediateResultsJs, "Enabled");
83 _saveIntermediateFrequency = jaffarCommon::json::popNumber<float>(saveIntermediateResultsJs, "Frequency (s)");
84 _saveIntermediateBestSolutionPath = jaffarCommon::json::popString(saveIntermediateResultsJs, "Best Solution Path");
85 _saveIntermediateWorstSolutionPath = jaffarCommon::json::popString(saveIntermediateResultsJs, "Worst Solution Path");
86 jaffarCommon::json::checkEmpty(saveIntermediateResultsJs, "Driver Configuration > Save Intermediate Results");
87
88 // Optional reference reward floor: cancel the whole job if the BEST state's reward falls below a
89 // per-step reference reward trace (beyond tolerance). This is purely a driver-level stopping criterion
90 // and diagnostic -- it does NOT prune states (recoverable slower-then-faster lines are kept); it only
91 // detects the first step at which the leading edge falls behind the reference (e.g. a reference TAS) and
92 // stops, so the run isn't ground on once it can no longer keep pace. Reward must be monotone-comparable.
95 if (driverConfig.contains("Reference Reward Floor"))
96 {
97 auto refJs = jaffarCommon::json::popObject(driverConfig, "Reference Reward Floor");
98 _referenceFloorEnabled = jaffarCommon::json::popBoolean(refJs, "Enabled");
99 _referenceFloorTolerance = jaffarCommon::json::popNumber<float>(refJs, "Tolerance");
100 const auto refPath = jaffarCommon::json::popString(refJs, "Path");
101 jaffarCommon::json::checkEmpty(refJs, "Driver Configuration > Reference Reward Floor");
102 // The trace file is a runtime artifact: only open it for a real run. Under --dryRun (JAFFAR_IS_DRY_RUN)
103 // we validate the config shape but skip the read, matching how ROM / initial-solution files are deferred
104 // to engine init (which dryRun never reaches) -- so config validation does not depend on the cwd-relative
105 // trace being present next to the build directory.
106 if (_referenceFloorEnabled && std::getenv("JAFFAR_IS_DRY_RUN") == nullptr)
107 {
108 std::ifstream f(refPath);
109 if (f.good() == false) JAFFAR_THROW_RUNTIME("[ERROR] Could not open 'Reference Reward Floor' > 'Path': '%s'\n", refPath.c_str());
110 float v;
111 while (f >> v) _referenceReward.push_back(v);
112 jaffarCommon::logger::log("[J+] Reference reward floor enabled: %lu steps loaded, tolerance %.4f\n", _referenceReward.size(), _referenceFloorTolerance);
113 }
114 }
115
116 jaffarCommon::json::checkEmpty(driverConfig, "Driver Configuration");
117
118 // Getting component configurations (consumed from the root so the root check below can flag strays)
119 auto emulatorConfig = jaffarCommon::json::popObject(configRemaining, "Emulator Configuration");
120 auto gameConfig = jaffarCommon::json::popObject(configRemaining, "Game Configuration");
121 auto runnerConfig = jaffarCommon::json::popObject(configRemaining, "Runner Configuration");
122 auto engineConfig = jaffarCommon::json::popObject(configRemaining, "Engine Configuration");
123
124 // Any remaining top-level key is unrecognized
125 jaffarCommon::json::checkEmpty(configRemaining, "configuration root");
126
127 // Creating runner from the configuration
128 _runner = jaffarPlus::Runner::getRunner(emulatorConfig, gameConfig, runnerConfig);
129
130 // Creating engine from the configuration
131 _engine = std::make_unique<Engine>(emulatorConfig, gameConfig, runnerConfig, engineConfig);
132 }
133
136
144 {
145 // Resetting step counter
146 _currentStep = 0;
147
148 // Resetting win state counter
149 _winStatesFound = 0;
150
151 // Resetting best states reward
152 _bestWinStateReward = -std::numeric_limits<float>::infinity();
153 _bestStateReward = -std::numeric_limits<float>::infinity();
154 _bestStateFloorReward = -std::numeric_limits<float>::infinity();
155
156 // Resetting worst state reward
157 _worstStateReward = std::numeric_limits<float>::infinity();
158
159 // Initializing runner
160 _runner->initialize();
161
162 // Initializing engine
163 _engine->initialize();
164
165 // Allocating space for the current best and worst states. These are standalone snapshots outside the
166 // NUMA slabs, so they hold the FULL self-contained state ([hot]+[history]), not just the hot slot.
167 _stateSize = _engine->getFullStateSize();
170 }
171
182 int run()
183 {
184 // Internal flag to indicate we are still running
185 _hasFinished = false;
186
187 // If using ncurses, initialize terminal now
188 jaffarCommon::logger::initializeTerminal();
189
190 // Storage for the exit
191 exitReason_t exitReason;
192
193 // Starting intermediate result saving thread
194 std::thread intermediateResultSaverThread;
195 if (_saveIntermediateResultsEnabled == true) intermediateResultSaverThread = std::thread([this]() { intermediateResultSaveLoop(); });
196
197 // Running engine until a termination point
198 while (true)
199 {
200 // If found winning state, report it now
201 if (_endOnFirstWinState && _engine->getWinStatesFound() > 0)
202 {
203 exitReason = exitReason_t::winStateFound;
204 break;
205 }
206
207 // If ran out of states, finish now
208 if (_engine->getStateCount() == 0)
209 {
210 exitReason = _engine->getWinStatesFound() > 0 ? exitReason_t::winStateFound : exitReason_t::outOfStates;
211 break;
212 }
213
214 // If maximum step established and reached, finish now
215 if (_maxSteps > 0 && _currentStep >= _maxSteps)
216 {
217 if (_winStatesFound > 0) exitReason = exitReason_t::winStateFound;
219 break;
220 }
221
222 // Updating best and worst states
225
226 // Reference reward floor: if the best leading edge has fallen below the reference at this step, the run
227 // can no longer keep pace with the reference -- cancel now (purely a stop signal; nothing was pruned).
229 {
230 jaffarCommon::logger::log("[J+] Best (%.6f) fell below reference floor (%.6f, tol %.4f) at step %lu by %.6f -- cancelling.\n", _bestStateFloorReward,
234 break;
235 }
236
237 // Input-history backing guard: the "Trie" strategy's shared node pool grows ~ live-states x depth
238 // toward a hard ceiling (getInputHistoryMaxMemoryBytes). Stop GRACEFULLY at a high-water mark -- or, as
239 // a backstop, if a worker already latched the pool exhausted -- so the search saves its best result and
240 // exits cleanly, instead of a worker hitting the ceiling mid-step and terminating the whole process.
241 // No-op for None/Raw (ceiling 0): their history lives in the StateDb slot, already bounded.
242 {
243 const size_t ihCeiling = _engine->getInputHistoryMaxMemoryBytes();
244 if (ihCeiling > 0)
245 {
246 const size_t ihNow = _engine->getInputHistoryApproxMemoryBytes();
247 const bool exhausted = _engine->isInputHistoryExhausted();
248 if (exhausted || ihNow >= (size_t)((double)ihCeiling * _inputHistoryCapacityWatermark))
249 {
250 const double GB = 1024.0 * 1024.0 * 1024.0;
251 jaffarCommon::logger::log("[J+] Input-history trie at %.1f / %.1f GB (%.0f%% of its hard ceiling)%s at step %lu -- stopping "
252 "gracefully. The Trie node pool grows ~ live-states x depth and cannot be enlarged past RAM; switch "
253 "Store Input History Type to \"Raw\" (bounded by 'State Database/Max Size (Mb)'), or lower the State "
254 "DB size so fewer live states slow the trie's growth.\n",
255 (double)ihNow / GB, (double)ihCeiling / GB, 100.0 * (double)ihNow / (double)ihCeiling, exhausted ? " (pool exhausted)" : "", _currentStep);
257 break;
258 }
259 }
260 }
261
262 // Storing manually saved solution, if required
264
265 // Printing information
266 printInfo();
267
268 // Running engine step
269 _engine->runStep();
270
271 // Summing amount of win states found
272 _winStatesFound = _engine->getWinStatesFound();
273
274 // Increasing step counter
275 _currentStep++;
276 }
277
278 // Setting finalized flag
279 _hasFinished = true;
280
281 // Waiting for saver thread
282 if (_saveIntermediateResultsEnabled == true) intermediateResultSaverThread.join();
283
284 // If using ncurses, terminate terminal now
285 jaffarCommon::logger::finalizeTerminal();
286
287 // Updating and storing best states
290
291 // Final report
292 printInfo();
293
294 // Otherwise return the reason why we stopped
295 return exitReason;
296 }
297
305 {
306 auto manualSaveSolution = _engine->getManualSaveSolution();
307
308 if (manualSaveSolution.path != "")
309 {
310 // Loading the saved state into the runner (its depth was recorded at capture; set it before
311 // deserializing so the trie can rebuild and the solution renders to the right length).
312 _runner->setStepCount(manualSaveSolution.stepCount);
313 _engine->getStateDb()->loadStateIntoRunner(*_runner, manualSaveSolution.stateData);
314
315 // Saving manually stored solution
316 std::string solutionData = _runner->getInputHistoryString();
317 jaffarCommon::file::saveStringToFile(solutionData, manualSaveSolution.path);
318 }
319 }
320
328 {
329 // Making sure the main thread is not currently writing
331
332 // Saving best solution and state
333 std::string jobSuffix = std::string(".") + std::to_string(_jobId);
334 std::string stepSuffix = std::string(".") + std::to_string(_currentStep);
335
336 // Saving files with standard name
337 if (_saveIntermediateBestSolutionPath != "") jaffarCommon::file::saveStringToFile(_bestSolutionStorage, _saveIntermediateBestSolutionPath);
338
339 // Saving files with a job suffix and step number
340 if (_saveIntermediateBestSolutionPath != "") jaffarCommon::file::saveStringToFile(_bestSolutionStorage, _saveIntermediateBestSolutionPath + jobSuffix + stepSuffix);
341
342 // Making sure the main thread is not currently writing
344 }
345
353 {
354 // Making sure the main thread is not currently writing
356
357 // Saving best solution and state
358 std::string jobSuffix = std::string(".") + std::to_string(_jobId);
359
360 // Saving best solution and state
362
363 // Saving best solution and state
364 if (_saveIntermediateWorstSolutionPath != "") jaffarCommon::file::saveStringToFile(_worstSolutionStorage, _saveIntermediateWorstSolutionPath + jobSuffix);
365
366 // Making sure the main thread is not currently writing
368 }
369
378 {
379 // Timer for saving to file
380 auto lastSaveTime = jaffarCommon::timing::now();
381
382 // Run loop while the driver is still running
383 while (_hasFinished == false)
384 {
385 // Sleeping for 100ms intervals to prevent excessive overheads
386 usleep(100000);
387
388 // Getting time elapsed since last save
389 auto currentTime = jaffarCommon::timing::now();
390 auto timeElapsedSinceLastSave = jaffarCommon::timing::timeDeltaSeconds(currentTime, lastSaveTime);
391
392 // Checking if we need to save best state
393 if (timeElapsedSinceLastSave > _saveIntermediateFrequency && _currentStep > 1)
394 {
395 // Saving worst and best state information
398
399 // Resetting timer
400 lastSaveTime = jaffarCommon::timing::now();
401 }
402 }
403 }
404
413 {
414 // If no states in database, there is nothing to update
415 if (_engine->getStateDb()->getStateCount() == 0) return;
416
417 // Making sure the intermediate result thread is not currently reading
419
420 // Getting worst state so far
421 auto worstState = _engine->getStateDb()->getWorstState();
422
423 // Saving worst state into the storage (gather hot slab slot + its cold history into the full buffer)
424 _engine->getStateDb()->captureSlotToBuffer(worstState, _worstStateStorage.data());
425
426 // The worst state belongs to the current frontier, so its depth is the current search step (the step
427 // counter is not stored per-state). Set it before loading so the solution renders to the right length.
428 _runner->setSearchStep(_currentStep);
429 _engine->getStateDb()->loadStateIntoRunner(*_runner, _worstStateStorage.data());
430
431 // Saving worst solution into storage
432 _worstSolutionStorage = _runner->getInputHistoryString();
433
434 // Updating worst state reward
435 _worstStateReward = _runner->getGame()->getReward();
436
437 // Making sure the intermediate result thread is not currently reading
439 }
440
451 {
452 // If no states in database and no win states, there is nothing to update
453 if (_engine->getStateDb()->getStateCount() == 0 && _winStatesFound == 0) return;
454
455 // Making sure the intermediate result thread is not currently reading
457
458 // If we haven't found any winning state, simply use the currently best state
459 if (_winStatesFound == 0)
460 {
461 // Getting best state so far
462 auto bestState = _engine->getStateDb()->getBestState();
463
464 // Saving best state into the storage (gather hot slab slot + its cold history into the full buffer)
465 if (bestState != nullptr) _engine->getStateDb()->captureSlotToBuffer(bestState, _bestStateStorage.data());
466 }
467
468 // If we have found a winning state in this step that improves on the current best, save it now
469 if (_engine->getWinStatesFound() > 0)
470 {
471 // Getting best win state (best reward) for the current step
472 auto winStateEntry = _engine->getStepBestWinState();
473
474 // If the reward if better than the current best, then make it the new best state
475 if (winStateEntry.reward > _bestWinStateReward)
476 {
477 // Saving new best
478 _bestWinStateReward = winStateEntry.reward;
479
480 // Saving win state into the storage (and remembering its depth for solution rendering)
481 memcpy(_bestStateStorage.data(), winStateEntry.stateData, _stateSize);
482 _bestWinStateStepCount = winStateEntry.stepCount;
483 }
484 }
485
486 // Set the runner's step counter to the best state's depth before loading (the counter is not stored
487 // per-state): a win's depth was recorded at capture; an ordinary best belongs to the current frontier.
488 if (_winStatesFound > 0)
489 _runner->setStepCount(_bestWinStateStepCount);
490 else
491 _runner->setSearchStep(_currentStep);
492 _bestStateStepCount = _runner->getStepCount(); // remembered so the printInfo reload uses the same depth
493 _engine->getStateDb()->loadStateIntoRunner(*_runner, _bestStateStorage.data());
494
495 // Updating best state reward
496 _bestStateReward = _runner->getGame()->getReward();
497 _bestStateFloorReward = _runner->getGame()->getFloorReward(); // un-biased position for the Reference Reward Floor (decoupled from the magnet)
498
499 // Storing best solution
500 _bestSolutionStorage = _runner->getInputHistoryString();
501
502 // Making sure the intermediate result thread is not currently reading
504 }
505
514 {
515 // If using ncurses, clear terminal before printing the information for this step
516 jaffarCommon::logger::clearTerminal();
517
518 // Printing information
519 jaffarCommon::logger::log("[J+] Job Id: %lu\n", _jobId);
520 jaffarCommon::logger::log("[J+] Script File: '%s'\n", _configFilePath.c_str());
521 jaffarCommon::logger::log("[J+] Emulator Name: '%s'\n", _runner->getGame()->getEmulator()->getName().c_str());
522 jaffarCommon::logger::log("[J+] Game Name: '%s'\n", _runner->getGame()->getName().c_str());
523 jaffarCommon::logger::log("[J+] Current Step #: %lu", _currentStep);
524 if (_maxSteps > 0) jaffarCommon::logger::log(" (Max: %lu)", _maxSteps);
525 jaffarCommon::logger::log("\n");
526
527 if (_winStatesFound == 0)
528 jaffarCommon::logger::log("[J+] Current Reward (Best / Worst): %.6f / %.6f (Diff: %.6f)\n", _bestStateReward, _worstStateReward,
530
531 if (_winStatesFound > 0)
532 jaffarCommon::logger::log("[J+] Current Reward (Win / Worst): %.6f / %.6f (Diff: %.6f)\n", _bestStateReward, _worstStateReward,
534
535 // When a reference reward floor is active, show the reference reward at this step and how the best
536 // compares to it (positive = best ahead of the reference, negative = best behind), for easy human review.
538 {
539 if (_currentStep < _referenceReward.size())
540 jaffarCommon::logger::log("[J+] Reference Reward (Best - Ref): %.6f (Best %+.6f, tol %.4f)\n", _referenceReward[_currentStep],
542 else
543 jaffarCommon::logger::log("[J+] Reference Reward (Best - Ref): (none: step beyond reference trace)\n");
544 }
545
546 // Printing engine information
547 jaffarCommon::logger::log("[J+] Engine Information: \n");
548 _engine->printInfo();
549
550 // Loading best state into runner (same depth updateBestState() used, so the trie rebuilds correctly)
551 _runner->setStepCount(_bestStateStepCount);
552 _engine->getStateDb()->loadStateIntoRunner(*_runner, _bestStateStorage.data());
553
554 // Printing best state information to screen
555 jaffarCommon::logger::log("[J+] Runner Information (Best State): \n");
556 _runner->printInfo();
557 jaffarCommon::logger::log("[J+] Game Information (Best State): \n");
558 _runner->getGame()->printInfo();
559 jaffarCommon::logger::log("[J+] Emulator Information (Best State): \n");
560 _runner->getGame()->getEmulator()->printInfo();
561
562 // Division rule to separate different steps
563 jaffarCommon::logger::log("[J+] --------------------------------------------------------------\n");
564
565 // If using ncurses, refresh terminal now
566 jaffarCommon::logger::refreshTerminal();
567 }
568
575 static std::unique_ptr<Driver> getDriver(const std::string& configFilePath, const nlohmann::json& config)
576 {
577 // Creating new engine
578 auto d = std::make_unique<Driver>(configFilePath, config);
579
580 // Returning engine
581 return d;
582 }
583
585 size_t getCurrentStep() { return _currentStep; }
586
587private:
588 const std::string _configFilePath;
589
590 std::unique_ptr<Engine> _engine;
591
592 std::unique_ptr<Runner> _runner;
593
594 size_t _jobId;
595
596 size_t _maxSteps;
597
599
601
603
607
610
612
615 std::vector<float> _referenceReward;
616
622
623 std::string _bestStateStorage;
624
625 std::string _worstStateStorage;
626
628
630
631 size_t _stateSize;
632
633 __volatile__ bool _hasFinished;
634
636
638
640
642
644
646};
647
648} // namespace jaffarPlus
Owns and runs the engine's step loop and reports how the run ended.
Definition driver.hpp:33
std::mutex _updateIntermediateResultMutex
Guards intermediate result storage between the main and saver threads.
Definition driver.hpp:645
size_t _bestWinStateStepCount
Depth of the best win state (recorded at capture; the count is not stored per-state).
Definition driver.hpp:605
float _saveIntermediateFrequency
Minimum interval, in seconds, between intermediate result saves.
Definition driver.hpp:639
std::string _bestSolutionStorage
Storage for the current best solution's input history.
Definition driver.hpp:627
size_t _winStatesFound
Total number of win states found so far.
Definition driver.hpp:602
float _worstStateReward
Reward for the worst state found so far.
Definition driver.hpp:611
int run()
Runs the engine's step loop until a termination condition is met.
Definition driver.hpp:182
size_t getCurrentStep()
Returns the current step counter.
Definition driver.hpp:585
exitReason_t
Reason the run loop terminated, returned by run.
Definition driver.hpp:37
@ outOfStates
Engine ran out of states.
Definition driver.hpp:40
@ bestBelowReference
The best state's reward fell below the reference reward floor at this step.
Definition driver.hpp:44
@ winStateFound
Found a win state.
Definition driver.hpp:38
@ maximumStepReached
Maximum step reached.
Definition driver.hpp:42
@ inputHistoryNearCapacity
The shared input-history trie neared/hit its hard memory ceiling.
Definition driver.hpp:46
size_t _jobId
Job identifier (derived from system time) distinguishing intermediate values between jobs.
Definition driver.hpp:594
std::string _bestStateStorage
Storage for the current best (win or otherwise) state.
Definition driver.hpp:623
std::unique_ptr< Runner > _runner
Runner used for printing information and saving partial results.
Definition driver.hpp:592
float _bestStateFloorReward
Un-biased progress (position) reward of the best state; used for the Reference Reward Floor compariso...
Definition driver.hpp:609
float _referenceFloorTolerance
Allowed shortfall of best below the reference per step.
Definition driver.hpp:614
std::vector< float > _referenceReward
Per-step reference reward floor (index = step).
Definition driver.hpp:615
size_t _currentStep
Counter for the number of steps performed; the initial state counts as step zero.
Definition driver.hpp:598
void storeManualSaveSolution()
Saves a solution explicitly requested by the engine, if any.
Definition driver.hpp:304
~Driver()
Destroys the driver.
Definition driver.hpp:135
void updateWorstState()
Refreshes the tracked worst state, its solution, and its reward.
Definition driver.hpp:412
__volatile__ bool _hasFinished
Internal flag indicating the driver has finished.
Definition driver.hpp:633
bool _referenceFloorEnabled
Whether the reference reward floor cancel is active.
Definition driver.hpp:613
static std::unique_ptr< Driver > getDriver(const std::string &configFilePath, const nlohmann::json &config)
Factory that constructs a driver from configuration.
Definition driver.hpp:575
float _bestStateReward
Ranking reward (magnet-biased) for the best state found so far; drives eviction/display.
Definition driver.hpp:608
void printInfo()
Prints the current state of execution to the logger.
Definition driver.hpp:513
std::string _saveIntermediateBestSolutionPath
Path to store the best solution found so far.
Definition driver.hpp:641
bool _endOnFirstWinState
Whether to end the run on the first win state found.
Definition driver.hpp:600
size_t _maxSteps
Maximum number of steps (zero = not established).
Definition driver.hpp:596
void updateBestState()
Refreshes the tracked best state, its solution, and its reward.
Definition driver.hpp:450
std::unique_ptr< Engine > _engine
Pointer to the internal Jaffar engine.
Definition driver.hpp:590
void initialize()
Resets the execution back to the starting point.
Definition driver.hpp:143
void saveBestStateInformation()
Writes the current best solution to file, under the mutex.
Definition driver.hpp:327
std::string _worstSolutionStorage
Storage for the current worst solution's input history.
Definition driver.hpp:629
bool _saveIntermediateResultsEnabled
Whether to store intermediate results at all.
Definition driver.hpp:637
void saveWorstStateInformation()
Writes the current worst solution to file, under the mutex.
Definition driver.hpp:352
float _bestWinStateReward
Reward for the best win state found so far.
Definition driver.hpp:604
size_t _stateSize
Storage size of a runner state.
Definition driver.hpp:631
Driver(const std::string &configFilePath, const nlohmann::json &config)
Constructs the driver and its engine/runner from the parsed configuration.
Definition driver.hpp:58
std::string _saveIntermediateWorstSolutionPath
Path to store the worst solution found so far.
Definition driver.hpp:643
void intermediateResultSaveLoop()
Background loop that periodically saves best and worst solutions to file.
Definition driver.hpp:377
std::string _worstStateStorage
Storage for the current worst (win or otherwise) state.
Definition driver.hpp:625
size_t _bestStateStepCount
Depth of the current best state, set by updateBestState() and reused by the printInfo reload.
Definition driver.hpp:606
const std::string _configFilePath
Path to the config file, kept for reference.
Definition driver.hpp:588
double _inputHistoryCapacityWatermark
Fraction of the input-history trie's hard ceiling at which the run stops gracefully (high-water mark)...
Definition driver.hpp:621
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.
Definition runner.hpp:527
Parallel breadth-first search engine that expands game states step by step, deduplicating via a hash ...
Abstract base for a JaffarPlus game: wraps an emulator, registers game properties,...
Drives a Game forward one input at a time, managing the allowed/candidate input sets,...