Skip to content

Add seeded RNG support for deterministic randomization#139

Merged
James-Quigley merged 5 commits intodevfrom
claude/seeded-rng-testing-BK8Pa
Mar 26, 2026
Merged

Add seeded RNG support for deterministic randomization#139
James-Quigley merged 5 commits intodevfrom
claude/seeded-rng-testing-BK8Pa

Conversation

@James-Quigley
Copy link
Copy Markdown
Collaborator

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.random for backward compatibility.

Key Changes

  • New RNG abstraction: Added createRNG() function in utils.ts that creates seeded pseudo-random generators using the seedrandom library (ARC4-based)
  • Updated utility functions: Modified getRandomInt() and shuffle() to accept an optional rng parameter
  • Updated character generation: Modified getRandomCharacter() in alphaHelpers.ts to accept an optional rng parameter
  • Updated hexgrid functions: Modified getNewCellValues() and getPotentialWords() to accept and use an optional rng parameter
  • Updated bot logic: Modified getBotMove() in bot.ts to accept and use an optional rng parameter for all randomization calls
  • Updated GameManager: Modified constructor to accept an optional rng parameter and use it throughout the class for all random operations (capital assignment, cell value generation)
  • Comprehensive test coverage: Added 10 new tests in alphaHelpers.test.ts covering deterministic behavior, bounds checking, and correctness of seeded RNG across all modified functions
  • Dependencies: Added seedrandom package for seeded PRNG implementation

Implementation Details

  • All RNG parameters default to Math.random to maintain backward compatibility with existing code
  • The RNG type is defined as () => number for a simple, composable interface
  • String or numeric seeds can be passed to createRNG(), with string seeds being useful for storing alongside game records for deterministic replay
  • Tests use a constant seed (TEST_SEED = 12345) to verify deterministic behavior across multiple RNG instances

https://claude.ai/code/session_01DqArDCs8UuyZ5KiTdNMdMs

claude and others added 5 commits March 14, 2026 14:27
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
@James-Quigley James-Quigley merged commit 9000e2e into dev Mar 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants