/** * Seeded RNG (mulberry32) for deterministic hatch results. * @param {number} seed * @returns {() => number} */ export function createSeededRng(seed) { let state = seed >>> 0; return function next() { state = (state + 0x6d2b79f5) >>> 0; // 32-bit let t = state; t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; } /** * @param {() => number} rng * @param {Array<{ id: string, weight: number }>} entries * @returns {string} */ export function pickId(rng, entries) { let total = 0; for (const e of entries) total += e.weight; if (total <= 0) throw new Error("WeightedRandom: non-positive total weight"); const roll = rng() * total; let cumulative = 0; for (const e of entries) { cumulative += e.weight; if (roll < cumulative) return e.id; } return entries[entries.length - 1].id; }