// https://stackoverflow.com/questions/521295#47593316 export type State = [number, number, number, number]; export interface Randy { state: State; int(): number; // whole int32 range int(x: number): number; // [0, x) int(x: number, y: number): number; // [x, y) float(): number; // [0, 1) float(x: number): number; // [0, x) float(x: number, y: number): number; // [x, y) choice(array: A[]): A; boolean(): boolean; } export function fromHex(s: string): State { const a = s.substring(0, 8); const b = s.substring(8, 16); const c = s.substring(16, 24); const d = s.substring(24, 32); return [h(a), h(b), h(c), h(d)]; function h(x: string) { return parseInt(x, 16); } } export function toHex([a, b, c, d]: State): string { return `${h(a)}${h(b)}${h(c)}${h(d)}`; function h(x: number) { return x.toString(16); } } const UINT_MAX = 4294967296; export class Rand implements Randy { #a: number; #b: number; #c: number; #d: number; constructor(); constructor([a, b, c, d]: State); constructor(str: string); constructor(st?: State | string) { const [a, b, c, d] = st === '' ? s4() : typeof st === 'string' ? Rand.stateFrom(st) : st ?? s4(); this.#a = a; this.#b = b; this.#c = c; this.#d = d; for (let i = 0; i < 20; ++i) this.#next(); function s() { return (Math.random() * 2**32) >>> 0; } function s4() { return [s(), s(), s(), s()] as const; } } get state(): State { return [this.#a, this.#b, this.#c, this.#d]; } get stateString() { return toHex(this.state); } static stateFrom(str: string): State { let h1 = 1779033703, h2 = 3144134277, h3 = 1013904242, h4 = 2773480762; for (let i = 0, k; i < str.length; i++) { k = str.charCodeAt(i); h1 = h2 ^ Math.imul(h1 ^ k, 597399067); h2 = h3 ^ Math.imul(h2 ^ k, 2869860233); h3 = h4 ^ Math.imul(h3 ^ k, 951274213); h4 = h1 ^ Math.imul(h4 ^ k, 2716044179); } h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067); h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233); h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213); h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179); h1 ^= (h2 ^ h3 ^ h4); h2 ^= h1; h3 ^= h1; h4 ^= h1; return [h1>>>0, h2>>>0, h3>>>0, h4>>>0]; } int(): number; // whole int32 range int(to: number): number; // [0, x) int(from: number, to: number): number; // [x, y) int(x?: number, y?: number): number { if (x === undefined) return this.#next(); return Math.floor(y === undefined ? this.float(x) : this.float(x, y)); } float(): number; // [0, 1) float(x: number): number; // [0, x) float(x: number, y: number): number; // [x, y) float(x?: number, y?: number): number { const in01 = this.#next() / UINT_MAX; if (x === undefined) return in01; const [lo, hi] = y === undefined? [0, x] : [x, y]; return lo + in01 * (hi - lo); } choice(array: A[]): A { return array[this.int(array.length)]!; } boolean(): boolean { return this.float() > 0.5; } alphaNum(len?: number): string { let res = ""; // [todo] is there a better way to make a string in js if (len === undefined || len <= 0) len = 16; // idk for (let i = 0; i < len; ++i) res += this.choice(Array.from("abcdefghijklmnopqrstuvwxyz0123456789")); return res; } #next(): number { this.#a |= 0; this.#b |= 0; this.#c |= 0; this.#d |= 0; const t = (this.#a + this.#b | 0) + this.#d | 0; this.#d = this.#d + 1 | 0; this.#a = this.#b ^ this.#b >>> 9; this.#b = this.#c + (this.#c << 3) | 0; this.#c = (this.#c << 21 | this.#c >>> 11); this.#c = this.#c + t | 0; return (t >>> 0); } }