/**
 * Детерминированный 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
    };
}

/**
 * Детерминированный хэш координат тайла без состояния: одинаковые (x, y, seed)
 * — одинаковый результат навсегда. В отличие от createRng не потребляет
 * последовательность и не зависит от порядка вызовов — годится для выбора
 * варианта текстуры по тайлу (hashTile(x, y, id) % варианты.length).
 */
export function hashTile(x: number, y: number, seed = 0): number {
    let h = (Math.imul(x, 374761393) + Math.imul(y, 668265263) + Math.imul(seed, 2654435761)) >>> 0;
    h = (h ^ (h >>> 13)) >>> 0;
    h = Math.imul(h, 1274126177) >>> 0;
    return (h ^ (h >>> 16)) >>> 0;
}