Online co-op browser remake of Arknights' Stronghold Protocol: Alliance (client-simulated combat, official waves/bonds/items/strategies, 3D board, facing wheel, operator loadout, official emotes, deployment scripts).
57 lines
1.9 KiB
JavaScript
57 lines
1.9 KiB
JavaScript
// server/sim/rng.js — seeded PRNG (mulberry32) + helpers. The ONLY source of randomness in the sim.
|
||
|
||
/**
|
||
* Create a deterministic PRNG. Returns a function producing floats in [0, 1) with helper methods.
|
||
* @param {number} seed uint32 (any number is coerced)
|
||
*/
|
||
export function createRng(seed = 1) {
|
||
let s = (Number(seed) >>> 0) || 0x9e3779b9;
|
||
const next = () => {
|
||
s = (s + 0x6d2b79f5) >>> 0;
|
||
let t = s;
|
||
t = Math.imul(t ^ (t >>> 15), t | 1);
|
||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||
};
|
||
const rng = () => next();
|
||
/** integer in [0, n) */
|
||
rng.int = (n) => Math.floor(next() * Math.max(0, n));
|
||
/** float in [a, b) */
|
||
rng.range = (a, b) => a + next() * (b - a);
|
||
/** true with probability p */
|
||
rng.chance = (p) => next() < p;
|
||
/** random element (undefined for empty) */
|
||
rng.pick = (arr) => (arr && arr.length ? arr[Math.floor(next() * arr.length)] : undefined);
|
||
/** in-place Fisher–Yates shuffle, returns arr */
|
||
rng.shuffle = (arr) => {
|
||
for (let i = arr.length - 1; i > 0; i--) {
|
||
const j = Math.floor(next() * (i + 1));
|
||
const tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
|
||
}
|
||
return arr;
|
||
};
|
||
/** weighted pick: items with weightFn(item) */
|
||
rng.weighted = (arr, weightFn) => {
|
||
let total = 0;
|
||
for (const x of arr) total += Math.max(0, weightFn(x));
|
||
if (total <= 0) return undefined;
|
||
let r = next() * total;
|
||
for (const x of arr) { r -= Math.max(0, weightFn(x)); if (r < 0) return x; }
|
||
return arr[arr.length - 1];
|
||
};
|
||
/** current internal state (for debugging / hashing) */
|
||
rng.state = () => s;
|
||
return rng;
|
||
}
|
||
|
||
/** Derive a child seed deterministically (e.g. per field). */
|
||
export function deriveSeed(seed, salt) {
|
||
let h = (Number(seed) >>> 0) ^ 0x85ebca6b;
|
||
const str = String(salt);
|
||
for (let i = 0; i < str.length; i++) {
|
||
h = Math.imul(h ^ str.charCodeAt(i), 0x9e3779b1);
|
||
h ^= h >>> 13;
|
||
}
|
||
return h >>> 0;
|
||
}
|