src/rng.rs 1.4 K · 53 lines · raw · history

1 use std::ops::Range;
2
3 use bevy::prelude::*;
4
5 #[derive(Resource, Clone, Debug)]
6 pub struct SimRng(u64);
7
8 impl SimRng {
9 pub fn new(seed: u64) -> Self {
10 Self(seed)
11 }
12
13 pub fn next_u64(&mut self) -> u64 {
14 self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
15 let mut z = self.0;
16 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
17 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
18 z ^ (z >> 31)
19 }
20
21 pub fn range_i32(&mut self, range: Range<i32>) -> i32 {
22 let span = (range.end - range.start) as u64;
23 range.start + (self.next_u64() % span) as i32
24 }
25
26 pub fn range_f32(&mut self, range: Range<f32>) -> f32 {
27 let unit = (self.next_u64() >> 40) as f32 / (1u64 << 24) as f32;
28 range.start + unit * (range.end - range.start)
29 }
30 }
31
32 #[cfg(test)]
33 mod tests {
34 use super::*;
35
36 #[test]
37 fn same_seed_gives_same_sequence_so_simulations_are_reproducible() {
38 let mut a = SimRng::new(7);
39 let mut b = SimRng::new(7);
40 for _ in 0..100 {
41 assert_eq!(a.next_u64(), b.next_u64());
42 }
43 }
44
45 #[test]
46 fn ranges_stay_within_bounds() {
47 let mut rng = SimRng::new(1);
48 for _ in 0..1000 {
49 assert!((-3..4).contains(&rng.range_i32(-3..4)));
50 assert!((1.0..2.5).contains(&rng.range_f32(1.0..2.5)));
51 }
52 }
53 }