Skip to main content

proof_engine/worldgen/
mod.rs

1//! Procedural world generation framework.
2//!
3//! Generates entire mathematical realities: tectonic plates, erosion, climate,
4//! biomes, rivers, caves, settlements, history, languages, mythology, artifacts,
5//! and genetics — all from equations.
6
7pub mod tectonics;
8pub mod erosion;
9pub mod climate;
10pub mod biomes;
11pub mod rivers;
12pub mod caves;
13pub mod settlements;
14pub mod history;
15pub mod language;
16pub mod mythology;
17pub mod artifacts;
18pub mod genetics;
19
20/// Master world seed — all generation derives from this.
21#[derive(Debug, Clone, Copy)]
22pub struct WorldSeed(pub u64);
23
24impl WorldSeed {
25    pub fn new(seed: u64) -> Self { Self(seed) }
26    /// Derive a sub-seed for a specific system.
27    pub fn derive(&self, system_id: u32) -> u64 {
28        let mut h = self.0;
29        h ^= system_id as u64;
30        h = h.wrapping_mul(0x517CC1B727220A95);
31        h ^= h >> 32;
32        h
33    }
34}
35
36/// Simple deterministic RNG (xoshiro256**).
37#[derive(Debug, Clone)]
38pub struct Rng {
39    s: [u64; 4],
40}
41
42impl Rng {
43    pub fn new(seed: u64) -> Self {
44        let mut s = [seed, seed ^ 0xDEADBEEF, seed.wrapping_mul(6364136223846793005), seed ^ 0xCAFEBABE];
45        // Warm up
46        let mut r = Self { s };
47        for _ in 0..20 { r.next_u64(); }
48        r
49    }
50
51    pub fn next_u64(&mut self) -> u64 {
52        let result = self.s[1].wrapping_mul(5).rotate_left(7).wrapping_mul(9);
53        let t = self.s[1] << 17;
54        self.s[2] ^= self.s[0];
55        self.s[3] ^= self.s[1];
56        self.s[1] ^= self.s[2];
57        self.s[0] ^= self.s[3];
58        self.s[2] ^= t;
59        self.s[3] = self.s[3].rotate_left(45);
60        result
61    }
62
63    pub fn next_f32(&mut self) -> f32 {
64        (self.next_u64() >> 40) as f32 / (1u64 << 24) as f32
65    }
66
67    pub fn next_f64(&mut self) -> f64 {
68        (self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
69    }
70
71    pub fn range_u32(&mut self, min: u32, max: u32) -> u32 {
72        if min >= max { return min; }
73        min + (self.next_u64() % (max - min) as u64) as u32
74    }
75
76    pub fn range_f32(&mut self, min: f32, max: f32) -> f32 {
77        min + self.next_f32() * (max - min)
78    }
79
80    pub fn range_usize(&mut self, min: usize, max: usize) -> usize {
81        if min >= max { return min; }
82        min + (self.next_u64() as usize % (max - min))
83    }
84
85    pub fn gaussian(&mut self) -> f64 {
86        let u1 = self.next_f64().max(1e-15);
87        let u2 = self.next_f64();
88        (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos()
89    }
90
91    pub fn shuffle<T>(&mut self, slice: &mut [T]) {
92        for i in (1..slice.len()).rev() {
93            let j = self.next_u64() as usize % (i + 1);
94            slice.swap(i, j);
95        }
96    }
97
98    pub fn pick<'a, T>(&mut self, slice: &'a [T]) -> Option<&'a T> {
99        if slice.is_empty() { return None; }
100        Some(&slice[self.next_u64() as usize % slice.len()])
101    }
102
103    pub fn coin(&mut self, probability: f32) -> bool {
104        self.next_f32() < probability
105    }
106}
107
108/// 2D grid coordinate.
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
110pub struct GridPos {
111    pub x: i32,
112    pub y: i32,
113}
114
115impl GridPos {
116    pub fn new(x: i32, y: i32) -> Self { Self { x, y } }
117
118    pub fn neighbors4(&self) -> [GridPos; 4] {
119        [
120            GridPos::new(self.x - 1, self.y),
121            GridPos::new(self.x + 1, self.y),
122            GridPos::new(self.x, self.y - 1),
123            GridPos::new(self.x, self.y + 1),
124        ]
125    }
126
127    pub fn neighbors8(&self) -> [GridPos; 8] {
128        [
129            GridPos::new(self.x - 1, self.y - 1),
130            GridPos::new(self.x, self.y - 1),
131            GridPos::new(self.x + 1, self.y - 1),
132            GridPos::new(self.x - 1, self.y),
133            GridPos::new(self.x + 1, self.y),
134            GridPos::new(self.x - 1, self.y + 1),
135            GridPos::new(self.x, self.y + 1),
136            GridPos::new(self.x + 1, self.y + 1),
137        ]
138    }
139
140    pub fn distance_sq(&self, other: &GridPos) -> i32 {
141        let dx = self.x - other.x;
142        let dy = self.y - other.y;
143        dx * dx + dy * dy
144    }
145}
146
147/// A 2D heightfield grid used across worldgen systems.
148#[derive(Debug, Clone)]
149pub struct Grid2D {
150    pub width: usize,
151    pub height: usize,
152    pub data: Vec<f32>,
153}
154
155impl Grid2D {
156    pub fn new(width: usize, height: usize) -> Self {
157        Self { width, height, data: vec![0.0; width * height] }
158    }
159
160    pub fn filled(width: usize, height: usize, value: f32) -> Self {
161        Self { width, height, data: vec![value; width * height] }
162    }
163
164    #[inline]
165    pub fn idx(&self, x: usize, y: usize) -> usize { y * self.width + x }
166
167    #[inline]
168    pub fn get(&self, x: usize, y: usize) -> f32 {
169        self.data[y * self.width + x]
170    }
171
172    #[inline]
173    pub fn set(&mut self, x: usize, y: usize, v: f32) {
174        self.data[y * self.width + x] = v;
175    }
176
177    #[inline]
178    pub fn get_clamped(&self, x: i32, y: i32) -> f32 {
179        let cx = x.clamp(0, self.width as i32 - 1) as usize;
180        let cy = y.clamp(0, self.height as i32 - 1) as usize;
181        self.data[cy * self.width + cx]
182    }
183
184    pub fn add(&mut self, x: usize, y: usize, v: f32) {
185        self.data[y * self.width + x] += v;
186    }
187
188    pub fn min_value(&self) -> f32 { self.data.iter().cloned().fold(f32::MAX, f32::min) }
189    pub fn max_value(&self) -> f32 { self.data.iter().cloned().fold(f32::MIN, f32::max) }
190
191    pub fn normalize(&mut self) {
192        let min = self.min_value();
193        let max = self.max_value();
194        let range = (max - min).max(1e-9);
195        for v in &mut self.data { *v = (*v - min) / range; }
196    }
197
198    /// Bilinear sample at fractional coordinates.
199    pub fn sample(&self, x: f32, y: f32) -> f32 {
200        let x0 = (x.floor() as i32).clamp(0, self.width as i32 - 2) as usize;
201        let y0 = (y.floor() as i32).clamp(0, self.height as i32 - 2) as usize;
202        let tx = x.fract().clamp(0.0, 1.0);
203        let ty = y.fract().clamp(0.0, 1.0);
204        let v00 = self.get(x0, y0);
205        let v10 = self.get(x0 + 1, y0);
206        let v01 = self.get(x0, y0 + 1);
207        let v11 = self.get(x0 + 1, y0 + 1);
208        let a = v00 + tx * (v10 - v00);
209        let b = v01 + tx * (v11 - v01);
210        a + ty * (b - a)
211    }
212
213    /// Gradient at a point (finite differences).
214    pub fn gradient(&self, x: usize, y: usize) -> (f32, f32) {
215        let left = self.get_clamped(x as i32 - 1, y as i32);
216        let right = self.get_clamped(x as i32 + 1, y as i32);
217        let down = self.get_clamped(x as i32, y as i32 - 1);
218        let up = self.get_clamped(x as i32, y as i32 + 1);
219        ((right - left) * 0.5, (up - down) * 0.5)
220    }
221}
222
223/// Master world generation parameters.
224#[derive(Debug, Clone)]
225pub struct WorldGenParams {
226    pub seed: WorldSeed,
227    pub grid_size: usize,
228    pub num_plates: usize,
229    pub erosion_iterations: usize,
230    pub climate_iterations: usize,
231    pub history_years: usize,
232    pub num_civilizations: usize,
233    pub num_languages: usize,
234    pub sea_level: f32,
235}
236
237impl Default for WorldGenParams {
238    fn default() -> Self {
239        Self {
240            seed: WorldSeed(42),
241            grid_size: 256,
242            num_plates: 12,
243            erosion_iterations: 50000,
244            climate_iterations: 100,
245            history_years: 5000,
246            num_civilizations: 8,
247            num_languages: 6,
248            sea_level: 0.4,
249        }
250    }
251}
252
253/// The complete generated world.
254#[derive(Debug, Clone)]
255pub struct GeneratedWorld {
256    pub params: WorldGenParams,
257    pub heightmap: Grid2D,
258    pub plates: tectonics::PlateMap,
259    pub temperature: Grid2D,
260    pub precipitation: Grid2D,
261    pub biome_map: biomes::BiomeMap,
262    pub river_network: rivers::RiverNetwork,
263    pub cave_systems: Vec<caves::CaveSystem>,
264    pub settlements: Vec<settlements::Settlement>,
265    pub civilizations: Vec<history::Civilization>,
266    pub languages: Vec<language::Language>,
267    pub myths: Vec<mythology::Myth>,
268    pub artifacts: Vec<artifacts::Artifact>,
269}
270
271/// Generate a complete world from parameters.
272pub fn generate_world(params: WorldGenParams) -> GeneratedWorld {
273    let mut rng = Rng::new(params.seed.0);
274    let sz = params.grid_size;
275
276    // 1. Tectonic plates → base heightmap
277    let (heightmap, plates) = tectonics::generate(sz, params.num_plates, &mut rng);
278
279    // 2. Erosion sculpts the terrain
280    let heightmap = erosion::erode(heightmap, params.erosion_iterations, &mut rng);
281
282    // 3. Climate from heat equation
283    let (temperature, precipitation) = climate::simulate(&heightmap, params.climate_iterations, &mut rng);
284
285    // 4. Biomes from climate
286    let biome_map = biomes::classify(&heightmap, &temperature, &precipitation, params.sea_level);
287
288    // 5. Rivers from rainfall
289    let river_network = rivers::generate(&heightmap, &precipitation, params.sea_level);
290
291    // 6. Cave systems
292    let cave_systems = caves::generate(sz, 5, &mut rng);
293
294    // 7. Settlements
295    let settlements = settlements::place(
296        &heightmap, &biome_map, &river_network, params.num_civilizations * 3, &mut rng,
297    );
298
299    // 8. History
300    let civilizations = history::simulate(
301        &settlements, &biome_map, params.history_years, params.num_civilizations, &mut rng,
302    );
303
304    // 9. Languages
305    let languages = language::generate(params.num_languages, &civilizations, &mut rng);
306
307    // 10. Mythology
308    let myths = mythology::generate(&civilizations, &languages, &mut rng);
309
310    // 11. Artifacts
311    let artifacts = artifacts::generate(&civilizations, &myths, &mut rng);
312
313    GeneratedWorld {
314        params,
315        heightmap,
316        plates,
317        temperature,
318        precipitation,
319        biome_map,
320        river_network,
321        cave_systems,
322        settlements,
323        civilizations,
324        languages,
325        myths,
326        artifacts,
327    }
328}
329
330#[cfg(test)]
331mod tests {
332    use super::*;
333
334    #[test]
335    fn test_rng_deterministic() {
336        let mut a = Rng::new(42);
337        let mut b = Rng::new(42);
338        for _ in 0..100 {
339            assert_eq!(a.next_u64(), b.next_u64());
340        }
341    }
342
343    #[test]
344    fn test_rng_range() {
345        let mut r = Rng::new(123);
346        for _ in 0..1000 {
347            let v = r.range_f32(0.0, 1.0);
348            assert!(v >= 0.0 && v <= 1.0);
349        }
350    }
351
352    #[test]
353    fn test_grid2d() {
354        let mut g = Grid2D::new(4, 4);
355        g.set(2, 3, 1.0);
356        assert_eq!(g.get(2, 3), 1.0);
357        assert_eq!(g.get(0, 0), 0.0);
358    }
359
360    #[test]
361    fn test_grid2d_normalize() {
362        let mut g = Grid2D::new(4, 4);
363        g.set(0, 0, -5.0);
364        g.set(3, 3, 10.0);
365        g.normalize();
366        assert!((g.get(0, 0) - 0.0).abs() < 0.01);
367        assert!((g.get(3, 3) - 1.0).abs() < 0.01);
368    }
369
370    #[test]
371    fn test_world_seed_derive() {
372        let seed = WorldSeed(42);
373        let a = seed.derive(1);
374        let b = seed.derive(2);
375        assert_ne!(a, b);
376    }
377}