Newer
Older
rpg / v2 / packages / engine / src / math / rng.ts
/**
 * Детерминированный ГПСЧ (mulberry32) — генераторы моделей и мир зависят
 * только от сида, не от порядка вызовов за его пределами. Чистая математика.
 */
export class Rng {
    private state: number;

    constructor(seed: number) {
        if (!Number.isInteger(seed) || seed < 0) {
            throw new Error(`Rng: сид должен быть неотрицательным целым, дано ${seed}`);
        }
        this.state = seed >>> 0;
    }

    /** [0, 2^32) как беззнаковое целое. */
    nextUint(): number {
        this.state = (this.state + 0x6d2b79f5) >>> 0;
        let t = this.state;
        t = Math.imul(t ^ (t >>> 15), t | 1);
        t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
        return ((t ^ (t >>> 14)) >>> 0);
    }

    /** [0, 1). */
    next(): number {
        return this.nextUint() / 4294967296;
    }

    /** Целое в [0, n). */
    int(n: number): number {
        return Math.floor(this.next() * n);
    }

    /** Целое в [min, max] включительно. */
    range(min: number, max: number): number {
        return min + this.int(max - min + 1);
    }

    /** Логический выбор с вероятностью p. */
    chance(p: number): boolean {
        return this.next() < p;
    }

    /** Выбор случайного элемента массива. */
    pick<T>(items: readonly T[]): T {
        return items[this.int(items.length)];
    }
}