1use super::Rng;
8
9#[derive(Debug, Clone)]
11pub struct VoxelGrid {
12 pub width: usize,
13 pub height: usize,
14 pub depth: usize,
15 pub solid: Vec<bool>,
17}
18
19impl VoxelGrid {
20 pub fn new(w: usize, h: usize, d: usize) -> Self {
21 Self { width: w, height: h, depth: d, solid: vec![true; w * h * d] }
22 }
23
24 #[inline]
25 pub fn idx(&self, x: usize, y: usize, z: usize) -> usize {
26 z * self.width * self.height + y * self.width + x
27 }
28
29 pub fn get(&self, x: i32, y: i32, z: i32) -> bool {
30 if x < 0 || y < 0 || z < 0 { return true; }
31 let (ux, uy, uz) = (x as usize, y as usize, z as usize);
32 if ux >= self.width || uy >= self.height || uz >= self.depth { return true; }
33 self.solid[self.idx(ux, uy, uz)]
34 }
35
36 pub fn set(&mut self, x: usize, y: usize, z: usize, solid: bool) {
37 let i = self.idx(x, y, z);
38 self.solid[i] = solid;
39 }
40
41 pub fn count_neighbors(&self, x: usize, y: usize, z: usize) -> u32 {
43 let mut count = 0u32;
44 for dz in -1..=1i32 {
45 for dy in -1..=1i32 {
46 for dx in -1..=1i32 {
47 if dx == 0 && dy == 0 && dz == 0 { continue; }
48 if self.get(x as i32 + dx, y as i32 + dy, z as i32 + dz) {
49 count += 1;
50 }
51 }
52 }
53 }
54 count
55 }
56
57 pub fn open_count(&self) -> usize {
59 self.solid.iter().filter(|&&s| !s).count()
60 }
61}
62
63#[derive(Debug, Clone)]
65pub enum CaveFeature {
66 Chamber { center: (usize, usize, usize), volume: usize },
68 Tunnel { cells: Vec<(usize, usize, usize)> },
70 Lake { cells: Vec<(usize, usize, usize)> },
72 Shaft { x: usize, z: usize, y_min: usize, y_max: usize },
74}
75
76#[derive(Debug, Clone)]
78pub struct CaveSystem {
79 pub grid: VoxelGrid,
80 pub features: Vec<CaveFeature>,
81 pub entrances: Vec<(usize, usize, usize)>,
83}
84
85pub fn generate(grid_size: usize, num_systems: usize, rng: &mut Rng) -> Vec<CaveSystem> {
87 let mut systems = Vec::with_capacity(num_systems);
88 let cave_size = grid_size.min(48); for _ in 0..num_systems {
91 let system = generate_single(cave_size, cave_size, cave_size / 2, rng);
92 systems.push(system);
93 }
94
95 systems
96}
97
98fn generate_single(w: usize, h: usize, d: usize, rng: &mut Rng) -> CaveSystem {
100 let mut grid = VoxelGrid::new(w, h, d);
101
102 for z in 0..d {
104 for y in 0..h {
105 for x in 0..w {
106 grid.set(x, y, z, rng.coin(0.55));
107 }
108 }
109 }
110
111 for z in 0..d {
113 for y in 0..h {
114 for x in 0..w {
115 if x == 0 || y == 0 || z == 0 || x == w - 1 || y == h - 1 || z == d - 1 {
116 grid.set(x, y, z, true);
117 }
118 }
119 }
120 }
121
122 for _ in 0..4 {
124 let old = grid.solid.clone();
125 for z in 1..d - 1 {
126 for y in 1..h - 1 {
127 for x in 1..w - 1 {
128 let neighbors = grid.count_neighbors(x, y, z);
129 let idx = grid.idx(x, y, z);
130 grid.solid[idx] = if old[idx] {
131 neighbors >= 13
133 } else {
134 neighbors >= 14
136 };
137 }
138 }
139 }
140 }
141
142 let features = detect_features(&grid);
144
145 let mut entrances = Vec::new();
147 let top_z = d - 2;
148 for y in 1..h - 1 {
149 for x in 1..w - 1 {
150 if !grid.get(x as i32, y as i32, top_z as i32) {
151 entrances.push((x, y, top_z));
152 if entrances.len() >= 4 { break; }
153 }
154 }
155 if entrances.len() >= 4 { break; }
156 }
157
158 CaveSystem { grid, features, entrances }
159}
160
161fn detect_features(grid: &VoxelGrid) -> Vec<CaveFeature> {
163 let mut features = Vec::new();
164 let w = grid.width;
165 let h = grid.height;
166 let d = grid.depth;
167
168 let mut visited = vec![false; w * h * d];
170 for z in 1..d - 1 {
171 for y in 1..h - 1 {
172 for x in 1..w - 1 {
173 let idx = grid.idx(x, y, z);
174 if grid.solid[idx] || visited[idx] { continue; }
175
176 let mut stack = vec![(x, y, z)];
178 let mut cells = Vec::new();
179 while let Some((cx, cy, cz)) = stack.pop() {
180 let ci = grid.idx(cx, cy, cz);
181 if visited[ci] || grid.solid[ci] { continue; }
182 visited[ci] = true;
183 cells.push((cx, cy, cz));
184
185 for &(dx, dy, dz) in &[
186 (1i32, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)
187 ] {
188 let nx = cx as i32 + dx;
189 let ny = cy as i32 + dy;
190 let nz = cz as i32 + dz;
191 if nx > 0 && ny > 0 && nz > 0
192 && (nx as usize) < w - 1 && (ny as usize) < h - 1 && (nz as usize) < d - 1
193 {
194 let ni = grid.idx(nx as usize, ny as usize, nz as usize);
195 if !visited[ni] && !grid.solid[ni] {
196 stack.push((nx as usize, ny as usize, nz as usize));
197 }
198 }
199 }
200 }
201
202 if cells.len() > 50 {
203 let (sx, sy, sz): (usize, usize, usize) = cells.iter()
205 .fold((0, 0, 0), |(ax, ay, az), (x, y, z)| (ax + x, ay + y, az + z));
206 let n = cells.len();
207 features.push(CaveFeature::Chamber {
208 center: (sx / n, sy / n, sz / n),
209 volume: n,
210 });
211 }
212
213 let lake_cells: Vec<_> = cells.iter().filter(|&&(_, _, z)| z <= 2).cloned().collect();
215 if lake_cells.len() > 10 {
216 features.push(CaveFeature::Lake { cells: lake_cells });
217 }
218 }
219 }
220 }
221
222 features
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn test_cave_generation() {
231 let mut rng = Rng::new(42);
232 let systems = generate(32, 2, &mut rng);
233 assert_eq!(systems.len(), 2);
234 for sys in &systems {
235 assert!(sys.grid.open_count() > 0, "cave should have open spaces");
236 }
237 }
238
239 #[test]
240 fn test_voxel_grid() {
241 let mut g = VoxelGrid::new(4, 4, 4);
242 assert!(g.get(0, 0, 0)); g.set(2, 2, 2, false);
244 assert!(!g.get(2, 2, 2));
245 }
246}