here_be_dragons/lib.rs
1//! Generators for dungeon type maps.
2//!
3//! Generators can bu used directly or they can be combined with
4//! `MapGenerator`s and `MapModifier`s
5//!
6//! * MapGenerators are use to create initial map.
7//! * MapModifiers modify existing map.
8//!
9//! Example
10//! ```
11//! use here_be_dragons::{MapFilter, MapBuilder, Map, NoData, Tile};
12//! use here_be_dragons::filter::{
13//! NoiseGenerator,
14//! CellularAutomata,
15//! starting_point::{AreaStartingPosition, XStart, YStart}
16//! };
17//! use here_be_dragons::geometry::Point;
18//!
19//! let map = MapBuilder::<NoData>::new(80, 50)
20//! .with(NoiseGenerator::uniform())
21//! .with(CellularAutomata::new())
22//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
23//! .build();
24//!
25//! assert_eq!(map.width, 80);
26//! assert_eq!(map.height, 50);
27//! assert_eq!(map.starting_point.is_some(), true);
28//! ```
29//!
30
31pub mod filter;
32pub mod geometry;
33pub mod map;
34pub mod metric;
35
36pub use filter::*;
37pub use map::{Map, NoData, Symmetry, Tile};
38
39pub(crate) mod dijkstra;
40pub(crate) mod random;
41
42use rand::prelude::*;
43use std::time::{SystemTime, UNIX_EPOCH};
44
45/// Trait which should be implemented by map modifier.
46/// Modifier takes initiall map and apply changes to it.
47pub trait MapFilter<D: Clone + Default> {
48 fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D>;
49}
50
51/// Used to chain MapBuilder and MapModifiers to create the final map.
52pub struct MapBuilder<D: Clone + Default> {
53 width: usize,
54 height: usize,
55 modifiers: Vec<Box<dyn MapFilter<D>>>,
56}
57
58impl<D: Clone + Default> MapBuilder<D> {
59 /// Create Map Builder with initial map generator
60 pub fn new(width: usize, height: usize) -> MapBuilder<D> {
61 MapBuilder {
62 width,
63 height,
64 modifiers: Vec::new(),
65 }
66 }
67
68 pub fn with(&mut self, modifier: Box<dyn MapFilter<D>>) -> &mut MapBuilder<D> {
69 self.modifiers.push(modifier);
70 self
71 }
72
73 /// Build map using random number seeded with system time
74 pub fn build(&mut self) -> Map<D> {
75 let system_time = SystemTime::now()
76 .duration_since(UNIX_EPOCH)
77 .expect("Can't access system time");
78 let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64);
79 self.build_with_rng(&mut rng)
80 }
81
82 /// Build map using provided random number generator
83 pub fn build_with_rng(&mut self, rng: &mut StdRng) -> Map<D> {
84 let mut map = Map::new(self.width, self.height);
85
86 // Build additional layers in turn
87 for modifier in self.modifiers.iter() {
88 map = modifier.modify_map(rng, &map);
89 }
90
91 map
92 }
93}
94
95/// ------------------------------------------------------------------------------------------------
96/// Module unit tests
97/// ------------------------------------------------------------------------------------------------
98#[cfg(test)]
99mod tests {
100 use crate::map::NoData;
101
102 use super::*;
103 use filter::{
104 CellularAutomata, NoiseGenerator, {AreaStartingPosition, XStart, YStart},
105 };
106
107 #[test]
108 fn test_ca_map() {
109 let map = MapBuilder::<NoData>::new(80, 50)
110 .with(NoiseGenerator::new(0.55))
111 .with(CellularAutomata::new())
112 .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
113 .build();
114
115 assert_eq!(map.width, 80);
116 assert_eq!(map.height, 50);
117 assert!(map.starting_point.is_some());
118 }
119}