diff options
Diffstat (limited to 'dungeon/src/rng.rs')
| -rw-r--r-- | dungeon/src/rng.rs | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/dungeon/src/rng.rs b/dungeon/src/rng.rs new file mode 100644 index 0000000..bb9151b --- /dev/null +++ b/dungeon/src/rng.rs @@ -0,0 +1,74 @@ +//! Implements rng using the xoshiro256++ algorithm + +use rand::{ + SeedableRng, + rand_core::{RngCore, le}, +}; + +/// Deterministic pseudo random number generator. +/// +/// Is is a copy of `SmallRng` from `rand`, but is gurenteed +/// to be reproducible across versions and platforms. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DungeonRng { + s: [u64; 4], +} +impl SeedableRng for DungeonRng { + // Fix to 256 bits. Changing this is a breaking change! + type Seed = [u8; 32]; + + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + let mut state = [0; 4]; + le::read_u64_into(&seed, &mut state); + Self { s: state } + } + + #[inline] + fn seed_from_u64(mut state: u64) -> Self { + const PHI: u64 = 0x9e3779b97f4a7c15; + let mut s = [0; 4]; + for i in &mut s { + state = state.wrapping_add(PHI); + let mut z = state; + z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9); + z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb); + z = z ^ (z >> 31); + *i = z; + } + Self { s } + } +} +impl RngCore for DungeonRng { + #[inline] + fn next_u32(&mut self) -> u32 { + let val = self.next_u64(); + (val >> 32) as u32 + } + + #[inline] + fn next_u64(&mut self) -> u64 { + let res = self.s[0] + .wrapping_add(self.s[3]) + .rotate_left(23) + .wrapping_add(self.s[0]); + + let t = self.s[1] << 17; + + self.s[2] ^= self.s[0]; + self.s[3] ^= self.s[1]; + self.s[1] ^= self.s[2]; + self.s[0] ^= self.s[3]; + + self.s[2] ^= t; + + self.s[3] = self.s[3].rotate_left(45); + + res + } + + #[inline] + fn fill_bytes(&mut self, dst: &mut [u8]) { + le::fill_bytes_via_next(self, dst); + } +} |