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}