/** * Deterministischer Zufallszahlengenerator. * * Warum nicht Math.random: Mit einem Seed lässt sich eine Welle reproduzieren – * das macht die Wellengenerierung testbar und Balancing-Fehler nachstellbar. * @module logic/rng */ /** @typedef {() => number} Rng Liefert Zahlen im Intervall [0, 1). */ /** * Mulberry32: klein, schnell und für Spielzwecke gut genug verteilt. * @param {number} seed Beliebige Ganzzahl. * @returns {Rng} */ export function createRng(seed) { let state = seed >>> 0; return function next() { state = (state + 0x6d2b79f5) >>> 0; 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; }; } /** * Ganzzahl im geschlossenen Intervall [min, max]. * @param {Rng} rng * @param {number} min * @param {number} max * @returns {number} */ export function randInt(rng, min, max) { return min + Math.floor(rng() * (max - min + 1)); } /** * Gleitkommazahl im Intervall [min, max). * @param {Rng} rng * @param {number} min * @param {number} max * @returns {number} */ export function randFloat(rng, min, max) { return min + rng() * (max - min); } /** * Zufälliges Element einer nicht-leeren Liste. * @template T * @param {Rng} rng * @param {readonly T[]} items * @returns {T} */ export function pick(rng, items) { if (items.length === 0) { throw new Error('pick() braucht mindestens ein Element'); } return items[Math.floor(rng() * items.length)]; } /** * Seed aus der aktuellen Zeit, für echte Spielrunden. * @returns {number} */ export function timeSeed() { return (Date.now() ^ Math.floor(Math.random() * 0xffffffff)) >>> 0; }