Skip to main content

bones_sim/
rng.rs

1use serde::{Deserialize, Serialize};
2
3/// Tiny deterministic RNG used by the simulator.
4///
5/// This is intentionally simple and reproducible across platforms.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7pub struct DeterministicRng {
8    state: u64,
9}
10
11impl DeterministicRng {
12    /// Create a new deterministic RNG from a seed.
13    #[must_use]
14    pub const fn new(seed: u64) -> Self {
15        Self {
16            state: seed ^ 0x9E37_79B9_7F4A_7C15,
17        }
18    }
19
20    /// Next pseudo-random `u64`.
21    #[must_use]
22    pub const fn next_u64(&mut self) -> u64 {
23        self.state = self
24            .state
25            .wrapping_mul(6_364_136_223_846_793_005)
26            .wrapping_add(1_442_695_040_888_963_407);
27        self.state
28    }
29
30    /// Next value in `[0, upper_exclusive)`.
31    #[must_use]
32    pub const fn next_bounded(&mut self, upper_exclusive: u64) -> u64 {
33        if upper_exclusive == 0 {
34            return 0;
35        }
36        self.next_u64() % upper_exclusive
37    }
38
39    /// Bernoulli trial with integer percent.
40    #[must_use]
41    pub fn hit_rate_percent(&mut self, percent: u8) -> bool {
42        if percent == 0 {
43            return false;
44        }
45        if percent >= 100 {
46            return true;
47        }
48        self.next_bounded(100) < u64::from(percent)
49    }
50}