2.2 KiB
utils/sample
Purpose
The sample module covers the two sampling primitives that sit above shuffle: unique-without-replacement sample for drawing k items from n, and weightedChoice for proportional index selection. Both accept any Rng and throw typed PseudoRandomErrors on invalid input.
These primitives are intentionally small and allocation-frugal: sample reuses shuffle then slices, so we never allocate auxiliary structures beyond the shuffled copy; weightedChoice walks the weights once to compute the total, then once more to find the tipping point.
Features
sample(input, count, rng)— unique draw without replacementweightedChoice(weights, rng)— proportional index draw; rejects negative, NaN or all-zero weights- Both deterministic for equal seeds + equal RNG class
- Strict input validation (positive integer counts, finite non-negative weights)
Usage: Basic Example
import { Mulberry32Rng } from "@suaveplan/pseudo-random";
import { sample, weightedChoice } from "@suaveplan/pseudo-random/utils";
const rng = new Mulberry32Rng(1);
const hand = sample(["A", "K", "Q", "J", "10"], 3, rng);
const bucket = weightedChoice([1, 2, 4], rng); // 0, 1 or 2
Usage: Advanced Example
import { Sfc32Rng } from "@suaveplan/pseudo-random";
import { weightedChoice } from "@suaveplan/pseudo-random/utils";
type Drop = { id: string; weight: number };
const drops: Drop[] = [
{ id: "common", weight: 70 },
{ id: "rare", weight: 25 },
{ id: "epic", weight: 5 },
];
const rng = new Sfc32Rng("loot-table");
const idx = weightedChoice(drops.map((d) => d.weight), rng);
const chosen = drops[idx];
API Reference
sample<T>(input, count, rng)—PseudoRandomError.ARG.NEGATIVE_COUNTorARG.INVALIDon bad count;count === 0returns[].weightedChoice(weights, rng)—ARG.EMPTY_ARRAYon empty input;ARG.WEIGHTS_INVALIDon negative, NaN or all-zero weights.
Implementation Notes
weightedChoice draws rng.nextFloat() (a 32-bit uniform in [0, 1)), multiplies by the total weight, and walks the cumulative sum. The final fallback (return weights.length - 1) guards against floating-point rounding that would otherwise fail to find a bucket when the target sits exactly on the total.