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 = grid.clone();
128 let old = &old_grid.solid;
129 for z in 1..d - 1 {
130 for y in 1..h - 1 {
131 for x in 1..w - 1 {
132 let neighbors = old_grid.count_neighbors(x, y, z);
133 let idx = grid.idx(x, y, z);
134 grid.solid[idx] = if old[idx] {
135 neighbors >= 13
137 } else {
138 neighbors >= 14
140 };
141 }
142 }
143 }
144 }
145
146 let features = detect_features(&grid);
148
149 let mut entrances = Vec::new();
151 let top_z = d - 2;
152 for y in 1..h - 1 {
153 for x in 1..w - 1 {
154 if !grid.get(x as i32, y as i32, top_z as i32) {
155 entrances.push((x, y, top_z));
156 if entrances.len() >= 4 { break; }
157 }
158 }
159 if entrances.len() >= 4 { break; }
160 }
161
162 CaveSystem { grid, features, entrances }
163}
164
165fn detect_features(grid: &VoxelGrid) -> Vec<CaveFeature> {
167 let mut features = Vec::new();
168 let w = grid.width;
169 let h = grid.height;
170 let d = grid.depth;
171
172 let mut visited = vec![false; w * h * d];
174 for z in 1..d - 1 {
175 for y in 1..h - 1 {
176 for x in 1..w - 1 {
177 let idx = grid.idx(x, y, z);
178 if grid.solid[idx] || visited[idx] { continue; }
179
180 let mut stack = vec![(x, y, z)];
182 let mut cells = Vec::new();
183 while let Some((cx, cy, cz)) = stack.pop() {
184 let ci = grid.idx(cx, cy, cz);
185 if visited[ci] || grid.solid[ci] { continue; }
186 visited[ci] = true;
187 cells.push((cx, cy, cz));
188
189 for &(dx, dy, dz) in &[
190 (1i32, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)
191 ] {
192 let nx = cx as i32 + dx;
193 let ny = cy as i32 + dy;
194 let nz = cz as i32 + dz;
195 if nx > 0 && ny > 0 && nz > 0
196 && (nx as usize) < w - 1 && (ny as usize) < h - 1 && (nz as usize) < d - 1
197 {
198 let ni = grid.idx(nx as usize, ny as usize, nz as usize);
199 if !visited[ni] && !grid.solid[ni] {
200 stack.push((nx as usize, ny as usize, nz as usize));
201 }
202 }
203 }
204 }
205
206 if cells.len() > 50 {
207 let (sx, sy, sz): (usize, usize, usize) = cells.iter()
209 .fold((0, 0, 0), |(ax, ay, az), (x, y, z)| (ax + x, ay + y, az + z));
210 let n = cells.len();
211 features.push(CaveFeature::Chamber {
212 center: (sx / n, sy / n, sz / n),
213 volume: n,
214 });
215 }
216
217 let lake_cells: Vec<_> = cells.iter().filter(|&&(_, _, z)| z <= 2).cloned().collect();
219 if lake_cells.len() > 10 {
220 features.push(CaveFeature::Lake { cells: lake_cells });
221 }
222 }
223 }
224 }
225
226 features
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[test]
234 fn test_cave_generation() {
235 let mut rng = Rng::new(42);
236 let systems = generate(32, 2, &mut rng);
237 assert_eq!(systems.len(), 2);
238 for sys in &systems {
239 assert!(sys.grid.open_count() > 0, "cave should have open spaces");
240 }
241 }
242
243 #[test]
244 fn test_voxel_grid() {
245 let mut g = VoxelGrid::new(4, 4, 4);
246 assert!(g.get(0, 0, 0)); g.set(2, 2, 2, false);
248 assert!(!g.get(2, 2, 2));
249 }
250}