use rand::prelude::*; use super::{MapGenerator, MapModifier}; use super::map::{Map, TileType}; pub struct CellularAutomataGen { width: usize, height: usize } impl MapGenerator for CellularAutomataGen { fn generate_map(&self, rng : &mut StdRng) -> Map { self.build(rng) } } impl CellularAutomataGen { pub fn new(width: usize, height: usize) -> CellularAutomataGen { CellularAutomataGen {width, height} } /// Generate map fn build(&self, rng : &mut StdRng) -> Map { let mut map = Map::new(self.width, self.height); // First we completely randomize the map, setting 55% of it to be floor. for y in 1..self.height-1 { for x in 1..self.width-1 { let roll = rng.next_u32() % 100; if roll > 55 { map.set_tile(x, y, TileType::Floor) } else { map.set_tile(x, y, TileType::Wall) } } } // Now we iteratively apply cellular automata rules for _ in 0..15 { map = apply_iteration(&map); } map } } impl MapModifier for CellularAutomataGen { fn modify_map(&self, _rng: &mut StdRng, map : &Map) -> Map { apply_iteration(map) } } fn apply_iteration(map: &Map) -> Map { let mut new_map = map.clone(); for y in 1..map.height-1 { for x in 1..map.width-1 { let idxs = [ (x-1, y-1), (x, y-1), (x+1, y-1), (x-1, y), (x+1, y), (x-1, y+1), (x, y+1), (x+1, y+1)]; let neighbors = idxs.iter() .filter(|(x, y)| map.at(*x, *y) == TileType::Wall) .count(); if neighbors > 4 || neighbors == 0 { new_map.set_tile(x, y, TileType::Wall) } else { new_map.set_tile(x, y, TileType::Floor); } } } new_map } /// ------------------------------------------------------------------------------------------------ /// Module unit tests /// ------------------------------------------------------------------------------------------------ #[cfg(test)] mod tests { use super::*; #[test] fn test_iteration_wal() { let map = Map::new(3, 3); let new_map = apply_iteration(&map); assert_eq!(new_map.at(1, 1), TileType::Wall); } #[test] fn test_iteration_floor() { let mut map = Map::new(3, 3); for i in 0..3 { for j in 0..2 { map.set_tile(i, j, TileType::Floor); } } let new_map = apply_iteration(&map); assert_eq!(new_map.at(1, 1), TileType::Floor); } }