use std::ops::Range; use bevy::prelude::*; #[derive(Resource, Clone, Debug)] pub struct SimRng(u64); impl SimRng { pub fn new(seed: u64) -> Self { Self(seed) } pub fn next_u64(&mut self) -> u64 { self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15); let mut z = self.0; z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9); z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); z ^ (z >> 31) } pub fn range_i32(&mut self, range: Range) -> i32 { let span = (range.end - range.start) as u64; range.start + (self.next_u64() % span) as i32 } pub fn range_f32(&mut self, range: Range) -> f32 { let unit = (self.next_u64() >> 40) as f32 / (1u64 << 24) as f32; range.start + unit * (range.end - range.start) } } #[cfg(test)] mod tests { use super::*; #[test] fn same_seed_gives_same_sequence_so_simulations_are_reproducible() { let mut a = SimRng::new(7); let mut b = SimRng::new(7); for _ in 0..100 { assert_eq!(a.next_u64(), b.next_u64()); } } #[test] fn ranges_stay_within_bounds() { let mut rng = SimRng::new(1); for _ in 0..1000 { assert!((-3..4).contains(&rng.range_i32(-3..4))); assert!((1.0..2.5).contains(&rng.range_f32(1.0..2.5))); } } }