proof_engine/procedural/
mod.rs1pub mod dungeon;
15pub mod spawn;
16pub mod names;
17pub mod loot;
18pub mod world;
19pub mod items;
20
21pub use dungeon::{DungeonFloor, Room, Corridor, DungeonTheme};
22pub use spawn::{SpawnTable, SpawnEntry, SpawnResult};
23pub use names::{NameGenerator, NameStyle};
24pub use loot::{LootTable, LootTier, LootDrop};
25
26#[derive(Clone, Debug)]
31pub struct Rng {
32 state: [u64; 4],
33}
34
35impl Rng {
36 pub fn new(seed: u64) -> Self {
37 let mut s = seed;
39 let mut next = || {
40 s = s.wrapping_add(0x9e3779b97f4a7c15);
41 let mut z = s;
42 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
43 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
44 z ^ (z >> 31)
45 };
46 Self { state: [next(), next(), next(), next()] }
47 }
48
49 fn rol64(x: u64, k: u32) -> u64 {
50 (x << k) | (x >> (64 - k))
51 }
52
53 pub fn next_u64(&mut self) -> u64 {
55 let result = Self::rol64(self.state[1].wrapping_mul(5), 7).wrapping_mul(9);
56 let t = self.state[1] << 17;
57 self.state[2] ^= self.state[0];
58 self.state[3] ^= self.state[1];
59 self.state[1] ^= self.state[2];
60 self.state[0] ^= self.state[3];
61 self.state[2] ^= t;
62 self.state[3] = Self::rol64(self.state[3], 45);
63 result
64 }
65
66 pub fn next_f32(&mut self) -> f32 {
68 (self.next_u64() >> 11) as f32 / (1u64 << 53) as f32
69 }
70
71 pub fn range_f32(&mut self, min: f32, max: f32) -> f32 {
73 min + self.next_f32() * (max - min)
74 }
75
76 pub fn range_usize(&mut self, n: usize) -> usize {
78 (self.next_u64() % n as u64) as usize
79 }
80
81 pub fn range_i32(&mut self, min: i32, max: i32) -> i32 {
83 min + (self.next_u64() % ((max - min + 1) as u64)) as i32
84 }
85
86 pub fn chance(&mut self, p: f32) -> bool {
88 self.next_f32() < p
89 }
90
91 pub fn shuffle<T>(&mut self, slice: &mut [T]) {
93 for i in (1..slice.len()).rev() {
94 let j = self.range_usize(i + 1);
95 slice.swap(i, j);
96 }
97 }
98
99 pub fn pick<'a, T>(&mut self, slice: &'a [T]) -> Option<&'a T> {
101 if slice.is_empty() { return None; }
102 Some(&slice[self.range_usize(slice.len())])
103 }
104
105 pub fn pick_weighted<'a, T>(&mut self, items: &'a [(T, f32)]) -> Option<&'a T> {
107 let total: f32 = items.iter().map(|(_, w)| *w).sum();
108 if total <= 0.0 { return None; }
109 let mut r = self.next_f32() * total;
110 for (item, weight) in items {
111 r -= weight;
112 if r <= 0.0 { return Some(item); }
113 }
114 items.last().map(|(t, _)| t)
115 }
116
117 pub fn gaussian(&mut self, mean: f32, stddev: f32) -> f32 {
119 let u1 = self.next_f32().max(1e-10);
120 let u2 = self.next_f32();
121 let z = (-2.0 * u1.ln()).sqrt() * (u2 * std::f32::consts::TAU).cos();
122 mean + z * stddev
123 }
124
125 pub fn poisson(&mut self, lambda: f32) -> u32 {
127 let l = (-lambda).exp();
128 let mut k = 0u32;
129 let mut p = 1.0_f32;
130 loop {
131 k += 1;
132 p *= self.next_f32();
133 if p <= l { break; }
134 }
135 k - 1
136 }
137
138 pub fn fork(&mut self) -> Rng {
140 Rng::new(self.next_u64())
141 }
142}