deadhead-space-freight-tycoon/packages/sim/src/rng.ts

51 lines
1.4 KiB
TypeScript

/** A 32-bit unsigned integer seed. */
export type Seed = number;
export interface Rng {
/** Float in [0, 1). */
next(): number;
/** Integer in [min, max], inclusive. */
int(min: number, max: number): number;
/** A uniformly chosen element. Throws on an empty list. */
pick<T>(list: readonly T[]): T;
}
/** Seeded PRNG (mulberry32). The same seed always yields the same sequence. */
export function createRng(seed: Seed): Rng {
let state = seed >>> 0;
const next = () => {
state = (state + 0x6d2b79f5) >>> 0;
let t = state;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
return {
next,
int(min, max) {
return min + Math.floor(next() * (max - min + 1));
},
pick(list) {
if (list.length === 0) throw new Error("Cannot pick from an empty list");
return list[Math.floor(next() * list.length)]!;
},
};
}
/** Derives an independent child seed from a parent seed and a label (FNV-1a, then a final mix). */
export function deriveSeed(parent: Seed, label: string): Seed {
let hash = 0x811c9dc5 ^ (parent >>> 0);
for (let i = 0; i < label.length; i++) {
hash ^= label.charCodeAt(i);
hash = Math.imul(hash, 0x01000193);
}
hash ^= hash >>> 16;
hash = Math.imul(hash, 0x85ebca6b);
hash ^= hash >>> 13;
hash = Math.imul(hash, 0xc2b2ae35);
hash ^= hash >>> 16;
return hash >>> 0;
}