//! Seeded, deterministic RNG (xorshift64* over splitmix64-derived streams). Mods only ever
//! reach it via `ctx:random()`, which keeps replays/tests stable — that's why it lives in the
//! shared context core, not as a free function.
pub struct Rng {
state: u64,
}
impl Rng {
pub fn seeded(seed: u64) -> Rng {
Rng {
state: seed ^ 0x9E37_79B9_7F4A_7C15,
}
}
/// A child stream for one pass. Advances the parent (so successive frames fork
/// distinct streams) and seeds the child from the mixed bits.
pub fn fork(&mut self) -> Rng {
self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.state;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
Rng {
state: (z ^ (z >> 31)) | 1,
}
}
pub fn next_u32(&mut self) -> u32 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
(x >> 32) as u32
}
pub fn f32(&mut self) -> f32 {
(self.next_u32() >> 8) as f32 / (1u32 << 24) as f32
}
pub fn range_f32(&mut self, min: f32, max: f32) -> f32 {
min + (self.f32() * (max - min))
}
}