From fea3cf7ad14f8f2aeade6994f052caf49cd30285 Mon Sep 17 00:00:00 2001 From: kralverde Date: Thu, 29 Aug 2024 18:25:02 -0400 Subject: [PATCH] refactor and verify --- pumpkin-core/Cargo.toml | 1 - pumpkin-core/src/random/mod.rs | 6 +- pumpkin-core/src/random/xoroshiro128.rs | 195 ++++++++++-------------- 3 files changed, 87 insertions(+), 115 deletions(-) diff --git a/pumpkin-core/Cargo.toml b/pumpkin-core/Cargo.toml index a5a7ad16d..6bd39248b 100644 --- a/pumpkin-core/Cargo.toml +++ b/pumpkin-core/Cargo.toml @@ -9,4 +9,3 @@ fastnbt = { git = "https://github.com/owengage/fastnbt.git" } uuid.workspace = true colored = "2" md5 = "0.7.0" - diff --git a/pumpkin-core/src/random/mod.rs b/pumpkin-core/src/random/mod.rs index 42b7a6e08..f56a4c182 100644 --- a/pumpkin-core/src/random/mod.rs +++ b/pumpkin-core/src/random/mod.rs @@ -3,11 +3,13 @@ pub mod xoroshiro128; mod gaussian; pub trait Random { + fn from_seed(seed: u64) -> Self; + fn split(&mut self) -> Self; fn next_splitter(&mut self) -> impl RandomSplitter; - fn set_seed(&mut self, seed: i64); + fn next(&mut self, bits: u64) -> u64; fn next_i32(&mut self) -> i32; @@ -45,7 +47,7 @@ pub trait Random { pub trait RandomSplitter { fn split_string(&self, seed: &str) -> impl Random; - fn split_i64(&self, seed: i64) -> impl Random; + fn split_u64(&self, seed: u64) -> impl Random; fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random; } diff --git a/pumpkin-core/src/random/xoroshiro128.rs b/pumpkin-core/src/random/xoroshiro128.rs index 6826fc683..dea1255ad 100644 --- a/pumpkin-core/src/random/xoroshiro128.rs +++ b/pumpkin-core/src/random/xoroshiro128.rs @@ -1,32 +1,46 @@ use super::{gaussian::GaussianGenerator, Random, RandomSplitter}; pub struct Xoroshiro { - seed: XoroshiroSeed, + lo: u64, + hi: u64, internal_next_gaussian: f64, internal_has_next_gaussian: bool, } impl Xoroshiro { - fn new(lo: i64, hi: i64) -> Self { - if (lo | hi) == 0 { - return Self { - seed: XoroshiroSeed { - lo: -7046029254386353131i64, - hi: 7640891576956012809i64, - }, - internal_next_gaussian: 0f64, - internal_has_next_gaussian: false, - }; - } + fn new(lo: u64, hi: u64) -> Self { + let (lo, hi) = if (lo | hi) == 0 { + (0x9E3779B97F4A7C15, 0x6A09E667F3BCC909) + } else { + (lo, hi) + }; Self { - seed: XoroshiroSeed { lo, hi }, + lo, + hi, internal_next_gaussian: 0f64, internal_has_next_gaussian: false, } } - fn next(&mut self, bits: u64) -> u64 { - (self.seed.next() as u64) >> (64 - bits) + fn mix_u64(seed: u64) -> (u64, u64) { + let l = seed ^ 0x6A09E667F3BCC909; + let m = l.wrapping_add(0x9E3779B97F4A7C15); + (l, m) + } + + pub fn from_seed_unmixed(seed: u64) -> Self { + let (lo, hi) = Self::mix_u64(seed); + Self::new(lo, hi) + } + + fn next_random(&mut self) -> u64 { + let l = self.lo; + let m = self.hi; + let n = (l.wrapping_add(m)).rotate_left(17).wrapping_add(l); + let m = m ^ l; + self.lo = l.rotate_left(49) ^ m ^ (m << 21); + self.hi = m.rotate_left(28); + n } } @@ -48,37 +62,49 @@ impl GaussianGenerator for Xoroshiro { } } +fn mix_stafford_13(z: u64) -> u64 { + let z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9); + let z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB); + z ^ (z >> 31) +} + impl Random for Xoroshiro { + fn from_seed(seed: u64) -> Self { + let (lo, hi) = Self::mix_u64(seed); + let lo = mix_stafford_13(lo); + let hi = mix_stafford_13(hi); + Self::new(lo, hi) + } + fn split(&mut self) -> Self { - Self::new(self.seed.next(), self.seed.next()) + Self::new(self.next_random(), self.next_random()) + } + + fn next(&mut self, bits: u64) -> u64 { + self.next_random() >> (64 - bits) } fn next_splitter(&mut self) -> impl RandomSplitter { XoroshiroSplitter { - lo: self.seed.next(), - hi: self.seed.next(), + lo: self.next_random(), + hi: self.next_random(), } } - fn set_seed(&mut self, seed: i64) { - self.seed = XoroshiroSeed::mixed_seed(seed); - self.set_has_next_gaussian(false); - } - fn next_i32(&mut self) -> i32 { - self.seed.next() as i32 + self.next_random() as i32 } fn next_bounded_i32(&mut self, bound: i32) -> i32 { let mut l = (self.next_i32() as u64) & 0xFFFFFFFF; let mut m = l.wrapping_mul(bound as u64); - let mut n = m & 4294967295u64; + let mut n = m & 0xFFFFFFFF; if n < bound as u64 { let i = (((!bound).wrapping_add(1)) as u64) % (bound as u64); while n < i { l = (self.next_i32() as u64) & 0xFFFFFFFF; m = l.wrapping_mul(bound as u64); - n = m & 4294967295u64; + n = m & 0xFFFFFFFF; } } let o = m >> 32; @@ -86,11 +112,11 @@ impl Random for Xoroshiro { } fn next_i64(&mut self) -> i64 { - self.seed.next() + self.next_random() as i64 } fn next_bool(&mut self) -> bool { - (self.seed.next() & 1) != 0 + (self.next_random() & 1) != 0 } fn next_f32(&mut self) -> f32 { @@ -107,61 +133,14 @@ impl Random for Xoroshiro { fn skip(&mut self, count: i32) { for _ in 0..count { - self.seed.next(); + self.next_random(); } } } -struct XoroshiroSeed { - lo: i64, - hi: i64, -} - -fn mix_stafford_13(seed: i64) -> i64 { - let seed = (seed ^ ((seed as u64) >> 30) as i64).wrapping_mul(-4658895280553007687i64); - let seed = (seed ^ ((seed as u64) >> 27) as i64).wrapping_mul(-7723592293110705685i64); - seed ^ ((seed as u64) >> 31) as i64 -} - -impl XoroshiroSeed { - fn split(&self, lo: i64, hi: i64) -> Self { - Self { - lo: self.lo ^ lo, - hi: self.hi ^ hi, - } - } - - fn mix(&self) -> Self { - Self { - lo: mix_stafford_13(self.lo), - hi: mix_stafford_13(self.hi), - } - } - - fn next(&mut self) -> i64 { - let l = self.lo; - let m = self.hi; - let n = l.wrapping_add(m).rotate_left(17).wrapping_add(l); - let o = m ^ l; - self.lo = l.rotate_left(49) ^ o ^ (o << 21); - self.hi = o.rotate_left(28); - n - } - - fn unmixed_seed(seed: i64) -> Self { - let l = seed ^ 7640891576956012809i64; - let m = l + -7046029254386353131i64; - Self { lo: l, hi: m } - } - - fn mixed_seed(seed: i64) -> Self { - Self::unmixed_seed(seed).mix() - } -} - pub struct XoroshiroSplitter { - lo: i64, - hi: i64, + lo: u64, + hi: u64, } fn hash_pos(x: i32, y: i32, z: i32) -> i64 { @@ -176,22 +155,21 @@ fn hash_pos(x: i32, y: i32, z: i32) -> i64 { impl RandomSplitter for XoroshiroSplitter { fn split_pos(&self, x: i32, y: i32, z: i32) -> impl Random { - let l = hash_pos(x, y, z); + let l = hash_pos(x, y, z) as u64; let m = l ^ self.lo; Xoroshiro::new(m, self.hi) } - fn split_i64(&self, seed: i64) -> impl Random { + fn split_u64(&self, seed: u64) -> impl Random { Xoroshiro::new(seed ^ self.lo, seed ^ self.hi) } fn split_string(&self, seed: &str) -> impl Random { let bytes = md5::compute(seed.as_bytes()); - let l = i64::from_be_bytes(bytes[0..8].try_into().expect("incorrect length")); - let m = i64::from_be_bytes(bytes[8..16].try_into().expect("incorrect length")); + let l = u64::from_be_bytes(bytes[0..8].try_into().expect("incorrect length")); + let m = u64::from_be_bytes(bytes[8..16].try_into().expect("incorrect length")); - let new_seed = XoroshiroSeed { lo: l, hi: m }.split(self.lo, self.hi); - Xoroshiro::new(new_seed.lo, new_seed.hi) + Xoroshiro::new(l ^ self.lo, m ^ self.hi) } } @@ -199,7 +177,7 @@ impl RandomSplitter for XoroshiroSplitter { mod tests { use crate::random::{Random, RandomSplitter}; - use super::{hash_pos, mix_stafford_13, Xoroshiro, XoroshiroSeed}; + use super::{hash_pos, mix_stafford_13, Xoroshiro}; // Values checked against results from the equivalent Java source @@ -223,7 +201,7 @@ mod tests { #[test] fn test_mix_stafford_13() { - let values: [(i64, i64); 31] = [ + let values: [(u64, i64); 31] = [ (0, 0), (1, 6238072747940578789), (64, -8456553050427055661), @@ -256,9 +234,8 @@ mod tests { (562949953421567, -5076179956679497213), (36028797018964223, -5993365784811617721), ]; - for (input, output) in values { - assert_eq!(mix_stafford_13(input), output); + assert_eq!(mix_stafford_13(input), output as u64); } } @@ -277,9 +254,7 @@ mod tests { 1560080270, ]; - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); - + let mut xoroshiro = Xoroshiro::from_seed(0); for value in values { assert_eq!(xoroshiro.next_i32(), value); } @@ -287,8 +262,7 @@ mod tests { #[test] fn test_next_bounded_i32() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let values = [9, 1, 1, 3, 8, 9, 0, 3, 6, 3]; for value in values { @@ -306,8 +280,7 @@ mod tests { #[test] fn test_next_between_i32() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let values = [99, 59, 57, 65, 94, 100, 54, 66, 83, 68]; for value in values { @@ -317,8 +290,7 @@ mod tests { #[test] fn test_next_inbetween_exclusive() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let values = [98, 59, 57, 65, 94, 99, 53, 66, 82, 68]; for value in values { @@ -328,8 +300,7 @@ mod tests { #[test] fn test_next_f64() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let values: [f64; 10] = [ 0.16474369376959186, @@ -350,8 +321,7 @@ mod tests { #[test] fn test_next_f32() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let values: [f32; 10] = [ 0.16474366, @@ -372,8 +342,7 @@ mod tests { #[test] fn test_next_i64() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let values: [i64; 10] = [ 3038984756725240190, @@ -394,8 +363,7 @@ mod tests { #[test] fn test_next_bool() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let values: [bool; 10] = [ false, false, false, true, true, true, false, true, true, false, @@ -407,8 +375,7 @@ mod tests { #[test] fn test_next_gaussian() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let values: [f64; 10] = [ -0.48540690699780015, @@ -429,8 +396,7 @@ mod tests { #[test] fn test_next_triangular() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let values: [f64; 10] = [ 6.824989823834776, @@ -451,8 +417,7 @@ mod tests { #[test] fn test_split() { - let seed = XoroshiroSeed::mixed_seed(0); - let mut xoroshiro = Xoroshiro::new(seed.lo, seed.hi); + let mut xoroshiro = Xoroshiro::from_seed(0); let mut new_generator = xoroshiro.split(); assert_eq!(new_generator.next_i32(), 542195535); @@ -463,7 +428,7 @@ mod tests { let mut rand_1 = splitter.split_string("TEST STRING"); assert_eq!(rand_1.next_i32(), -641435713); - let mut rand_2 = splitter.split_i64(42069); + let mut rand_2 = splitter.split_u64(42069); assert_eq!(rand_2.next_i32(), -340700677); let mut rand_3 = splitter.split_pos(1337, 80085, -69420); @@ -473,4 +438,10 @@ mod tests { assert_eq!(xoroshiro.next_i32(), 653572596); assert_eq!(new_generator.next_i32(), 435917842); } + + #[test] + fn test_intersection() { + let mut xoroshiro = Xoroshiro::new(0, 0); + assert_eq!(xoroshiro.next_i64(), 6807859099481836695); + } }