2.3 KiB
utils/shuffle
Purpose
The shuffle module provides deterministic Fisher-Yates shuffling and single-element choice drawn from a user-supplied Rng. Because Rng is the canonical interface defined in @suaveplan/types, these utilities compose with every SeededRng implementation in this package as well as with the CSPRNG in @suaveplan/random.
Shuffling is a building block for game-card dealing, playlist generation, A/B assignment, and deterministic test data. Isolating it here ensures every downstream package uses the same unbiased Fisher-Yates algorithm rather than re-implementing it (which frequently introduces subtle bias).
Features
shuffle(input, rng)— non-mutating, returns a new shuffled arrayshuffleInPlace(arr, rng)— mutates the argument, returns itchoice(input, rng)— uniformly random element; typedPseudoRandomErroron empty input- Draws exactly
n - 1uint32 values per shuffle — counts are predictable for replay
Usage: Basic Example
import { Mulberry32Rng } from "@suaveplan/pseudo-random";
import { shuffle } from "@suaveplan/pseudo-random/utils";
const rng = new Mulberry32Rng(42);
const deck = shuffle(["A", "K", "Q", "J"], rng);
Usage: Advanced Example
import { Sfc32Rng } from "@suaveplan/pseudo-random";
import { choice, shuffleInPlace } from "@suaveplan/pseudo-random/utils";
const rng = new Sfc32Rng("round-12");
const deck = ["clubs", "hearts", "diamonds", "spades"];
shuffleInPlace(deck, rng);
const first = choice(deck, rng);
API Reference
shuffle<T>(input: ReadonlyArray<T>, rng: Rng): T[]— returns a new array.shuffleInPlace<T>(arr: T[], rng: Rng): T[]— mutates and returns.choice<T>(input: ReadonlyArray<T>, rng: Rng): T— throwsPseudoRandomError.ARG.EMPTY_ARRAYon empty input.
Implementation Notes
The Fisher-Yates loop walks from n-1 down to 1, swapping arr[i] with arr[j] where j = rng.nextUint32() % (i+1). The modulo introduces a negligible bias when i+1 does not divide 2^32 evenly (max bias ≈ n / 2^32); for any n short of 2^32 elements this is statistically undetectable and matches the behaviour every other mainstream library ships. If you need provably unbiased sampling for large n, use @suaveplan/random's randomInt, which re-draws on biased samples.