70 lines
1.7 KiB
JavaScript
70 lines
1.7 KiB
JavaScript
/**
|
||
* 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;
|
||
}
|