export class SeededRandom { private state: number constructor(seed: number) { this.state = seed >>> 0 if (this.state === 0) this.state = 0x6d2b79f5 } next(): number { this.state += 0x6d2b79f5 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) / 4294967296 } int(maxExclusive: number): number { return Math.floor(this.next() * maxExclusive) } pick(items: readonly T[]): T { if (items.length === 0) throw new Error('Cannot pick from an empty list') return items[this.int(items.length)] } fork(offset: number): SeededRandom { return new SeededRandom((this.state + Math.imul(offset + 1, 0x9e3779b9)) >>> 0) } } export function weightedIndex(weights: readonly number[], rng: SeededRandom): number { const total = weights.reduce((sum, w) => sum + Math.max(0, w), 0) if (total <= 0) return rng.int(weights.length) let needle = rng.next() * total for (let i = 0; i < weights.length; i++) { needle -= Math.max(0, weights[i]) if (needle <= 0) return i } return weights.length - 1 }