Newer
Older
rpg / packages / engine / src / math / rng.ts
/**
 * Детерминированный 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
    };
}