UNPKG

@jeremyckahn/farmhand

Version:
79 lines (65 loc) • 2.46 kB
import seedrandom from 'seedrandom' import globalWindow from 'global/window.js' import { randomStream } from '../../enums.js' export class RandomNumberService { seededRandom: (() => number) | null = null /** * Values queued per stream name that are returned, in order, before any * seeded or Math.random value. This lets tests force a specific outcome * (e.g. rain tonight) without searching for a seed that happens to produce * it. Exposed via the window.farmhand debug hook. */ queuedNumbers: Map<randomStream, number[]> = new Map() constructor() { // The availability of window.location needs to be checked before accessing // its .search property. This code runs in both a browser and Node.js // context, and window.location is not defined in Node.js environments. const initialSeed = new URLSearchParams(globalWindow.location?.search).get( 'seed' ) if (initialSeed) { this.seedRandomNumber(initialSeed) } } seedRandomNumber(seed: string) { this.seededRandom = seedrandom(seed) } /** * @param stream Optional name of the random number stream being drawn * from, which lets queueRandomNumbers target it. Values that aren't queued * come from the same seeded (or Math.random) sequence regardless of stream. */ generateRandomNumber(stream?: randomStream): number { if (stream !== undefined) { const queuedNumber = this.queuedNumbers.get(stream)?.shift() if (queuedNumber !== undefined) { return queuedNumber } } return this.seededRandom ? this.seededRandom() : Math.random() } /** * @param stream Name of the random number stream to queue values for. * @param numbers Values to return from the stream, in order, before it * resumes producing random numbers. */ queueRandomNumbers(stream: randomStream, numbers: number[]) { this.queuedNumbers.set(stream, [ ...(this.queuedNumbers.get(stream) ?? []), ...numbers, ]) } unseedRandomNumber() { this.seededRandom = null } /** * Compares given number against a randomly generated number. * @param chance Float between 0-1 to compare dice roll against. * @returns True if the dice roll was equal to or lower than the given chance, false otherwise. */ isRandomNumberLessThan(chance: number): boolean { return this.generateRandomNumber() <= chance } } export const randomNumberService = new RandomNumberService()