terrain_forge/
lib.rs

1//! # TerrainForge
2//!
3//! A modular procedural generation engine for terrain, dungeons, and maps.
4//!
5//! ## Quick Start
6//!
7//! ```rust
8//! use terrain_forge::{Grid, Tile, algorithms};
9//!
10//! let mut grid = Grid::new(80, 60);
11//! let algo = algorithms::get("bsp").unwrap();
12//! algo.generate(&mut grid, 12345);
13//!
14//! println!("Generated {} floor tiles", grid.count(|t| t.is_floor()));
15//! ```
16//!
17//! ## Semantic Extraction
18//!
19//! Extract semantic information from any generated map:
20//!
21//! ```rust
22//! use terrain_forge::{algorithms, SemanticExtractor, Grid, Rng};
23//!
24//! // 1. Generate map using any method
25//! let mut grid = Grid::new(80, 60);
26//! algorithms::get("cellular").unwrap().generate(&mut grid, 12345);
27//!
28//! // 2. Extract semantic information
29//! let extractor = SemanticExtractor::for_caves();
30//! let mut rng = Rng::new(12345);
31//! let semantic = extractor.extract(&grid, &mut rng);
32//!
33//! // Works with any grid source - pipelines, external tools, etc.
34//! ```
35//!
36//! ## Algorithms
37//!
38//! 14 generation algorithms available via [`algorithms::get`]:
39//! - `bsp` - Binary Space Partitioning for structured rooms
40//! - `cellular` - Cellular automata for organic caves
41//! - `drunkard` - Random walk for winding corridors
42//! - `maze` - Perfect maze generation
43//! - `rooms` - Simple rectangular rooms
44//! - `voronoi` - Voronoi-based regions
45//! - `dla` - Diffusion-limited aggregation
46//! - `wfc` - Wave Function Collapse
47//! - `percolation` - Connected cluster generation
48//! - `diamond_square` - Heightmap terrain
49//! - `fractal` - Fractal noise terrain
50//! - `agent` - Multi-agent carving
51//! - `glass_seam` - Region connector
52//! - `room_accretion` - Brogue-style organic dungeons
53//!
54//! ## Composition
55//!
56//! Chain algorithms with [`compose::Pipeline`] or layer with [`compose::LayeredGenerator`]:
57//!
58//! ```rust
59//! use terrain_forge::{Grid, Tile, Algorithm, algorithms};
60//! use terrain_forge::compose::Pipeline;
61//!
62//! let mut grid = Grid::new(80, 60);
63//! let pipeline = Pipeline::new()
64//!     .add(algorithms::get("rooms").unwrap())
65//!     .add(algorithms::get("cellular").unwrap());
66//! pipeline.generate(&mut grid, 12345);
67//! ```
68//!
69//! ## Effects
70//!
71//! Post-process with [`effects`]: morphology, connectivity, filters, transforms.
72//!
73//! ## Noise
74//!
75//! [`noise`] module provides Perlin, Simplex, Value, Worley with FBM and modifiers.
76
77mod algorithm;
78mod grid;
79mod rng;
80mod semantic_extractor;
81mod semantic_visualization;
82
83#[cfg(test)]
84mod semantic_tests;
85
86pub mod algorithms;
87pub mod analysis;
88pub mod compose;
89pub mod constraints;
90pub mod effects;
91pub mod noise;
92pub mod pipeline;
93pub mod semantic;
94pub mod spatial;
95
96pub use algorithm::Algorithm;
97pub use grid::{Cell, Grid, Tile};
98pub use rng::Rng;
99pub use semantic::{ConnectivityGraph, Marker, Masks, Region, SemanticConfig, SemanticLayers};
100pub use semantic_extractor::{extract_semantics, extract_semantics_default, SemanticExtractor};
101pub use semantic_visualization::{
102    visualize_connectivity_graph, visualize_masks, visualize_region_ids, visualize_regions,
103    visualize_semantic_layers, VisualizationConfig,
104};
105
106/// Generate a map with semantic layers using the new extraction approach
107///
108/// **DEPRECATED**: This function is provided for backward compatibility.
109/// For new code, use the decoupled `SemanticExtractor` approach:
110///
111/// ```rust
112/// use terrain_forge::{algorithms, SemanticExtractor, Grid, Rng};
113///
114/// // Instead of this deprecated approach:
115/// // let (grid, semantic) = generate_with_semantic_tuple("cellular", 80, 60, 12345);
116///
117/// // Use this:
118/// let mut grid = Grid::new(80, 60);
119/// algorithms::get("cellular").unwrap().generate(&mut grid, 12345);
120/// let semantic = SemanticExtractor::for_caves().extract(&grid, &mut Rng::new(12345));
121/// ```
122#[deprecated(
123    since = "0.3.0",
124    note = "Use decoupled SemanticExtractor for better flexibility"
125)]
126pub fn generate_with_semantic(
127    algorithm_name: &str,
128    width: usize,
129    height: usize,
130    seed: u64,
131) -> (Grid<Tile>, Option<SemanticLayers>) {
132    let mut grid = Grid::new(width, height);
133    let mut rng = Rng::new(seed);
134
135    // Generate tiles using any algorithm
136    if let Some(algo) = algorithms::get(algorithm_name) {
137        algo.generate(&mut grid, seed);
138    }
139
140    // Extract semantic layers using the new standalone system
141    let extractor = match algorithm_name {
142        "cellular" => SemanticExtractor::for_caves(),
143        "bsp" | "rooms" | "room_accretion" => SemanticExtractor::for_rooms(),
144        "maze" => SemanticExtractor::for_mazes(),
145        _ => SemanticExtractor::default(),
146    };
147
148    let semantic = extractor.extract(&grid, &mut rng);
149
150    (grid, Some(semantic))
151}
152
153/// Generate a map that meets specific semantic requirements
154///
155/// This function attempts to generate a map that satisfies the given semantic requirements
156/// by trying different seeds and validating the results. It provides a simple wrapper
157/// around the existing generation system with requirement validation.
158///
159/// # Arguments
160/// * `algorithm_name` - Name of the generation algorithm to use
161/// * `width` - Grid width
162/// * `height` - Grid height  
163/// * `requirements` - Semantic requirements that must be met
164/// * `max_attempts` - Maximum number of generation attempts (default: 10)
165/// * `base_seed` - Base seed for generation attempts
166///
167/// # Returns
168/// * `Ok((grid, semantic))` - Successfully generated map meeting requirements
169/// * `Err(String)` - Failed to meet requirements after max attempts
170///
171/// # Example
172/// ```rust
173/// use terrain_forge::{generate_with_requirements, semantic::SemanticRequirements};
174///
175/// let requirements = SemanticRequirements::basic_dungeon();
176/// match generate_with_requirements("bsp", 80, 60, requirements, Some(5), 12345) {
177///     Ok((grid, semantic)) => println!("Generated valid dungeon!"),
178///     Err(msg) => println!("Failed: {}", msg),
179/// }
180/// ```
181pub fn generate_with_requirements(
182    algorithm_name: &str,
183    width: usize,
184    height: usize,
185    requirements: semantic::SemanticRequirements,
186    max_attempts: Option<usize>,
187    base_seed: u64,
188) -> Result<(Grid<Tile>, semantic::SemanticLayers), String> {
189    let max_attempts = max_attempts.unwrap_or(10);
190
191    for attempt in 0..max_attempts {
192        let seed = base_seed.wrapping_add(attempt as u64);
193        let mut grid = Grid::new(width, height);
194        let mut rng = Rng::new(seed);
195
196        // Generate using specified algorithm
197        if let Some(algo) = algorithms::get(algorithm_name) {
198            algo.generate(&mut grid, seed);
199        } else {
200            return Err(format!("Unknown algorithm: {}", algorithm_name));
201        }
202
203        // Extract semantic layers
204        let extractor = match algorithm_name {
205            "cellular" => SemanticExtractor::for_caves(),
206            "bsp" | "rooms" | "room_accretion" => SemanticExtractor::for_rooms(),
207            "maze" => SemanticExtractor::for_mazes(),
208            _ => SemanticExtractor::default(),
209        };
210
211        let semantic = extractor.extract(&grid, &mut rng);
212
213        // Validate requirements
214        if requirements.validate(&semantic) {
215            return Ok((grid, semantic));
216        }
217    }
218
219    Err(format!(
220        "Failed to generate map meeting requirements after {} attempts",
221        max_attempts
222    ))
223}