mapgen/filter/
cellular_automata.rs

1//! Cellular automata map filter.
2//! 
3//! Check this [article](http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels)
4//! for more information about the algorithm behind this generator.
5//! 
6//! This algorithm requires that map first is filtered with some noise.
7//! For example `UniformNoise`. It can also be apply to any other non empty map.
8//! 
9//! Example usage:
10//! ```
11//! use rand::prelude::*;
12//! use mapgen::{MapBuffer, MapFilter};
13//! use mapgen::filter::CellularAutomata;
14//! 
15//! let mut rng = StdRng::seed_from_u64(100);
16//! let gen = CellularAutomata::new();
17//! let map = gen.modify_map(&mut rng, &MapBuffer::new(80, 50));
18//! 
19//! assert_eq!(map.width, 80);
20//! assert_eq!(map.height, 50);
21//! ```
22//! 
23
24use rand::prelude::*;
25use crate::MapFilter;
26use crate::MapBuffer;
27
28
29/// Map filter
30pub struct CellularAutomata {
31    num_iteraction: u32,
32}
33
34impl MapFilter for CellularAutomata {
35    fn modify_map(&self, _rng: &mut StdRng, map: &MapBuffer)  -> MapBuffer {
36        self.build(map)
37    }
38}
39
40impl CellularAutomata {
41    /// Create generator which will create map with the given dimension.
42    pub fn new() -> Box<CellularAutomata> {
43        Box::new(CellularAutomata { num_iteraction: 15})
44    }
45
46    /// Generate map
47    fn build(&self, map: &MapBuffer) -> MapBuffer {
48        let mut new_map = map.clone();
49        for _ in 0..self.num_iteraction {
50            new_map = apply_iteration(&new_map);
51        }
52
53        new_map
54    }
55
56}
57
58fn apply_iteration(map: &MapBuffer) -> MapBuffer {
59    let mut new_map = map.clone();
60
61    for y in 1..map.height-1 {
62        for x in 1..map.width-1 {
63            let idxs = [
64                (x-1, y-1), (x, y-1), (x+1, y-1), 
65                (x-1, y), (x+1, y), 
66                (x-1, y+1), (x, y+1), (x+1, y+1)];
67            let neighbors = idxs.iter()
68                .filter(|(x, y)| map.is_blocked(*x, *y))
69                .count();
70            
71            let walkable = neighbors < 5 && neighbors > 0;
72            new_map.set_walkable(x, y, walkable);
73        }
74    }
75
76    new_map
77}
78
79/// ------------------------------------------------------------------------------------------------
80/// Module unit tests
81/// ------------------------------------------------------------------------------------------------
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_iteration_wal() {
88        let map = MapBuffer::new(3, 3);
89        let new_map = apply_iteration(&map);
90        assert!(new_map.is_blocked(1, 1));
91    }
92
93
94    #[test]
95    fn test_iteration_floor() {
96        let mut map = MapBuffer::new(3, 3);
97        for i in 0..3 {
98            for j in 0..2 {
99                map.set_walkable(i, j, true);
100            }
101        }
102        let new_map = apply_iteration(&map);
103        assert!(new_map.is_walkable(1, 1));
104    }
105
106}