here_be_dragons/src/dungeon/cellular_automata.rs
2020-08-31 22:03:48 +02:00

103 lines
2.6 KiB
Rust

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);
}
}