1.9 KiB
algorithms/counter-rng
Purpose
CounterRng is a hash-based PRNG: its output at logical position i is splitmix64(seed XOR i). Because the output depends only on position, every seek is constant time — at(1_000_000) does not draw a million values, it just advances the counter. This makes CounterRng uniquely suited to massively parallel simulations, tile-based procedural generation, and any scenario where you need to sample an arbitrary index of the stream without replaying it.
Features
- O(1)
at(offset)andskip(n)— no per-step iteration cost - 64-bit seed state stored as two uint32 halves for JSON safety
- Full
SeededRngcontract - Output quality on par with splitmix64 (passes BigCrush on the upper 32 bits)
Usage: Basic Example
import { CounterRng } from "@suaveplan/pseudo-random";
const rng = new CounterRng("world");
const v = rng.nextUint32();
Usage: Advanced Example
import { CounterRng } from "@suaveplan/pseudo-random";
// Sample tile (1234, 5678) without materialising everything in between.
const rng = new CounterRng("map");
const tile = rng.at(1234 * 10000 + 5678);
const decoration = tile.nextUint32();
API Reference
new CounterRng(seed)— accepts the standardstring | numberseed.nextUint32()/nextFloat()— one counter step, output issplitmix64(seed XOR pos).out >> 32.skip(n)/at(offset)— both constant-time.fork(id)— derives a new counter keyed on(seed, position, id).checkpoint()/restore(cp)— state is[seedLo, seedHi]plus counter.
Implementation Notes
Counter generators expose their index directly, so the usual "position = draws so far" semantics hold regardless of whether you reach position 1000 by calling nextUint32 1000 times, calling skip(1000), or jumping via at(1000). The streamAt helper is deterministic and stateless, which is what makes the O(1) seek possible.