Replaying a game from a share code: seeds, decisions, and version changes
A player reports that the last round produced the wrong result. Replaying the game does not reproduce it. Randomness is only one possible cause: the report may be missing the starting configuration, earlier decisions, or the rules used at the time. This walkthrough uses KeepOnFirst’s career simulator to reconstruct an input, run the engine, and locate a divergence.
The source and downloadable engine are frozen at revision 1a6b177. The example was checked with Node.js 22.23.2 and requires no package installation or website server. It suits turn-based simulations and decision-driven bug reports. Real-time physics adds timestep and platform concerns that this example does not address.
Define what makes it the same run
The engine accepts a baseSeed, education, industry, career goal, and ordered choices. A seed controls the random sequence; it cannot reconstruct those other fields. Changing the starting industry can alter event weights and available decisions even with the same seed. A replay therefore needs the complete input and a fixed implementation of the rules.
const payload = {
version: 3,
baseSeed: 20260921,
education: 'publicUniversity',
industry: 'technology',
goal: 'balance',
choices: [0, 1, 2, 0, 1, 2, 0, 1, 2, 0],
};Each number in choices is an option index for that round, not a permanent semantic identifier. Zero means the first option in the event selected at that moment. Reordering options can change what zero means. The v3 prefix marks an evolving format; it does not guarantee that every future implementation will reproduce past outcomes.
Control the generator and its call order
The engine creates a mulberry32 generator inside simulateCareer and restarts it from the supplied seed for each simulation. Standard Math.random does not expose a seed-setting or reset API. This generator is used for reproducible event sampling, not cryptographic randomness.
The sequence remains useful only when the algorithm and consumption order stay stable. If a decorative animation draws from the same generator, one extra draw can change the next event. Keep presentation randomness separate from simulation randomness, and record the event ID selected at each step.
Run the downloadable example
Download the frozen engine, verifier, and expected output (ZIP)unzip replay-guide.zip
cd replay-guide
node verify.mjsThe archive contains the TypeScript source in engine.ts, executable JavaScript in engine.mjs, checks in verify.mjs, and the recorded output in result.json. A failed assertion terminates the process with a nonzero exit status. Run node verify.mjs > my-result.json to save your own output and compare it with result.json.
| Item | Result |
|---|---|
| Share code | v3-c29fd-0010120120120 |
| Fixture ending ID | foreverManager |
| Configurations | 64 seeds × 4 education profiles × 4 industries × 4 goals = 4,096 |
| Per configuration | Encode/decode round trip; identical replay fingerprint |
| Invalid inputs | 5 rejected cases |
| Environment | Node.js 22.23.2; engine revision 1a6b177 |
All 4,096 configurations use the same ten-decision sequence. This does not enumerate every possible path. The result supports repeatability for these inputs within this engine; it does not establish balance, reachability of every ending, or correctness on every device. The count is meaningful only alongside that scope.
Compare the trajectory, not just the ending
Two runs reaching the same ending can still differ internally. careerFingerprint includes event and effective choice IDs, commitment callbacks and changes, stats, flags, and ending factors. The result is serialized for comparison. A SHA-256 digest makes equality checks convenient; it does not prove that the game is fair.
import { simulateCareer, careerFingerprint } from './engine.mjs';
const first = simulateCareer(payload);
const second = simulateCareer(payload);
console.log(careerFingerprint(first) === careerFingerprint(second));
console.table(first.moments.map((m, turn) => ({
turn: turn + 1, event: m.nodeId, choice: m.choiceId,
}))); // Run after defining payload aboveWhen results diverge, locate the first round with a different event ID. Different events suggest examining the preceding state, weights, or random draws. Matching events with different choices point toward option indexing or availability checks. This engine substitutes an available option when the requested one is missing or locked, so preserve both the requested index and the effective choiceId in a bug report.
An accepted old code may be a migration, not a replay
The v2-to-v3 path retains the seed, education, and industry, but clears the goal and decisions. The player starts a fresh decision path. This is migration rather than historical replay. If a product must reproduce past matches, retain the old rules engine or sufficient event/state records, and record a rules version independently. A prefix alone cannot supply that history.
Validate a share code as user input: seed range, setup order, option values, and maximum decision count all matter. The format transports simulation inputs; it cannot prove that someone actually played the run. Anyone can construct a code. Leaderboards and rewards need a separate trust model.
Related: separating a puzzle game from its solver