/**
* Детерминированный RNG (mulberry32): одинаковый seed — одинаковая последовательность.
* Используется для генерации мира, лута, частиц — везде, где нужен повторяемый «случай».
*/
export interface Rng {
/** Следующее число 0..1. */
next(): number;
/** Целое в диапазоне [min, max] включительно. */
int(min: number, max: number): number;
/** Случайный элемент массива (массив должен быть непустым). */
pick<T>(arr: readonly T[]): T;
/** true с вероятностью p (0..1). */
chance(p: number): boolean;
/** Текущее состояние — можно сохранить и восстановить для продолжения последовательности. */
state(): number;
}
/** Создать RNG из 32-битного seed. */
export function createRng(seed: number): Rng {
let s = seed >>> 0;
const next = (): number => {
s = (s + 0x6d2b79f5) >>> 0;
let t = s;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
return {
next,
int: (min, max) => min + Math.floor(next() * (max - min + 1)),
pick: <T,>(arr: readonly T[]) => {
if (arr.length === 0) throw new Error('pick из пустого массива');
return arr[Math.floor(next() * arr.length)];
},
chance: (p) => next() < p,
state: () => s
};
}