|
|
@ -2,6 +2,10 @@ export type Rng = () => number; |
|
|
|
|
|
|
|
|
const DEFAULT_SEED = 0x9e3779b9; |
|
|
const DEFAULT_SEED = 0x9e3779b9; |
|
|
|
|
|
|
|
|
|
|
|
function toUint32(value: number): number { |
|
|
|
|
|
return value >>> 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
function hashString(input: string): number { |
|
|
function hashString(input: string): number { |
|
|
let hash = 2166136261; |
|
|
let hash = 2166136261; |
|
|
for (let i = 0; i < input.length; i += 1) { |
|
|
for (let i = 0; i < input.length; i += 1) { |
|
|
@ -12,8 +16,13 @@ function hashString(input: string): number { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export function normalizeSeed(input?: string | number): number { |
|
|
export function normalizeSeed(input?: string | number): number { |
|
|
|
|
|
// Seed normalization contract (always returns uint32):
|
|
|
|
|
|
// 1) finite number -> ToUint32 (e.g. -1 => 0xffffffff, 1.9 => 1)
|
|
|
|
|
|
// 2) numeric string -> parse then ToUint32
|
|
|
|
|
|
// 3) non-numeric string -> stable hash -> uint32
|
|
|
|
|
|
// 4) undefined / empty / whitespace / NaN / non-finite -> DEFAULT_SEED
|
|
|
if (typeof input === "number" && Number.isFinite(input)) { |
|
|
if (typeof input === "number" && Number.isFinite(input)) { |
|
|
return input >>> 0; |
|
|
return toUint32(input); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (typeof input === "string") { |
|
|
if (typeof input === "string") { |
|
|
@ -24,7 +33,7 @@ export function normalizeSeed(input?: string | number): number { |
|
|
|
|
|
|
|
|
const numericSeed = Number(trimmed); |
|
|
const numericSeed = Number(trimmed); |
|
|
if (Number.isFinite(numericSeed)) { |
|
|
if (Number.isFinite(numericSeed)) { |
|
|
return numericSeed >>> 0; |
|
|
return toUint32(numericSeed); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return hashString(trimmed); |
|
|
return hashString(trimmed); |
|
|
@ -37,9 +46,9 @@ export function createRng(seed: number): Rng { |
|
|
let state = normalizeSeed(seed); |
|
|
let state = normalizeSeed(seed); |
|
|
|
|
|
|
|
|
return () => { |
|
|
return () => { |
|
|
state = (state + 0x6d2b79f5) >>> 0; |
|
|
state = toUint32(state + 0x6d2b79f5); |
|
|
let t = Math.imul(state ^ (state >>> 15), 1 | state); |
|
|
let t = Math.imul(state ^ (state >>> 15), 1 | state); |
|
|
t ^= t + Math.imul(t ^ (t >>> 7), 61 | t); |
|
|
t ^= t + Math.imul(t ^ (t >>> 7), 61 | t); |
|
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296; |
|
|
return toUint32(t ^ (t >>> 14)) / 4294967296; |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
|