71 lines
2.2 KiB
JavaScript
71 lines
2.2 KiB
JavaScript
/* ============================================================
|
|
rng.js — deterministic seeding & sampling
|
|
Every stochastic decision in the generator draws from a
|
|
salted sub-stream so that changing one parameter does not
|
|
reshuffle unrelated parts of the scene.
|
|
============================================================ */
|
|
|
|
/* cyrb53 — fast 53-bit string hash. Stable across runs/machines. */
|
|
export function cyrb53(str, seed = 0) {
|
|
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
|
|
for (let i = 0; i < str.length; i++) {
|
|
const ch = str.charCodeAt(i);
|
|
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
}
|
|
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16).padStart(13, '0');
|
|
}
|
|
|
|
/* mulberry32 — tiny fast PRNG, returns [0,1). */
|
|
export function mulberry32(seed) {
|
|
let a = seed >>> 0;
|
|
return function () {
|
|
a = (a + 0x6D2B79F5) >>> 0;
|
|
let t = a;
|
|
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
};
|
|
}
|
|
|
|
/* makeRng — seed string + salt → independent PRNG stream. */
|
|
export function makeRng(seedStr, salt = '') {
|
|
const h = cyrb53(seedStr + '::' + salt);
|
|
return mulberry32(parseInt(h.slice(0, 8), 16));
|
|
}
|
|
|
|
/* ---------- Distributions ---------- */
|
|
|
|
/* Standard normal via Box-Muller. */
|
|
export function gauss(rng) {
|
|
const u = 1 - rng(), v = rng();
|
|
return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
|
|
}
|
|
|
|
/* Log-normal — heavy-tailed positive sample. */
|
|
export function logNormal(rng, mu, sigma) {
|
|
return Math.exp(mu + sigma * gauss(rng));
|
|
}
|
|
|
|
/* Uniform in [lo, hi). */
|
|
export function range(rng, lo, hi) {
|
|
return lo + (hi - lo) * rng();
|
|
}
|
|
|
|
/* Random int in [lo, hi]. */
|
|
export function randInt(rng, lo, hi) {
|
|
return lo + Math.floor(rng() * (hi - lo + 1));
|
|
}
|
|
|
|
/* Pick one element. */
|
|
export function pick(rng, arr) {
|
|
return arr[Math.floor(rng() * arr.length)];
|
|
}
|
|
|
|
/* Bernoulli. */
|
|
export function chance(rng, p) {
|
|
return rng() < p;
|
|
}
|