import prand from 'pure-rand' export default class NumberGenerator { private generator: prand.RandomGenerator public seed: number constructor(seed?: number) { this.seed = seed ?? Date.now() ^ (Math.random() * 0x100000000) this.generator = prand.xoroshiro128plus(this.seed) } /** * Returns a uniformly distributed random integer between min and max */ public uniformInt(min: number, max: number): number { const [value, rng] = prand.uniformIntDistribution(min, max)(this.generator) this.generator = rng return value } /* * Returns a uniformly distributed random float between min and max */ public uniformFloat(min: number, max: number) { const [value, rng] = prand.uniformIntDistribution(min * 1000, max * 1000)(this.generator) this.generator = rng return value / 1000 } /** * Returns a normally distributed random number with mean mu and standard deviation sigma. */ public normalFloat(mu: number, sigma: number): number { let u = 0, v = 0 while(u === 0) u = 1 - this.uniformFloat(0, 1) while(v === 0) v = this.uniformFloat(0, 1) return sigma * Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v ) + mu } /** * Picks a single random element from the array. * Returns undefined if the array is empty. */ public pickRandom(array: T[]): T | undefined { if (array.length === 0) { return undefined } return array[this.uniformInt(0, array.length - 1)] } /** * Picks a random subset of the array without replacement. * Returns an empty array if the array is empty. */ public pickRandomArray(array: T[], size: number): T[] { if (array.length === 0) { return [] } const result: T[] = [] const copy = array.slice() const limit = Math.min(size, copy.length) for (let i = 0; i < limit; i++) { const index = this.uniformInt(0, copy.length - 1) result.push(copy[index]) copy.splice(index, 1) } return result } }