Add seeded RNG support for deterministic randomization#139
Merged
James-Quigley merged 5 commits intodevfrom Mar 26, 2026
Merged
Add seeded RNG support for deterministic randomization#139James-Quigley merged 5 commits intodevfrom
James-Quigley merged 5 commits intodevfrom
Conversation
Introduce a mulberry32-based seeded PRNG (`createRNG`) in shared/utils.ts. Thread an optional `rng` parameter through all random-using functions: getRandomInt, shuffle, getRandomCharacter, getPotentialWords, getNewCellValues, and getBotMove. GameManager now creates a per-instance seeded RNG (random seed in production). Tests use a constant seed (12345) enabling deterministic, reproducible test scenarios. https://claude.ai/code/session_01DqArDCs8UuyZ5KiTdNMdMs
Swaps the hand-rolled mulberry32 PRNG in shared/utils.ts for the seedrandom package (ARC4-based, well-tested, ~13M weekly downloads). The seed type is widened from `number` to `string | number`, which makes it easy to seed a game's RNG from a string identifier (e.g. a game ID or nanoid) and store/replay that seed alongside the game record. https://claude.ai/code/session_01DqArDCs8UuyZ5KiTdNMdMs
The bot was falling back to Math.random instead of using the game's seeded RNG instance. Expose rng on GameManager and pass it through to getBotMove so all randomness in a game flows from the same seed.
- Add rngSeed and rngState to Game type and Mongoose schema so the PRNG sequence survives round-trips through the database - GameManager constructor restores from rngState, falls back to rngSeed, then a random bootstrap for legacy games - Snapshot rng.state() after every move, pass, and game creation - Fold removeMongoId into sanitizeGame so there is one function to call before sending any game data to a client; rngSeed/rngState are always stripped, _id fields are always removed - Add util.test.ts covering sanitizeGame behaviour
- Fix pass() socket emission sending pre-pass game state instead of the updated newGame, which left connected clients with stale turn info - Add restoreRNG test: advances RNG, snapshots state, records next values, restores and confirms the sequence resumes exactly
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR introduces seeded random number generation throughout the codebase to enable deterministic behavior for testing and game replay functionality. All randomization now accepts an optional RNG parameter that defaults to
Math.randomfor backward compatibility.Key Changes
createRNG()function inutils.tsthat creates seeded pseudo-random generators using the seedrandom library (ARC4-based)getRandomInt()andshuffle()to accept an optionalrngparametergetRandomCharacter()inalphaHelpers.tsto accept an optionalrngparametergetNewCellValues()andgetPotentialWords()to accept and use an optionalrngparametergetBotMove()inbot.tsto accept and use an optionalrngparameter for all randomization callsrngparameter and use it throughout the class for all random operations (capital assignment, cell value generation)alphaHelpers.test.tscovering deterministic behavior, bounds checking, and correctness of seeded RNG across all modified functionsseedrandompackage for seeded PRNG implementationImplementation Details
Math.randomto maintain backward compatibility with existing codeRNGtype is defined as() => numberfor a simple, composable interfacecreateRNG(), with string seeds being useful for storing alongside game records for deterministic replayTEST_SEED = 12345) to verify deterministic behavior across multiple RNG instanceshttps://claude.ai/code/session_01DqArDCs8UuyZ5KiTdNMdMs