miaoda-game-random-core
v0.1.0
Published
Engine-independent compatibility primitives for deterministic random streams with exact serializable state.
Maintainers
Readme
miaoda-game-random-core
Use this package for engine-independent deterministic random-stream primitives whose exact output must remain compatible with replays and saved state. It currently provides the repository's legacy Mulberry32 stream; it is not a cryptographic random-number generator.
Exact state, not a seed policy
import { Mulberry32Stream } from 'miaoda-game-random-core';
const random = new Mulberry32Stream(1);
const roll = random.next();
const savedState = random.state;
const expectedNext = random.nextUint32();
const restored = new Mulberry32Stream(savedState);
restored.nextUint32() === expectedNext; // true
const die = random.intInclusive(1, 6);
const slot = random.intExclusive(0, 8);
random.shuffleInPlace(cards);
const shuffledCopy = random.shuffled(readonlyCards);The constructor accepts an exact unsigned 32-bit stream state, including 0. It deliberately does
not normalize seeds, choose a fallback seed, use Math.random, or provide domain pick/weight rules.
intExclusive(minInclusive, maxExclusive) and intInclusive(minInclusive, maxInclusive) use
rejection sampling over at most 2^32 outcomes. shuffleInPlace uses those unbiased draws;
shuffled copies first.
These helpers are opt-in protocol choices. Existing facades that historically used float scaling keep that implementation because switching can change both results and random-stream consumption.
Changing the output transform or state transition is a replay and save-game compatibility change.
Use a different explicitly named stream for any future algorithm rather than changing
Mulberry32Stream in place.
Shared source protocol
Generic consumers may accept UnitRandomInput, which supports either a () => number function or
an object with next(): number. nextUnitFloat consumes exactly one value and validates the shared
finite [0, 1) contract. This protocol deliberately says nothing about seeds, integer intervals,
selection failure, draw counts, or persistence; those are algorithm and compatibility decisions.
import { nextUnitFloat, type UnitRandomInput } from 'miaoda-game-random-core';
function coinFlip(random: UnitRandomInput): boolean {
return nextUnitFloat(random) < 0.5;
}See docs/random-protocol-compatibility.md in the repository for the facade migration policy.
