Skip to main content

pinch_points/sim/
rng.rs

1/// Minimal PCG32 (XSH-RR 64/32): the `Board`'s seeded, deterministic PRNG.
2///
3/// Implemented inline rather than pulled in as a dependency so the simulation
4/// core stays dependency-free and the exact sequence is under our control
5/// (replays and rollback depend on it never changing behind our back).
6#[derive(Clone, Debug)]
7pub struct Pcg32 {
8    state: u64,
9    inc: u64,
10}
11
12const MULTIPLIER: u64 = 6364136223846793005;
13
14impl Pcg32 {
15    pub fn new(seed: u64, stream: u64) -> Self {
16        let mut rng = Pcg32 {
17            state: 0,
18            inc: (stream << 1) | 1,
19        };
20        rng.next_u32();
21        rng.state = rng.state.wrapping_add(seed);
22        rng.next_u32();
23        rng
24    }
25
26    pub fn next_u32(&mut self) -> u32 {
27        let old = self.state;
28        self.state = old.wrapping_mul(MULTIPLIER).wrapping_add(self.inc);
29        let xorshifted = (((old >> 18) ^ old) >> 27) as u32;
30        let rot = (old >> 59) as u32;
31        xorshifted.rotate_right(rot)
32    }
33
34    pub(crate) fn hash_state(&self) -> (u64, u64) {
35        (self.state, self.inc)
36    }
37
38    /// Restore a stream mid-sequence, as [`hash_state`](Pcg32::hash_state)
39    /// read it. For snapshots: a board resumed from a fresh `new(seed, ..)`
40    /// would replay draws it has already spent.
41    pub(crate) fn from_state(state: u64, inc: u64) -> Pcg32 {
42        Pcg32 { state, inc }
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::Pcg32;
49
50    #[test]
51    fn same_seed_same_sequence() {
52        let mut a = Pcg32::new(42, 7);
53        let mut b = Pcg32::new(42, 7);
54        for _ in 0..1000 {
55            assert_eq!(a.next_u32(), b.next_u32());
56        }
57    }
58
59    #[test]
60    fn different_seeds_diverge() {
61        let mut a = Pcg32::new(1, 7);
62        let mut b = Pcg32::new(2, 7);
63        let same = (0..100).filter(|_| a.next_u32() == b.next_u32()).count();
64        assert!(same < 5);
65    }
66}