cuqueclicker_lib/game/tree/
rng.rs1pub struct SplitMix64 {
12 state: u64,
13}
14
15impl SplitMix64 {
16 pub fn new(seed: u64) -> Self {
17 Self { state: seed }
18 }
19
20 pub fn from_coords(seed: u64, x: i32, y: i32, salt: u64) -> Self {
24 let mix = mix_u64(
28 seed ^ salt
29 ^ ((x as i64 as u64).rotate_left(13))
30 ^ ((y as i64 as u64)
31 .rotate_left(31)
32 .wrapping_mul(0x9E37_79B9_7F4A_7C15)),
33 );
34 Self::new(mix)
35 }
36
37 pub fn next_u64(&mut self) -> u64 {
38 self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
39 mix_u64(self.state)
40 }
41
42 pub fn next_f64(&mut self) -> f64 {
44 (self.next_u64() >> 11) as f64 / ((1u64 << 53) as f64)
45 }
46
47 pub fn range_f64(&mut self, lo: f64, hi: f64) -> f64 {
49 lo + (hi - lo) * self.next_f64()
50 }
51
52 pub fn range_usize(&mut self, max: usize) -> usize {
56 if max == 0 {
57 return 0;
58 }
59 (self.next_u64() % (max as u64)) as usize
60 }
61
62 pub fn bool_with_prob(&mut self, p: f64) -> bool {
64 self.next_f64() < p
65 }
66}
67
68pub const fn mix_u64(seed: u64) -> u64 {
71 let mut z = seed;
72 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
73 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
74 z ^ (z >> 31)
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn same_seed_same_sequence() {
83 let mut a = SplitMix64::new(42);
84 let mut b = SplitMix64::new(42);
85 for _ in 0..16 {
86 assert_eq!(a.next_u64(), b.next_u64());
87 }
88 }
89
90 #[test]
91 fn different_coords_different_states() {
92 let a = SplitMix64::from_coords(7, 1, 1, 0).state;
93 let b = SplitMix64::from_coords(7, 1, 2, 0).state;
94 assert_ne!(a, b);
95 }
96
97 #[test]
98 fn salt_separates_call_sites() {
99 let a = SplitMix64::from_coords(7, 1, 1, 0).state;
100 let b = SplitMix64::from_coords(7, 1, 1, 1).state;
101 assert_ne!(a, b);
102 }
103
104 #[test]
105 fn range_f64_within_bounds() {
106 let mut r = SplitMix64::new(1234);
107 for _ in 0..1000 {
108 let v = r.range_f64(-2.0, 5.0);
109 assert!((-2.0..5.0).contains(&v), "{}", v);
110 }
111 }
112
113 #[test]
114 fn range_usize_within_bounds() {
115 let mut r = SplitMix64::new(1234);
116 for _ in 0..1000 {
117 let v = r.range_usize(10);
118 assert!(v < 10);
119 }
120 }
121}