Add ability to associate arbitrary BuilderData with maps.

I'm finding that, for best results, I need to integrate everything into my map generation process. So for instance, object/monster spawns need to run as a filter so they can influence future steps.

This associates a `Clone + Default` type with maps and makes it available to filters. `NoData` exists for the current behavior.

All examples/tests/demos have been updated accordingly.
This commit is contained in:
Nolan Darilek 2022-03-12 09:45:42 -06:00
parent 1ef42b11ad
commit 957dcca447
20 changed files with 268 additions and 183 deletions

View File

@ -1,7 +1,7 @@
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use web_sys; use web_sys;
use rand::prelude::*; use rand::prelude::*;
use mapgen::{Map, MapBuilder, geometry::Point}; use mapgen::{Map, MapBuilder, NoData, geometry::Point};
use mapgen::filter::*; use mapgen::filter::*;
use mapgen::metric; use mapgen::metric;
@ -19,7 +19,7 @@ pub struct World {
width: u32, width: u32,
height: u32, height: u32,
tiles: Vec<Cell>, tiles: Vec<Cell>,
map: Map, map: Map<NoData>,
} }
#[wasm_bindgen] #[wasm_bindgen]
@ -32,7 +32,7 @@ pub struct Position {
#[wasm_bindgen] #[wasm_bindgen]
impl World { impl World {
fn new(width: u32, height: u32, map: Map) -> World { fn new(width: u32, height: u32, map: Map<NoData>) -> World {
let tiles = (0..map.tiles.len()) let tiles = (0..map.tiles.len())
.map(|i| if map.tiles[i].is_walkable() {Cell::Floor} else {Cell::Wall}) .map(|i| if map.tiles[i].is_walkable() {Cell::Floor} else {Cell::Wall})
.collect(); .collect();
@ -176,7 +176,7 @@ impl World {
div.set_inner_html(&info); div.set_inner_html(&info);
} }
fn print_map_metrics(map: &Map) { fn print_map_metrics(map: &Map<NoData>) {
let window = web_sys::window().expect("no global `window` exists"); let window = web_sys::window().expect("no global `window` exists");
let document = window.document().expect("should have a document on window"); let document = window.document().expect("should have a document on window");
let div = document.get_element_by_id("map-metrics").expect("Need div with id: map-metrics"); let div = document.get_element_by_id("map-metrics").expect("Need div with id: map-metrics");

View File

@ -4,7 +4,7 @@ use mapgen::*;
fn main() { fn main() {
let mut rng = StdRng::seed_from_u64(907647352); let mut rng = StdRng::seed_from_u64(907647352);
let gen = BspInterior::new(); let gen = BspInterior::<NoData>::new();
let map = gen.modify_map(&mut rng, &Map::new(20, 10)); let map = gen.modify_map(&mut rng, &Map::new(20, 10));
println!("{:}", &map); println!("{:}", &map);
} }

View File

@ -6,7 +6,7 @@ use mapgen::*;
fn main() { fn main() {
let system_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Can't access system time"); let system_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Can't access system time");
let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64); let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64);
let gen = BspRooms::new(); let gen = BspRooms::<NoData>::new();
let map = gen.modify_map(&mut rng, &Map::new(80, 50)); let map = gen.modify_map(&mut rng, &Map::new(80, 50));
println!("{:}", &map); println!("{:}", &map);
} }

View File

@ -1,5 +1,6 @@
use mapgen::{ use mapgen::{
MapBuilder, MapBuilder,
NoData,
filter::{ filter::{
NoiseGenerator, NoiseGenerator,
CellularAutomata, CellularAutomata,
@ -12,7 +13,7 @@ use mapgen::{
fn main() { fn main() {
let map = MapBuilder::new(20, 20) let map = MapBuilder::<NoData>::new(20, 20)
.with(NoiseGenerator::uniform()) .with(NoiseGenerator::uniform())
.with(CellularAutomata::new()) .with(CellularAutomata::new())
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))

View File

@ -25,25 +25,26 @@
//! --- //! ---
//! //!
use std::collections::VecDeque; use std::{collections::VecDeque, marker::PhantomData};
use std::f32::MAX; use std::f32::MAX;
use super::map::Map; use super::map::{BuilderData, Map};
/// Representation of a Dijkstra flow map. /// Representation of a Dijkstra flow map.
/// map is a vector of floats, having a size equal to size_x * size_y (one per tile). /// map is a vector of floats, having a size equal to size_x * size_y (one per tile).
/// size_x and size_y are stored for overflow avoidance. /// size_x and size_y are stored for overflow avoidance.
/// max_depth is the maximum number of iterations this search shall support. /// max_depth is the maximum number of iterations this search shall support.
pub struct DijkstraMap { pub struct DijkstraMap<D> {
pub tiles: Vec<f32>, pub tiles: Vec<f32>,
size_x: usize, size_x: usize,
size_y: usize, size_y: usize,
max_depth: f32, max_depth: f32,
phantom: PhantomData<D>,
} }
impl DijkstraMap { impl<D: BuilderData> DijkstraMap<D> {
//! Construct a new Dijkstra map, ready to run. //! Construct a new Dijkstra map, ready to run.
pub fn new(map: &Map) -> DijkstraMap { pub fn new(map: &Map<D>) -> DijkstraMap<D> {
let len = map.width * map.height; let len = map.width * map.height;
let tiles = vec![MAX; len]; let tiles = vec![MAX; len];
let mut d = DijkstraMap { let mut d = DijkstraMap {
@ -51,6 +52,7 @@ impl DijkstraMap {
size_x: map.width, size_x: map.width,
size_y: map.height, size_y: map.height,
max_depth: len as f32, max_depth: len as f32,
phantom: PhantomData,
}; };
d.build(map); d.build(map);
d d
@ -61,7 +63,7 @@ impl DijkstraMap {
/// depth is further than the current depth. /// depth is further than the current depth.
/// WARNING: Will give incorrect results when used with non-uniform exit costs. Much slower /// WARNING: Will give incorrect results when used with non-uniform exit costs. Much slower
/// algorithm required to support that. /// algorithm required to support that.
fn build(&mut self, map: &Map) { fn build(&mut self, map: &Map<D>) {
let mapsize: usize = (self.size_x * self.size_y) as usize; let mapsize: usize = (self.size_x * self.size_y) as usize;
let mut open_list: VecDeque<((usize, usize), f32)> = VecDeque::with_capacity(mapsize); let mut open_list: VecDeque<((usize, usize), f32)> = VecDeque::with_capacity(mapsize);
@ -97,7 +99,7 @@ impl DijkstraMap {
mod tests { mod tests {
use super::*; use super::*;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::Map; use crate::map::{Map, NoData};
#[test] #[test]
fn test_culling() { fn test_culling() {
@ -106,7 +108,7 @@ mod tests {
# # # # # #
########## ##########
"; ";
let mut map = Map::from_string(map_str); let mut map = Map::<NoData>::from_string(map_str);
map.starting_point = Some(Point::new(8, 1)); map.starting_point = Some(Point::new(8, 1));
let dm = DijkstraMap::new(&map); let dm = DijkstraMap::new(&map);
@ -134,7 +136,7 @@ mod tests {
# # # #
#### ####
"; ";
let mut map = Map::from_string(map_str); let mut map = Map::<NoData>::from_string(map_str);
map.starting_point = Some(Point::new(2, 2)); map.starting_point = Some(Point::new(2, 2));
let dm = DijkstraMap::new(&map); let dm = DijkstraMap::new(&map);
let expected = [MAX, MAX, MAX, MAX, let expected = [MAX, MAX, MAX, MAX,
@ -153,7 +155,7 @@ mod tests {
# # # # # #
########## ##########
"; ";
let mut map = Map::from_string(map_str); let mut map = Map::<NoData>::from_string(map_str);
map.starting_point = Some(Point::new(8, 2)); map.starting_point = Some(Point::new(8, 2));
let dm = DijkstraMap::new(&map); let dm = DijkstraMap::new(&map);
let expected = [MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, let expected = [MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX,

View File

@ -6,13 +6,13 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{Map, MapFilter}; //! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::{ //! use mapgen::filter::{
//! BspInterior //! BspInterior
//! }; //! };
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = BspInterior::new(); //! let gen = BspInterior::<NoData>::new();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
@ -20,32 +20,36 @@
//! ``` //! ```
//! //!
use std::marker::PhantomData;
use rand::prelude::*; use rand::prelude::*;
use crate::MapFilter; use crate::{BuilderData, MapFilter};
use crate::geometry::{Point, Rect}; use crate::geometry::{Point, Rect};
use crate::random::Rng; use crate::random::Rng;
use crate::Map; use crate::Map;
pub struct BspInterior { pub struct BspInterior<D: BuilderData> {
min_room_size: usize, min_room_size: usize,
phantom: PhantomData<D>,
} }
impl MapFilter for BspInterior { impl<D: BuilderData> MapFilter<D> for BspInterior<D> {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(rng, map) self.build(rng, map)
} }
} }
impl BspInterior { impl<D: BuilderData> BspInterior<D> {
pub fn new() -> Box<BspInterior> { pub fn new() -> Box<BspInterior<D>> {
Box::new(BspInterior{ Box::new(BspInterior{
min_room_size: 8, min_room_size: 8,
phantom: PhantomData,
}) })
} }
fn build(&self, rng: &mut StdRng, map: &Map) -> Map { fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
let mut rects: Vec<Rect> = Vec::new(); let mut rects: Vec<Rect> = Vec::new();
rects.push( Rect::new(1, 1, new_map.width-2, new_map.height-2) ); rects.push( Rect::new(1, 1, new_map.width-2, new_map.height-2) );
@ -114,12 +118,12 @@ impl BspInterior {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{Map}; use crate::{Map, map::NoData};
#[test] #[test]
fn no_corridors_on_borders() { fn no_corridors_on_borders() {
let mut rng = StdRng::seed_from_u64(907647352); let mut rng = StdRng::seed_from_u64(907647352);
let gen = BspInterior::new(); let gen = BspInterior::<NoData>::new();
let map = gen.modify_map(&mut rng, &Map::new(80, 50)); let map = gen.modify_map(&mut rng, &Map::new(80, 50));
for i in 0..80 { for i in 0..80 {
assert!(map.at(i, 0).is_blocked()); assert!(map.at(i, 0).is_blocked());

View File

@ -6,11 +6,11 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{Map, MapFilter}; //! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::BspRooms; //! use mapgen::filter::BspRooms;
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = BspRooms::new(); //! let gen = BspRooms::<NoData>::new();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
@ -18,31 +18,36 @@
//! ``` //! ```
//! //!
use std::marker::PhantomData;
use rand::prelude::*; use rand::prelude::*;
use crate::BuilderData;
use crate::MapFilter; use crate::MapFilter;
use crate::geometry::Rect; use crate::geometry::Rect;
use crate::random::Rng; use crate::random::Rng;
use crate::Map; use crate::Map;
pub struct BspRooms { pub struct BspRooms<D: BuilderData> {
max_split: usize, max_split: usize,
phantom: PhantomData<D>,
} }
impl MapFilter for BspRooms { impl<D: BuilderData> MapFilter<D> for BspRooms<D> {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build_rooms(map, rng) self.build_rooms(map, rng)
} }
} }
impl BspRooms { impl<D: BuilderData> BspRooms<D> {
pub fn new() -> Box<BspRooms> { pub fn new() -> Box<BspRooms<D>> {
Box::new(BspRooms { Box::new(BspRooms {
max_split: 240, max_split: 240,
phantom: PhantomData,
}) })
} }
fn build_rooms(&self, map: &Map, rng : &mut StdRng) -> Map { fn build_rooms(&self, map: &Map<D>, rng : &mut StdRng) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
let mut rects: Vec<Rect> = Vec::new(); let mut rects: Vec<Rect> = Vec::new();
// Start with a single map-sized rectangle // Start with a single map-sized rectangle
@ -102,7 +107,7 @@ impl BspRooms {
result result
} }
fn is_possible(&self, rect: Rect, map: &Map) -> bool { fn is_possible(&self, rect: Rect, map: &Map<D>) -> bool {
let mut expanded = rect; let mut expanded = rect;
expanded.x1 -= 2; expanded.x1 -= 2;
expanded.x2 += 2; expanded.x2 += 2;
@ -139,12 +144,12 @@ impl BspRooms {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::map::Map; use crate::map::{Map, NoData};
#[test] #[test]
fn no_corridors_on_borders() { fn no_corridors_on_borders() {
let mut rng = StdRng::seed_from_u64(907647352); let mut rng = StdRng::seed_from_u64(907647352);
let gen = BspRooms::new(); let gen = BspRooms::<NoData>::new();
let map = gen.modify_map(&mut rng, &Map::new(80, 50)); let map = gen.modify_map(&mut rng, &Map::new(80, 50));
for i in 0..80 { for i in 0..80 {
assert!(map.at(i, 0).is_blocked()); assert!(map.at(i, 0).is_blocked());

View File

@ -9,11 +9,11 @@
//! Example usage: //! Example usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{Map, MapFilter}; //! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::CellularAutomata; //! use mapgen::filter::CellularAutomata;
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = CellularAutomata::new(); //! let gen = CellularAutomata::<NoData>::new();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
@ -21,30 +21,37 @@
//! ``` //! ```
//! //!
use std::marker::PhantomData;
use rand::prelude::*; use rand::prelude::*;
use crate::BuilderData;
use crate::MapFilter; use crate::MapFilter;
use crate::{Map, Tile}; use crate::{Map, Tile};
/// Map filter /// Map filter
pub struct CellularAutomata { pub struct CellularAutomata<D: BuilderData> {
num_iteraction: u32, num_iteraction: u32,
phantom: PhantomData<D>,
} }
impl MapFilter for CellularAutomata { impl<D: BuilderData> MapFilter<D> for CellularAutomata<D> {
fn modify_map(&self, _rng: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _rng: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(map) self.build(map)
} }
} }
impl CellularAutomata { impl<D: BuilderData> CellularAutomata<D> {
/// Create generator which will create map with the given dimension. /// Create generator which will create map with the given dimension.
pub fn new() -> Box<CellularAutomata> { pub fn new() -> Box<CellularAutomata<D>> {
Box::new(CellularAutomata { num_iteraction: 15}) Box::new(CellularAutomata {
num_iteraction: 15,
phantom: PhantomData,
})
} }
/// Generate map /// Generate map
fn build(&self, map: &Map) -> Map { fn build(&self, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
for _ in 0..self.num_iteraction { for _ in 0..self.num_iteraction {
new_map = apply_iteration(&new_map); new_map = apply_iteration(&new_map);
@ -55,7 +62,7 @@ impl CellularAutomata {
} }
fn apply_iteration(map: &Map) -> Map { fn apply_iteration<D: BuilderData>(map: &Map<D>) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
for y in 1..map.height-1 { for y in 1..map.height-1 {
@ -85,11 +92,13 @@ fn apply_iteration(map: &Map) -> Map {
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::map::NoData;
use super::*; use super::*;
#[test] #[test]
fn test_iteration_wal() { fn test_iteration_wal() {
let map = Map::new(3, 3); let map = Map::<NoData>::new(3, 3);
let new_map = apply_iteration(&map); let new_map = apply_iteration(&map);
assert!(new_map.at(1, 1).is_blocked()); assert!(new_map.at(1, 1).is_blocked());
} }
@ -97,7 +106,7 @@ mod tests {
#[test] #[test]
fn test_iteration_floor() { fn test_iteration_floor() {
let mut map = Map::new(3, 3); let mut map = Map::<NoData>::new(3, 3);
for i in 0..3 { for i in 0..3 {
for j in 0..2 { for j in 0..2 {
map.set_tile(i, j, Tile::floor()); map.set_tile(i, j, Tile::floor());

View File

@ -4,28 +4,34 @@
//! It will add wall on every tile which is not accessible from the starting point. //! It will add wall on every tile which is not accessible from the starting point.
//! //!
use std::marker::PhantomData;
use rand::prelude::StdRng; use rand::prelude::StdRng;
use crate::MapFilter; use crate::MapFilter;
use crate::{Map, Tile}; use crate::{BuilderData, Map, Tile};
use crate::dijkstra::DijkstraMap; use crate::dijkstra::DijkstraMap;
/// Remove unreachable areas from the map. /// Remove unreachable areas from the map.
pub struct CullUnreachable {} pub struct CullUnreachable<D: BuilderData> {
phantom: PhantomData<D>,
}
impl MapFilter for CullUnreachable { impl<D: BuilderData> MapFilter<D> for CullUnreachable<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(map) self.build(map)
} }
} }
impl CullUnreachable { impl<D: BuilderData> CullUnreachable<D> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn new() -> Box<CullUnreachable> { pub fn new() -> Box<CullUnreachable<D>> {
Box::new(CullUnreachable{}) Box::new(CullUnreachable {
phantom: PhantomData,
})
} }
fn build(&self, map: &Map) -> Map { fn build(&self, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
let dijkstra_map = DijkstraMap::new(map); let dijkstra_map = DijkstraMap::new(map);
@ -51,7 +57,7 @@ mod tests {
use super::*; use super::*;
use super::MapFilter; use super::MapFilter;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::Map; use crate::map::{Map, NoData};
#[test] #[test]
fn test_culling() { fn test_culling() {
@ -60,14 +66,14 @@ mod tests {
# # # # # #
########## ##########
"; ";
let mut map = Map::from_string(map_str); let mut map = Map::<NoData>::from_string(map_str);
map.starting_point = Some(Point::new(9, 1)); map.starting_point = Some(Point::new(9, 1));
let expected_map_str = " let expected_map_str = "
########## ##########
#### # #### #
########## ##########
"; ";
let expected_map = Map::from_string(expected_map_str); let expected_map = Map::<NoData>::from_string(expected_map_str);
let modifier = CullUnreachable::new(); let modifier = CullUnreachable::new();

View File

@ -5,7 +5,9 @@
//! //!
use std::f32; use std::f32;
use std::marker::PhantomData;
use rand::prelude::StdRng; use rand::prelude::StdRng;
use crate::BuilderData;
use crate::geometry::Point; use crate::geometry::Point;
use crate::MapFilter; use crate::MapFilter;
use crate::Map; use crate::Map;
@ -13,21 +15,25 @@ use crate::dijkstra::DijkstraMap;
/// Add exist position to the map based on the distance from the start point. /// Add exist position to the map based on the distance from the start point.
pub struct DistantExit {} pub struct DistantExit<D: BuilderData> {
phantom: PhantomData<D>,
}
impl MapFilter for DistantExit { impl<D: BuilderData> MapFilter<D> for DistantExit<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(map) self.build(map)
} }
} }
impl DistantExit { impl<D: BuilderData> DistantExit<D> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn new() -> Box<DistantExit> { pub fn new() -> Box<DistantExit<D>> {
Box::new(DistantExit{}) Box::new(DistantExit {
phantom: PhantomData,
})
} }
fn build(&self, map: &Map) -> Map { fn build(&self, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
let mut best_idx = 0; let mut best_idx = 0;
@ -55,7 +61,7 @@ mod tests {
use super::*; use super::*;
use super::MapFilter; use super::MapFilter;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::Map; use crate::map::{Map, NoData};
#[test] #[test]
fn test_exit() { fn test_exit() {
@ -65,7 +71,7 @@ mod tests {
# # # # # #
########## ##########
"; ";
let mut map = Map::from_string(map_str); let mut map = Map::<NoData>::from_string(map_str);
map.starting_point = Some(Point::new(9, 2)); map.starting_point = Some(Point::new(9, 2));
let modifier = DistantExit::new(); let modifier = DistantExit::new();

View File

@ -1,11 +1,11 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{Map, MapFilter}; //! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::DrunkardsWalk; //! use mapgen::filter::DrunkardsWalk;
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = DrunkardsWalk::open_area(); //! let gen = DrunkardsWalk::<NoData>::open_area();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
@ -13,9 +13,12 @@
//! ``` //! ```
//! //!
use std::marker::PhantomData;
use rand::prelude::*; use rand::prelude::*;
use crate::MapFilter; use crate::MapFilter;
use crate::{ use crate::{
BuilderData,
map::{Map, Symmetry, Tile}, map::{Map, Symmetry, Tile},
geometry::Point, geometry::Point,
random::Rng random::Rng
@ -25,57 +28,59 @@ use crate::{
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone)]
pub enum DrunkSpawnMode { StartingPoint, Random } pub enum DrunkSpawnMode { StartingPoint, Random }
pub struct DrunkardsWalk { pub struct DrunkardsWalk<D: BuilderData> {
spawn_mode : DrunkSpawnMode, spawn_mode : DrunkSpawnMode,
drunken_lifetime : i32, drunken_lifetime : i32,
floor_percent: f32, floor_percent: f32,
brush_size: usize, brush_size: usize,
symmetry: Symmetry symmetry: Symmetry,
phantom: PhantomData<D>,
} }
impl MapFilter for DrunkardsWalk { impl<D: BuilderData> MapFilter<D> for DrunkardsWalk<D> {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(rng, map) self.build(rng, map)
} }
} }
impl DrunkardsWalk { impl<D: BuilderData> DrunkardsWalk<D> {
pub fn new( spawn_mode: DrunkSpawnMode, pub fn new( spawn_mode: DrunkSpawnMode,
drunken_lifetime: i32, drunken_lifetime: i32,
floor_percent: f32, floor_percent: f32,
brush_size: usize, brush_size: usize,
symmetry: Symmetry) -> Box<DrunkardsWalk> symmetry: Symmetry) -> Box<DrunkardsWalk<D>>
{ {
Box::new(DrunkardsWalk{ Box::new(DrunkardsWalk{
spawn_mode, spawn_mode,
drunken_lifetime, drunken_lifetime,
floor_percent, floor_percent,
brush_size, brush_size,
symmetry symmetry,
phantom: PhantomData,
}) })
} }
pub fn open_area() -> Box<DrunkardsWalk> { pub fn open_area() -> Box<DrunkardsWalk<D>> {
Self::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None) Self::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None)
} }
pub fn open_halls() -> Box<DrunkardsWalk> { pub fn open_halls() -> Box<DrunkardsWalk<D>> {
Self::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None) Self::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None)
} }
pub fn winding_passages() -> Box<DrunkardsWalk> { pub fn winding_passages() -> Box<DrunkardsWalk<D>> {
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None) Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None)
} }
pub fn fat_passages() -> Box<DrunkardsWalk> { pub fn fat_passages() -> Box<DrunkardsWalk<D>> {
Self::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None) Self::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None)
} }
pub fn fearful_symmetry() -> Box<DrunkardsWalk> { pub fn fearful_symmetry() -> Box<DrunkardsWalk<D>> {
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both) Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
} }
fn build(&self, rng: &mut StdRng, map: &Map) -> Map { fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
// Set a central starting point // Set a central starting point
let starting_position = Point::new( new_map.width / 2, new_map.height / 2 ); let starting_position = Point::new( new_map.width / 2, new_map.height / 2 );

View File

@ -1,11 +1,11 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{Map, MapFilter}; //! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::MazeBuilder; //! use mapgen::filter::MazeBuilder;
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = MazeBuilder::new(); //! let gen = MazeBuilder::<NoData>::new();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
@ -13,29 +13,35 @@
//! ``` //! ```
//! //!
use std::marker::PhantomData;
use rand::prelude::*; use rand::prelude::*;
use crate::MapFilter; use crate::MapFilter;
use crate::{ use crate::{
map::{Map, Tile}, map::{BuilderData, Map, Tile},
random::Rng random::Rng
}; };
pub struct MazeBuilder {} pub struct MazeBuilder<D: BuilderData> {
phantom: PhantomData<D>,
}
impl MapFilter for MazeBuilder { impl<D: BuilderData> MapFilter<D> for MazeBuilder<D> {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(rng, map) self.build(rng, map)
} }
} }
impl MazeBuilder { impl<D: BuilderData> MazeBuilder<D> {
pub fn new() -> Box<MazeBuilder> { pub fn new() -> Box<MazeBuilder<D>> {
Box::new(MazeBuilder{}) Box::new(MazeBuilder {
phantom: PhantomData,
})
} }
#[allow(clippy::map_entry)] #[allow(clippy::map_entry)]
fn build(&self, rng: &mut StdRng, map: &Map) -> Map { fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
let mut maze = Grid::new((map.width as i32/ 2)-2, (map.height as i32/ 2)-2, rng); let mut maze = Grid::new((map.width as i32/ 2)-2, (map.height as i32/ 2)-2, rng);
maze.generate_maze(&mut new_map); maze.generate_maze(&mut new_map);
@ -91,24 +97,26 @@ impl Cell {
} }
} }
struct Grid<'a> { struct Grid<'a, D: BuilderData> {
width: i32, width: i32,
height: i32, height: i32,
cells: Vec<Cell>, cells: Vec<Cell>,
backtrace: Vec<usize>, backtrace: Vec<usize>,
current: usize, current: usize,
rng : &'a mut StdRng rng : &'a mut StdRng,
phantom: PhantomData<D>,
} }
impl<'a> Grid<'a> { impl<'a, D: BuilderData> Grid<'a, D> {
fn new(width: i32, height:i32, rng: &mut StdRng) -> Grid { fn new(width: i32, height:i32, rng: &mut StdRng) -> Grid<D> {
let mut grid = Grid{ let mut grid = Grid{
width, width,
height, height,
cells: Vec::new(), cells: Vec::new(),
backtrace: Vec::new(), backtrace: Vec::new(),
current: 0, current: 0,
rng rng,
phantom: PhantomData,
}; };
for row in 0..height { for row in 0..height {
@ -162,7 +170,7 @@ impl<'a> Grid<'a> {
None None
} }
fn generate_maze(&mut self, map: &mut Map) { fn generate_maze(&mut self, map: &mut Map<D>) {
let mut i = 0; let mut i = 0;
loop { loop {
self.cells[self.current].visited = true; self.cells[self.current].visited = true;
@ -199,7 +207,7 @@ impl<'a> Grid<'a> {
} }
} }
fn copy_to_map(&self, map: &mut Map) { fn copy_to_map(&self, map: &mut Map<D>) {
// Clear the map // Clear the map
for i in map.tiles.iter_mut() { *i = Tile::wall(); } for i in map.tiles.iter_mut() { *i = Tile::wall(); }

View File

@ -4,11 +4,11 @@
//! Example usage: //! Example usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{Map, MapFilter}; //! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::NoiseGenerator; //! use mapgen::filter::NoiseGenerator;
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = NoiseGenerator::uniform(); //! let gen = NoiseGenerator::<NoData>::uniform();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
@ -16,39 +16,44 @@
//! ``` //! ```
//! //!
use std::marker::PhantomData;
use rand::prelude::*; use rand::prelude::*;
use crate::MapFilter; use crate::MapFilter;
use crate::{Map, Tile}; use crate::{BuilderData, Map, Tile};
/// Map noise generator /// Map noise generator
pub struct NoiseGenerator { pub struct NoiseGenerator<D: BuilderData> {
prob: f32, prob: f32,
phantom: PhantomData<D>,
} }
impl MapFilter for NoiseGenerator { impl<D: BuilderData> MapFilter<D> for NoiseGenerator<D> {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(map, rng) self.build(map, rng)
} }
} }
impl NoiseGenerator { impl<D: BuilderData> NoiseGenerator<D> {
/// Create noise with custom probability /// Create noise with custom probability
pub fn new(prob: f32) -> Box<NoiseGenerator> { pub fn new(prob: f32) -> Box<NoiseGenerator<D>> {
Box::new(NoiseGenerator { Box::new(NoiseGenerator {
prob, prob,
phantom: PhantomData,
}) })
} }
/// Create uniform noise (Probablity 0.5) /// Create uniform noise (Probablity 0.5)
pub fn uniform() -> Box<NoiseGenerator> { pub fn uniform() -> Box<NoiseGenerator<D>> {
Box::new(NoiseGenerator { Box::new(NoiseGenerator {
prob: 0.5, prob: 0.5,
phantom: PhantomData,
}) })
} }
/// Generate map /// Generate map
fn build(&self, map: &Map, rng: &mut StdRng) -> Map { fn build(&self, map: &Map<D>, rng: &mut StdRng) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
let p = (self.prob * 100.0) as u32; let p = (self.prob * 100.0) as u32;
for y in 1..new_map.height-1 { for y in 1..new_map.height-1 {

View File

@ -1,26 +1,32 @@
//! Connect nearest rooms on the map with corridors //! Connect nearest rooms on the map with corridors
//! //!
use rand::prelude::StdRng; use rand::prelude::StdRng;
use crate::BuilderData;
use crate::MapFilter; use crate::MapFilter;
use crate::Map; use crate::Map;
use std::collections::HashSet; use std::collections::HashSet;
use std::marker::PhantomData;
pub struct NearestCorridors {} pub struct NearestCorridors<D: BuilderData> {
phantom: PhantomData<D>,
}
impl MapFilter for NearestCorridors { impl<D: BuilderData> MapFilter<D> for NearestCorridors<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
self.corridors(map) self.corridors(map)
} }
} }
impl NearestCorridors { impl<D: BuilderData> NearestCorridors<D> {
pub fn new() -> Box<NearestCorridors> { pub fn new() -> Box<NearestCorridors<D>> {
Box::new(NearestCorridors{}) Box::new(NearestCorridors {
phantom: PhantomData,
})
} }
fn corridors(&self, map: &Map) -> Map { fn corridors(&self, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
let mut connected : HashSet<usize> = HashSet::new(); let mut connected : HashSet<usize> = HashSet::new();

View File

@ -6,11 +6,11 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{MapFilter, Map}; //! use mapgen::{MapFilter, Map, NoData};
//! use mapgen::filter::SimpleRooms; //! use mapgen::filter::SimpleRooms;
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = SimpleRooms::new(); //! let gen = SimpleRooms::<NoData>::new();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
@ -18,36 +18,41 @@
//! ``` //! ```
//! //!
use std::marker::PhantomData;
use rand::prelude::*; use rand::prelude::*;
use crate::BuilderData;
use crate::MapFilter; use crate::MapFilter;
use crate::geometry::Rect; use crate::geometry::Rect;
use crate::random::Rng; use crate::random::Rng;
use crate::Map; use crate::Map;
pub struct SimpleRooms { pub struct SimpleRooms<D: BuilderData> {
max_rooms: usize, max_rooms: usize,
min_room_size: usize, min_room_size: usize,
max_room_size: usize, max_room_size: usize,
phantom: PhantomData<D>,
} }
impl MapFilter for SimpleRooms { impl<D: BuilderData> MapFilter<D> for SimpleRooms<D> {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build_rooms(map, rng) self.build_rooms(map, rng)
} }
} }
impl SimpleRooms { impl<D: BuilderData> SimpleRooms<D> {
pub fn new() -> Box<SimpleRooms> { pub fn new() -> Box<SimpleRooms<D>> {
Box::new(SimpleRooms{ Box::new(SimpleRooms{
max_rooms: 30, max_rooms: 30,
min_room_size: 6, min_room_size: 6,
max_room_size: 10 max_room_size: 10,
phantom: PhantomData,
}) })
} }
fn build_rooms(&self, map: &Map, rng : &mut StdRng) -> Map { fn build_rooms(&self, map: &Map<D>, rng : &mut StdRng) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
// Create room dimensions // Create room dimensions

View File

@ -6,12 +6,12 @@
//! Example modifier usage: //! Example modifier usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{MapFilter, Map, Tile}; //! use mapgen::{MapFilter, Map, NoData, Tile};
//! use mapgen::filter::starting_point::{AreaStartingPosition, XStart, YStart}; //! use mapgen::filter::starting_point::{AreaStartingPosition, XStart, YStart};
//! use mapgen::geometry::Point; //! use mapgen::geometry::Point;
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let mut map = Map::new(80, 50); //! let mut map = Map::<NoData>::new(80, 50);
//! map.set_tile(10, 10, Tile::floor()); //! map.set_tile(10, 10, Tile::floor());
//! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP); //! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP);
//! let new_map = modifier.modify_map(&mut rng, &map); //! let new_map = modifier.modify_map(&mut rng, &map);
@ -20,7 +20,10 @@
//! ``` //! ```
//! //!
use std::marker::PhantomData;
use rand::prelude::StdRng; use rand::prelude::StdRng;
use crate::BuilderData;
use crate::MapFilter; use crate::MapFilter;
use crate::geometry::Point; use crate::geometry::Point;
use crate::Map; use crate::Map;
@ -33,26 +36,28 @@ pub enum XStart { LEFT, CENTER, RIGHT }
pub enum YStart { TOP, CENTER, BOTTOM } pub enum YStart { TOP, CENTER, BOTTOM }
/// Add starting position to the map /// Add starting position to the map
pub struct AreaStartingPosition { pub struct AreaStartingPosition<D: BuilderData> {
x : XStart, x : XStart,
y : YStart y : YStart,
phantom: PhantomData<D>,
} }
impl MapFilter for AreaStartingPosition { impl<D: BuilderData> MapFilter<D> for AreaStartingPosition<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(map) self.build(map)
} }
} }
impl AreaStartingPosition { impl<D: BuilderData> AreaStartingPosition<D> {
/// Create new modifier with given region /// Create new modifier with given region
pub fn new(x : XStart, y : YStart) -> Box<AreaStartingPosition> { pub fn new(x : XStart, y : YStart) -> Box<AreaStartingPosition<D>> {
Box::new(AreaStartingPosition{ Box::new(AreaStartingPosition{
x, y x, y,
phantom: PhantomData,
}) })
} }
fn build(&self, map : &Map) -> Map { fn build(&self, map : &Map<D>) -> Map<D> {
let seed_x = match self.x { let seed_x = match self.x {
XStart::LEFT => 1, XStart::LEFT => 1,
XStart::CENTER => map.width / 2, XStart::CENTER => map.width / 2,
@ -101,7 +106,7 @@ mod tests {
use super::*; use super::*;
use super::MapFilter; use super::MapFilter;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::Map; use crate::map::{Map, NoData};
#[test] #[test]
fn test_exit() { fn test_exit() {
@ -111,7 +116,7 @@ mod tests {
# # # # # # # #
########## ##########
"; ";
let mut map = Map::from_string(map_str); let mut map = Map::<NoData>::from_string(map_str);
map.starting_point = Some(Point::new(9, 2)); map.starting_point = Some(Point::new(9, 2));
let modifier = AreaStartingPosition::new(XStart::CENTER, YStart::TOP); let modifier = AreaStartingPosition::new(XStart::CENTER, YStart::TOP);

View File

@ -1,11 +1,11 @@
//! Example generator usage: //! Example generator usage:
//! ``` //! ```
//! use rand::prelude::*; //! use rand::prelude::*;
//! use mapgen::{Map, MapFilter}; //! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::VoronoiHive; //! use mapgen::filter::VoronoiHive;
//! //!
//! let mut rng = StdRng::seed_from_u64(100); //! let mut rng = StdRng::seed_from_u64(100);
//! let gen = VoronoiHive::new(); //! let gen = VoronoiHive::<NoData>::new();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//! //!
//! assert_eq!(map.width, 80); //! assert_eq!(map.width, 80);
@ -13,34 +13,38 @@
//! ``` //! ```
//! //!
use std::marker::PhantomData;
use rand::prelude::*; use rand::prelude::*;
use crate::MapFilter; use crate::MapFilter;
use crate::{ use crate::{
map::{Map, Tile}, map::{BuilderData, Map, Tile},
random::Rng, random::Rng,
geometry::Point, geometry::Point,
}; };
pub struct VoronoiHive { pub struct VoronoiHive<D: BuilderData> {
n_seeds: usize, n_seeds: usize,
phantom: PhantomData<D>,
} }
impl MapFilter for VoronoiHive { impl<D: BuilderData> MapFilter<D> for VoronoiHive<D> {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(rng, map) self.build(rng, map)
} }
} }
impl VoronoiHive { impl<D: BuilderData> VoronoiHive<D> {
pub fn new() -> Box<VoronoiHive> { pub fn new() -> Box<VoronoiHive<D>> {
Box::new(VoronoiHive{ Box::new(VoronoiHive{
n_seeds: 64, n_seeds: 64,
phantom: PhantomData,
}) })
} }
fn build(&self, rng: &mut StdRng, map: &Map) -> Map { fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone(); let mut new_map = map.clone();
let seeds = self.generate_seeds(rng, map.width, map.height); let seeds = self.generate_seeds(rng, map.width, map.height);

View File

@ -8,7 +8,7 @@
//! //!
//! Example //! Example
//! ``` //! ```
//! use mapgen::{MapFilter, MapBuilder, Map, Tile}; //! use mapgen::{MapFilter, MapBuilder, Map, NoData, Tile};
//! use mapgen::filter::{ //! use mapgen::filter::{
//! NoiseGenerator, //! NoiseGenerator,
//! CellularAutomata, //! CellularAutomata,
@ -16,7 +16,7 @@
//! }; //! };
//! use mapgen::geometry::Point; //! use mapgen::geometry::Point;
//! //!
//! let map = MapBuilder::new(80, 50) //! let map = MapBuilder::<NoData>::new(80, 50)
//! .with(NoiseGenerator::uniform()) //! .with(NoiseGenerator::uniform())
//! .with(CellularAutomata::new()) //! .with(CellularAutomata::new())
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) //! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
@ -33,7 +33,7 @@ pub mod geometry;
pub mod map; pub mod map;
pub mod metric; pub mod metric;
pub use map::{Map, Symmetry, Tile}; pub use map::{BuilderData, Map, NoData, Symmetry, Tile};
pub use filter::*; pub use filter::*;
pub (crate) mod dijkstra; pub (crate) mod dijkstra;
@ -45,20 +45,20 @@ use rand::prelude::*;
/// Trait which should be implemented by map modifier. /// Trait which should be implemented by map modifier.
/// Modifier takes initiall map and apply changes to it. /// Modifier takes initiall map and apply changes to it.
pub trait MapFilter { pub trait MapFilter<D: BuilderData> {
fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map; fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D>;
} }
/// Used to chain MapBuilder and MapModifiers to create the final map. /// Used to chain MapBuilder and MapModifiers to create the final map.
pub struct MapBuilder { pub struct MapBuilder<D> {
width: usize, width: usize,
height: usize, height: usize,
modifiers: Vec<Box<dyn MapFilter>>, modifiers: Vec<Box<dyn MapFilter<D>>>,
} }
impl MapBuilder { impl<D: BuilderData> MapBuilder<D> {
/// Create Map Builder with initial map generator /// Create Map Builder with initial map generator
pub fn new(width: usize, height: usize) -> MapBuilder { pub fn new(width: usize, height: usize) -> MapBuilder<D> {
MapBuilder { MapBuilder {
width, width,
height, height,
@ -66,20 +66,20 @@ impl MapBuilder {
} }
} }
pub fn with(&mut self, modifier : Box<dyn MapFilter>) -> &mut MapBuilder { pub fn with(&mut self, modifier : Box<dyn MapFilter<D>>) -> &mut MapBuilder<D> {
self.modifiers.push(modifier); self.modifiers.push(modifier);
self self
} }
/// Build map using random number seeded with system time /// Build map using random number seeded with system time
pub fn build(&mut self) -> Map { pub fn build(&mut self) -> Map<D> {
let system_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Can't access system time"); let system_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Can't access system time");
let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64); let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64);
self.build_with_rng(&mut rng) self.build_with_rng(&mut rng)
} }
/// Build map using provided random number generator /// Build map using provided random number generator
pub fn build_with_rng(&mut self, rng: &mut StdRng) -> Map { pub fn build_with_rng(&mut self, rng: &mut StdRng) -> Map<D> {
let mut map = Map::new(self.width, self.height); let mut map = Map::new(self.width, self.height);
// Build additional layers in turn // Build additional layers in turn
@ -97,6 +97,8 @@ impl MapBuilder {
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::map::NoData;
use super::*; use super::*;
use filter::{ use filter::{
CellularAutomata, CellularAutomata,
@ -106,7 +108,7 @@ mod tests {
#[test] #[test]
fn test_ca_map() { fn test_ca_map() {
let map = MapBuilder::new(80, 50) let map = MapBuilder::<NoData>::new(80, 50)
.with(NoiseGenerator::new(0.55)) .with(NoiseGenerator::new(0.55))
.with(CellularAutomata::new()) .with(CellularAutomata::new())
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))

View File

@ -21,9 +21,18 @@ pub struct Tile {
pub enum Symmetry { None, Horizontal, Vertical, Both } pub enum Symmetry { None, Horizontal, Vertical, Both }
/// Arbitrary data associated with each map
pub trait BuilderData: Clone + Default {}
/// No build data
#[derive(Clone, Debug, Default)]
pub struct NoData;
impl BuilderData for NoData {}
/// Map data /// Map data
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct Map { pub struct Map<D> {
pub tiles : Vec<Tile>, pub tiles : Vec<Tile>,
pub width : usize, pub width : usize,
pub height : usize, pub height : usize,
@ -31,6 +40,7 @@ pub struct Map {
pub exit_point: Option<Point>, pub exit_point: Option<Point>,
pub rooms: Vec<Rect>, pub rooms: Vec<Rect>,
pub corridors: Vec<Vec<Point>>, pub corridors: Vec<Vec<Point>>,
pub data: D,
} }
impl Tile { impl Tile {
@ -59,10 +69,10 @@ impl Tile {
} }
} }
impl Map { impl<D: BuilderData> Map<D> {
/// Generates an empty map, consisting entirely of solid walls /// Generates an empty map, consisting entirely of solid walls
pub fn new(width: usize, height: usize) -> Map { pub fn new(width: usize, height: usize) -> Map<D> {
let map_tile_count = width*height; let map_tile_count = width*height;
Map{ Map{
tiles : vec![Tile::wall(); map_tile_count], tiles : vec![Tile::wall(); map_tile_count],
@ -71,12 +81,13 @@ impl Map {
starting_point: None, starting_point: None,
exit_point: None, exit_point: None,
rooms: Vec::new(), rooms: Vec::new(),
corridors: Vec::new() corridors: Vec::new(),
data: Default::default()
} }
} }
/// Create map from given string /// Create map from given string
pub fn from_string(map_string: &str) -> Map { pub fn from_string(map_string: &str) -> Map<D> {
let lines: Vec<&str> = map_string.split("\n") let lines: Vec<&str> = map_string.split("\n")
.map(|l| l.trim()) .map(|l| l.trim())
.filter(|l| l.len() > 0) .filter(|l| l.len() > 0)
@ -235,7 +246,7 @@ impl Map {
} }
} }
impl fmt::Display for Map { impl<D: BuilderData> fmt::Display for Map<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for y in 0..self.height { for y in 0..self.height {
let bytes: Vec<u8> = (0..self.width) let bytes: Vec<u8> = (0..self.width)
@ -257,7 +268,7 @@ mod tests {
#[test] #[test]
fn test_new_map() { fn test_new_map() {
let map = Map::new(10, 10); let map = Map::<NoData>::new(10, 10);
for i in 0..10 { for i in 0..10 {
for j in 0..10 { for j in 0..10 {
assert!(map.at(i, j).is_blocked); assert!(map.at(i, j).is_blocked);
@ -272,7 +283,7 @@ mod tests {
# # # #
########## ##########
"; ";
let map = Map::from_string(map_str); let map = Map::<NoData>::from_string(map_str);
assert_eq!(map.width, 10); assert_eq!(map.width, 10);
assert_eq!(map.height, 3); assert_eq!(map.height, 3);
@ -295,7 +306,7 @@ mod tests {
# # # #
########## ##########
"; ";
let map = Map::from_string(map_str); let map = Map::<NoData>::from_string(map_str);
let exists = map.get_available_exits(1, 1); let exists = map.get_available_exits(1, 1);
let expected_exists = vec![(2, 1, 1.0), (1, 2, 1.0), (2, 2, 1.45)]; let expected_exists = vec![(2, 1, 1.0), (1, 2, 1.0), (2, 2, 1.45)];
assert_eq!(exists, expected_exists); assert_eq!(exists, expected_exists);
@ -303,7 +314,7 @@ mod tests {
#[test] #[test]
fn test_create_room() { fn test_create_room() {
let mut map = Map::new(5, 5); let mut map = Map::<NoData>::new(5, 5);
map.add_room(Rect::new(1, 1, 3, 3)); map.add_room(Rect::new(1, 1, 3, 3));
for x in 0..map.width { for x in 0..map.width {
for y in 0..map.height { for y in 0..map.height {
@ -323,13 +334,13 @@ mod tests {
# # # # # #
########## ##########
"; ";
let mut map = Map::from_string(map_str); let mut map = Map::<NoData>::from_string(map_str);
let expected_map_str = " let expected_map_str = "
########## ##########
# # # #
########## ##########
"; ";
let expected_map = Map::from_string(expected_map_str); let expected_map = Map::<NoData>::from_string(expected_map_str);
map.add_corridor(Point::new(1, 1), Point::new(8, 1)); map.add_corridor(Point::new(1, 1), Point::new(8, 1));
@ -344,7 +355,7 @@ mod tests {
# # # # # #
########## ##########
"; ";
let map = Map::from_string(map_str); let map = Map::<NoData>::from_string(map_str);
let exists = map.get_available_exits(0, 0); let exists = map.get_available_exits(0, 0);
assert_eq!(exists.len(), 1); assert_eq!(exists.len(), 1);

View File

@ -4,6 +4,7 @@
//! and the provide generator score as an average. //! and the provide generator score as an average.
//! //!
use super::BuilderData;
use super::map::Map; use super::map::Map;
use super::dijkstra::DijkstraMap; use super::dijkstra::DijkstraMap;
@ -11,7 +12,7 @@ use super::dijkstra::DijkstraMap;
/// This metric calculates the percentage of walkable cells (Floor). /// This metric calculates the percentage of walkable cells (Floor).
/// If this number is very low (like < 10%) then it means that the map /// If this number is very low (like < 10%) then it means that the map
/// is probably to degenerated and shouldn't be used /// is probably to degenerated and shouldn't be used
pub fn density(map: &Map) -> f32 { pub fn density<D>(map: &Map<D>) -> f32 {
let floor_count = map.tiles.iter() let floor_count = map.tiles.iter()
.filter(|&t| t.is_walkable()) .filter(|&t| t.is_walkable())
.count(); .count();
@ -22,7 +23,7 @@ pub fn density(map: &Map) -> f32 {
/// Calculate the length of the shortes path from the starting point /// Calculate the length of the shortes path from the starting point
/// to the exit. /// to the exit.
/// If this path is very short, then the map is probably degenerated. /// If this path is very short, then the map is probably degenerated.
pub fn path_length(map: &Map) -> f32 { pub fn path_length<D: BuilderData>(map: &Map<D>) -> f32 {
if map.starting_point.is_none() { if map.starting_point.is_none() {
return 0.0 return 0.0
} }
@ -43,12 +44,12 @@ pub fn path_length(map: &Map) -> f32 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::geometry::Point; use crate::{geometry::Point, map::NoData};
#[test] #[test]
fn test_density_no_floor() { fn test_density_no_floor() {
let map = Map::new(10, 10); let map = Map::<NoData>::new(10, 10);
let score = density(&map); let score = density(&map);
assert_eq!(score, 0.0); assert_eq!(score, 0.0);
} }
@ -60,7 +61,7 @@ mod tests {
# ## # # ## #
########## ##########
"; ";
let map = Map::from_string(map_str); let map = Map::<NoData>::from_string(map_str);
let score = density(&map); let score = density(&map);
assert_eq!(score, 0.2); assert_eq!(score, 0.2);
} }
@ -72,7 +73,7 @@ mod tests {
# ## # # ## #
########## ##########
"; ";
let map = Map::from_string(map_str); let map = Map::<NoData>::from_string(map_str);
let score = path_length(&map); let score = path_length(&map);
assert_eq!(score, 0.0); assert_eq!(score, 0.0);
} }
@ -85,7 +86,7 @@ mod tests {
# # # #
########## ##########
"; ";
let mut map = Map::from_string(map_str); let mut map = Map::<NoData>::from_string(map_str);
map.starting_point = Some(Point::new(1,1)); map.starting_point = Some(Point::new(1,1));
map.exit_point = Some(Point::new(8,1)); map.exit_point = Some(Point::new(8,1));