reikhelm/.dev/2026-05-28-procgen-map-core/tasks/04-rng.md
Parley Hatch 08e99e38a3 chore: scaffold reikhelm cargo workspace (core + viz crates)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-28 20:56:24 -06:00

2.5 KiB

Task 4: Deterministic RNG

Depends on: Task 1

Context Files

  • .dev/2026-05-28-procgen-map-core/spec/design.md — §4.4 (RNG) and §7 (determinism contract).
  • reikhelm-core/src/lib.rs — append the module declaration here.

Files

  • Create: reikhelm-core/src/rng.rsRng.
  • Modify: reikhelm-core/src/lib.rs — append pub mod rng;.

What to Build

A deterministic RNG wrapping rand_chacha::ChaCha8Rng (spec §4.4). Reproducible across machines — never thread_rng. The keystone feature is fork: deriving an independent child stream from a stable basis (the originating seed) plus a label, so that forks are independent of draw order and of each other. The Pipeline (Task 6) calls fork once per pass; this is what makes editing the pipeline non-destructive to existing seeds (spec §7).

Critical determinism detail: fork's label→seed mixing must be stable across runs and crate versions. Do not use std::collections::hash_map::DefaultHasher (unspecified, version-unstable). Use an explicit, fixed integer mix (e.g. a splitmix64 / FNV-style combine of the base seed and the label bytes). Document the chosen mixing.

Interface Dependencies

  • Produces: Rng with:
    • from_seed(seed: u64) -> Self (stores the seed as the fork basis)
    • fork(&self, label: &str) -> Rng — returns Rng::from_seed(mix(self.basis_seed, label)); does not advance self; order-independent.
    • range(&mut self, lo: i32, hi: i32) -> i32 (half-open or inclusive — document which)
    • chance(&mut self, p: f64) -> bool
    • choose<'a, T>(&mut self, items: &'a [T]) -> Option<&'a T>
    • shuffle<T>(&mut self, items: &mut [T])

Tests

  • Same seed → identical sequence of range draws (reproducibility).
  • fork is order-independent: r.fork("a") produces the same stream regardless of how many values were drawn from r beforehand.
  • Distinct labels produce distinct (non-identical) streams: fork("a") != fork("b").
  • fork("name#0") and fork("name#1") differ (the occurrence-keying scheme the pipeline relies on works).
  • choose on an empty slice returns None; shuffle is a permutation (same multiset).

Success Criteria

  • Rng wraps ChaCha8; fork derives from a stable basis with an explicit (non-DefaultHasher) mix.
  • fork is order-independent and does not mutate the parent.
  • pub mod rng; added to lib.rs.
  • All tests pass: cargo test -p reikhelm-core rng

Commit

"feat(core): deterministic ChaCha8 RNG with order-independent fork"