Skip to main content

advanced_prefabs/
advanced_prefabs.rs

1//! Advanced Prefab System Demo
2//!
3//! Demonstrates JSON support, weighted selection, and transformations
4
5use terrain_forge::{
6    algorithms::{PrefabConfig, PrefabData, PrefabLibrary, PrefabPlacer, PrefabTransform},
7    Algorithm, Grid, Rng, Tile,
8};
9
10fn main() {
11    println!("=== Advanced Prefab System Demo ===\n");
12
13    // Step 1: Create prefab library programmatically
14    println!("1. Creating Prefab Library:");
15    let library = create_sample_library();
16
17    println!(
18        "   Created library with {} prefabs:",
19        library.get_prefabs().len()
20    );
21    for prefab in library.get_prefabs() {
22        println!(
23            "     - {} ({}x{}, weight: {:.1}, tags: {:?})",
24            prefab.name, prefab.width, prefab.height, prefab.weight, prefab.tags
25        );
26    }
27
28    // Step 2: Demonstrate weighted selection
29    println!("\n2. Weighted Selection Test:");
30    let mut rng = Rng::new(12345);
31    let mut selection_counts = std::collections::HashMap::new();
32
33    for _ in 0..100 {
34        if let Some(prefab) = library.select_weighted(&mut rng, None) {
35            *selection_counts.entry(prefab.name.clone()).or_insert(0) += 1;
36        }
37    }
38
39    println!("   Selection frequency (100 trials):");
40    for (name, count) in &selection_counts {
41        println!("     {}: {} times", name, count);
42    }
43
44    // Step 3: Tag-based selection
45    println!("\n3. Tag-based Selection:");
46    let room_prefabs = library.get_by_tag("room");
47    let corridor_prefabs = library.get_by_tag("corridor");
48
49    println!("   Room prefabs: {}", room_prefabs.len());
50    for prefab in &room_prefabs {
51        println!("     - {}", prefab.name);
52    }
53
54    println!("   Corridor prefabs: {}", corridor_prefabs.len());
55    for prefab in &corridor_prefabs {
56        println!("     - {}", prefab.name);
57    }
58
59    // Step 4: Transformation examples
60    println!("\n4. Prefab Transformations:");
61    if let Some(base_prefab) = library.get_prefabs().first() {
62        println!(
63            "   Base prefab '{}' ({}x{}):",
64            base_prefab.name, base_prefab.width, base_prefab.height
65        );
66        print_prefab_pattern(base_prefab);
67
68        // Rotation
69        let rotated = base_prefab.rotated();
70        println!(
71            "   After 90° rotation ({}x{}):",
72            rotated.width, rotated.height
73        );
74        print_prefab_pattern(&rotated);
75
76        // Horizontal mirror
77        let mirrored = base_prefab.mirrored_horizontal();
78        println!(
79            "   After horizontal mirror ({}x{}):",
80            mirrored.width, mirrored.height
81        );
82        print_prefab_pattern(&mirrored);
83
84        // Combined transformation
85        let transform = PrefabTransform {
86            rotation: 1,
87            mirror_h: true,
88            mirror_v: false,
89        };
90        let transformed = transform.apply(base_prefab);
91        println!(
92            "   After rotation + mirror ({}x{}):",
93            transformed.width, transformed.height
94        );
95        print_prefab_pattern(&transformed);
96    }
97
98    // Step 5: Generation with advanced prefabs
99    println!("\n5. Generation with Advanced Prefabs:");
100    let config = PrefabConfig {
101        max_prefabs: 5,
102        min_spacing: 3,
103        allow_rotation: true,
104        allow_mirroring: true,
105        weighted_selection: true,
106        placement_mode: terrain_forge::algorithms::PrefabPlacementMode::Overwrite,
107        tags: None,
108    };
109
110    let placer = PrefabPlacer::new(config, library.clone());
111    let mut grid = Grid::new(30, 25);
112    placer.generate(&mut grid, 54321);
113
114    let floor_count = grid.count(|t| t.is_floor());
115    println!(
116        "   Generated {}x{} grid with {} floor tiles",
117        grid.width(),
118        grid.height(),
119        floor_count
120    );
121
122    print_grid(&grid);
123
124    // Step 6: JSON serialization example
125    println!("\n6. JSON Serialization:");
126    match library.save_to_json("prefab_library.json") {
127        Ok(()) => {
128            println!("   ✅ Saved library to prefab_library.json");
129
130            // Try to load it back
131            match PrefabLibrary::load_from_json("prefab_library.json") {
132                Ok(loaded_library) => {
133                    println!("   ✅ Successfully loaded library back");
134                    println!("   Loaded {} prefabs", loaded_library.get_prefabs().len());
135                }
136                Err(e) => println!("   ❌ Failed to load: {}", e),
137            }
138        }
139        Err(e) => println!("   ❌ Failed to save: {}", e),
140    }
141
142    // Step 7: Performance comparison
143    println!("\n7. Performance Comparison:");
144
145    // Simple generation
146    let simple_config = PrefabConfig {
147        max_prefabs: 10,
148        min_spacing: 2,
149        allow_rotation: false,
150        allow_mirroring: false,
151        weighted_selection: false,
152        placement_mode: terrain_forge::algorithms::PrefabPlacementMode::Overwrite,
153        tags: None,
154    };
155
156    let start = std::time::Instant::now();
157    let simple_placer = PrefabPlacer::new(simple_config, library.clone());
158    let mut simple_grid = Grid::new(40, 30);
159    simple_placer.generate(&mut simple_grid, 98765);
160    let simple_time = start.elapsed();
161
162    // Advanced generation
163    let advanced_config = PrefabConfig {
164        max_prefabs: 10,
165        min_spacing: 2,
166        allow_rotation: true,
167        allow_mirroring: true,
168        weighted_selection: true,
169        placement_mode: terrain_forge::algorithms::PrefabPlacementMode::Overwrite,
170        tags: None,
171    };
172
173    let start = std::time::Instant::now();
174    let advanced_placer = PrefabPlacer::new(advanced_config, library);
175    let mut advanced_grid = Grid::new(40, 30);
176    advanced_placer.generate(&mut advanced_grid, 98765);
177    let advanced_time = start.elapsed();
178
179    println!("   Simple generation: {:?}", simple_time);
180    println!("   Advanced generation: {:?}", advanced_time);
181    println!(
182        "   Overhead: {:.1}x",
183        advanced_time.as_nanos() as f32 / simple_time.as_nanos() as f32
184    );
185
186    println!("\n✅ Advanced prefab system demo complete!");
187    println!("   - JSON serialization for persistent libraries");
188    println!("   - Weighted selection for balanced generation");
189    println!("   - Transformations for variety and reuse");
190    println!("   - Tag-based organization for targeted selection");
191}
192
193fn create_sample_library() -> PrefabLibrary {
194    let mut library = PrefabLibrary::new();
195
196    // Small room (high weight)
197    let small_room = PrefabData {
198        name: "small_room".to_string(),
199        width: 5,
200        height: 5,
201        pattern: vec![
202            "#####".to_string(),
203            "#...#".to_string(),
204            "#...#".to_string(),
205            "#...#".to_string(),
206            "#####".to_string(),
207        ],
208        weight: 3.0,
209        tags: vec!["room".to_string(), "small".to_string()],
210        legend: None,
211    };
212    library.add_prefab(terrain_forge::algorithms::Prefab::from_data(small_room));
213
214    // Large room (medium weight)
215    let large_room = PrefabData {
216        name: "large_room".to_string(),
217        width: 7,
218        height: 6,
219        pattern: vec![
220            "#######".to_string(),
221            "#.....#".to_string(),
222            "#.....#".to_string(),
223            "#.....#".to_string(),
224            "#.....#".to_string(),
225            "#######".to_string(),
226        ],
227        weight: 1.5,
228        tags: vec!["room".to_string(), "large".to_string()],
229        legend: None,
230    };
231    library.add_prefab(terrain_forge::algorithms::Prefab::from_data(large_room));
232
233    // Corridor (low weight)
234    let corridor = PrefabData {
235        name: "corridor".to_string(),
236        width: 7,
237        height: 3,
238        pattern: vec![
239            "#######".to_string(),
240            ".......".to_string(),
241            "#######".to_string(),
242        ],
243        weight: 0.8,
244        tags: vec!["corridor".to_string(), "connection".to_string()],
245        legend: None,
246    };
247    library.add_prefab(terrain_forge::algorithms::Prefab::from_data(corridor));
248
249    // L-shaped room (rare)
250    let mut legend = std::collections::HashMap::new();
251    legend.insert(
252        "M".to_string(),
253        terrain_forge::algorithms::PrefabLegendEntry {
254            tile: Some("floor".to_string()),
255            marker: Some("loot_slot".to_string()),
256            mask: None,
257        },
258    );
259    let l_room = PrefabData {
260        name: "l_shaped_room".to_string(),
261        width: 6,
262        height: 6,
263        pattern: vec![
264            "######".to_string(),
265            "#....#".to_string(),
266            "#....#".to_string(),
267            "#..M##".to_string(),
268            "#..###".to_string(),
269            "######".to_string(),
270        ],
271        weight: 0.5,
272        tags: vec![
273            "room".to_string(),
274            "special".to_string(),
275            "l_shaped".to_string(),
276        ],
277        legend: Some(legend),
278    };
279    library.add_prefab(terrain_forge::algorithms::Prefab::from_data(l_room));
280
281    library
282}
283
284fn print_prefab_pattern(prefab: &terrain_forge::algorithms::Prefab) {
285    for y in 0..prefab.height {
286        print!("     ");
287        for x in 0..prefab.width {
288            print!("{}", if prefab.get(x, y) { "." } else { "#" });
289        }
290        println!();
291    }
292}
293
294fn print_grid(grid: &Grid<Tile>) {
295    println!("   Generated layout:");
296    for y in 0..grid.height().min(12) {
297        print!("     ");
298        for x in 0..grid.width() {
299            let tile = grid.get(x as i32, y as i32).unwrap();
300            print!("{}", if tile.is_floor() { "." } else { "#" });
301        }
302        println!();
303    }
304    if grid.height() > 12 {
305        println!("     ... ({} more rows)", grid.height() - 12);
306    }
307}