From 957dcca447b084f9f7776c278a191fbb625f19a6 Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Sat, 12 Mar 2022 09:45:42 -0600 Subject: [PATCH] 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. --- demo/src/lib.rs | 8 ++--- examples/bsp_interior.rs | 2 +- examples/bsp_rooms.rs | 2 +- examples/example1.rs | 3 +- src/dijkstra.rs | 22 +++++++------- src/filter/bsp_interior.rs | 26 ++++++++++------- src/filter/bsp_rooms.rs | 27 ++++++++++------- src/filter/cellular_automata.rs | 33 +++++++++++++-------- src/filter/cull_unreachable.rs | 28 +++++++++++------- src/filter/distant_exit.rs | 24 +++++++++------ src/filter/drunkard.rs | 35 ++++++++++++---------- src/filter/maze.rs | 42 ++++++++++++++++----------- src/filter/noise_generator.rs | 25 +++++++++------- src/filter/rooms_corridors_nearest.rs | 20 ++++++++----- src/filter/simple_rooms.rs | 23 +++++++++------ src/filter/starting_point.rs | 29 ++++++++++-------- src/filter/voronoi.rs | 22 ++++++++------ src/lib.rs | 28 +++++++++--------- src/map.rs | 37 ++++++++++++++--------- src/metric.rs | 15 +++++----- 20 files changed, 268 insertions(+), 183 deletions(-) diff --git a/demo/src/lib.rs b/demo/src/lib.rs index ab93937..c8e105e 100644 --- a/demo/src/lib.rs +++ b/demo/src/lib.rs @@ -1,7 +1,7 @@ use wasm_bindgen::prelude::*; use web_sys; use rand::prelude::*; -use mapgen::{Map, MapBuilder, geometry::Point}; +use mapgen::{Map, MapBuilder, NoData, geometry::Point}; use mapgen::filter::*; use mapgen::metric; @@ -19,7 +19,7 @@ pub struct World { width: u32, height: u32, tiles: Vec, - map: Map, + map: Map, } #[wasm_bindgen] @@ -32,7 +32,7 @@ pub struct Position { #[wasm_bindgen] impl World { - fn new(width: u32, height: u32, map: Map) -> World { + fn new(width: u32, height: u32, map: Map) -> World { let tiles = (0..map.tiles.len()) .map(|i| if map.tiles[i].is_walkable() {Cell::Floor} else {Cell::Wall}) .collect(); @@ -176,7 +176,7 @@ impl World { div.set_inner_html(&info); } - fn print_map_metrics(map: &Map) { + fn print_map_metrics(map: &Map) { let window = web_sys::window().expect("no global `window` exists"); 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"); diff --git a/examples/bsp_interior.rs b/examples/bsp_interior.rs index 2b87425..185b172 100644 --- a/examples/bsp_interior.rs +++ b/examples/bsp_interior.rs @@ -4,7 +4,7 @@ use mapgen::*; fn main() { let mut rng = StdRng::seed_from_u64(907647352); - let gen = BspInterior::new(); + let gen = BspInterior::::new(); let map = gen.modify_map(&mut rng, &Map::new(20, 10)); println!("{:}", &map); } \ No newline at end of file diff --git a/examples/bsp_rooms.rs b/examples/bsp_rooms.rs index cf3ca9b..f960b67 100644 --- a/examples/bsp_rooms.rs +++ b/examples/bsp_rooms.rs @@ -6,7 +6,7 @@ use mapgen::*; fn main() { 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 gen = BspRooms::new(); + let gen = BspRooms::::new(); let map = gen.modify_map(&mut rng, &Map::new(80, 50)); println!("{:}", &map); } \ No newline at end of file diff --git a/examples/example1.rs b/examples/example1.rs index 32efff9..fcaa7a3 100644 --- a/examples/example1.rs +++ b/examples/example1.rs @@ -1,5 +1,6 @@ use mapgen::{ MapBuilder, + NoData, filter::{ NoiseGenerator, CellularAutomata, @@ -12,7 +13,7 @@ use mapgen::{ fn main() { - let map = MapBuilder::new(20, 20) + let map = MapBuilder::::new(20, 20) .with(NoiseGenerator::uniform()) .with(CellularAutomata::new()) .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) diff --git a/src/dijkstra.rs b/src/dijkstra.rs index a0e4cb0..3ee1d6d 100644 --- a/src/dijkstra.rs +++ b/src/dijkstra.rs @@ -25,25 +25,26 @@ //! --- //! -use std::collections::VecDeque; +use std::{collections::VecDeque, marker::PhantomData}; use std::f32::MAX; -use super::map::Map; +use super::map::{BuilderData, 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). /// size_x and size_y are stored for overflow avoidance. /// max_depth is the maximum number of iterations this search shall support. -pub struct DijkstraMap { +pub struct DijkstraMap { pub tiles: Vec, size_x: usize, size_y: usize, max_depth: f32, + phantom: PhantomData, } -impl DijkstraMap { +impl DijkstraMap { //! Construct a new Dijkstra map, ready to run. - pub fn new(map: &Map) -> DijkstraMap { + pub fn new(map: &Map) -> DijkstraMap { let len = map.width * map.height; let tiles = vec![MAX; len]; let mut d = DijkstraMap { @@ -51,6 +52,7 @@ impl DijkstraMap { size_x: map.width, size_y: map.height, max_depth: len as f32, + phantom: PhantomData, }; d.build(map); d @@ -61,7 +63,7 @@ impl DijkstraMap { /// depth is further than the current depth. /// WARNING: Will give incorrect results when used with non-uniform exit costs. Much slower /// algorithm required to support that. - fn build(&mut self, map: &Map) { + fn build(&mut self, map: &Map) { let mapsize: usize = (self.size_x * self.size_y) as usize; let mut open_list: VecDeque<((usize, usize), f32)> = VecDeque::with_capacity(mapsize); @@ -97,7 +99,7 @@ impl DijkstraMap { mod tests { use super::*; use crate::geometry::Point; - use crate::map::Map; + use crate::map::{Map, NoData}; #[test] fn test_culling() { @@ -106,7 +108,7 @@ mod tests { # # # ########## "; - let mut map = Map::from_string(map_str); + let mut map = Map::::from_string(map_str); map.starting_point = Some(Point::new(8, 1)); let dm = DijkstraMap::new(&map); @@ -134,7 +136,7 @@ mod tests { # # #### "; - let mut map = Map::from_string(map_str); + let mut map = Map::::from_string(map_str); map.starting_point = Some(Point::new(2, 2)); let dm = DijkstraMap::new(&map); let expected = [MAX, MAX, MAX, MAX, @@ -153,7 +155,7 @@ mod tests { # # # ########## "; - let mut map = Map::from_string(map_str); + let mut map = Map::::from_string(map_str); map.starting_point = Some(Point::new(8, 2)); let dm = DijkstraMap::new(&map); let expected = [MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, diff --git a/src/filter/bsp_interior.rs b/src/filter/bsp_interior.rs index 996f01f..6db69e7 100644 --- a/src/filter/bsp_interior.rs +++ b/src/filter/bsp_interior.rs @@ -6,13 +6,13 @@ //! Example generator usage: //! ``` //! use rand::prelude::*; -//! use mapgen::{Map, MapFilter}; +//! use mapgen::{Map, MapFilter, NoData}; //! use mapgen::filter::{ //! BspInterior //! }; //! //! let mut rng = StdRng::seed_from_u64(100); -//! let gen = BspInterior::new(); +//! let gen = BspInterior::::new(); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! //! assert_eq!(map.width, 80); @@ -20,32 +20,36 @@ //! ``` //! +use std::marker::PhantomData; + use rand::prelude::*; -use crate::MapFilter; +use crate::{BuilderData, MapFilter}; use crate::geometry::{Point, Rect}; use crate::random::Rng; use crate::Map; -pub struct BspInterior { +pub struct BspInterior { min_room_size: usize, + phantom: PhantomData, } -impl MapFilter for BspInterior { - fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { +impl MapFilter for BspInterior { + fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { self.build(rng, map) } } -impl BspInterior { +impl BspInterior { - pub fn new() -> Box { + pub fn new() -> Box> { Box::new(BspInterior{ min_room_size: 8, + phantom: PhantomData, }) } - fn build(&self, rng: &mut StdRng, map: &Map) -> Map { + fn build(&self, rng: &mut StdRng, map: &Map) -> Map { let mut new_map = map.clone(); let mut rects: Vec = Vec::new(); rects.push( Rect::new(1, 1, new_map.width-2, new_map.height-2) ); @@ -114,12 +118,12 @@ impl BspInterior { #[cfg(test)] mod tests { use super::*; - use crate::{Map}; + use crate::{Map, map::NoData}; #[test] fn no_corridors_on_borders() { let mut rng = StdRng::seed_from_u64(907647352); - let gen = BspInterior::new(); + let gen = BspInterior::::new(); let map = gen.modify_map(&mut rng, &Map::new(80, 50)); for i in 0..80 { assert!(map.at(i, 0).is_blocked()); diff --git a/src/filter/bsp_rooms.rs b/src/filter/bsp_rooms.rs index 8028eda..8639408 100644 --- a/src/filter/bsp_rooms.rs +++ b/src/filter/bsp_rooms.rs @@ -6,11 +6,11 @@ //! Example generator usage: //! ``` //! use rand::prelude::*; -//! use mapgen::{Map, MapFilter}; +//! use mapgen::{Map, MapFilter, NoData}; //! use mapgen::filter::BspRooms; //! //! let mut rng = StdRng::seed_from_u64(100); -//! let gen = BspRooms::new(); +//! let gen = BspRooms::::new(); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! //! assert_eq!(map.width, 80); @@ -18,31 +18,36 @@ //! ``` //! +use std::marker::PhantomData; + use rand::prelude::*; +use crate::BuilderData; use crate::MapFilter; use crate::geometry::Rect; use crate::random::Rng; use crate::Map; -pub struct BspRooms { +pub struct BspRooms { max_split: usize, + phantom: PhantomData, } -impl MapFilter for BspRooms { - fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { +impl MapFilter for BspRooms { + fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { self.build_rooms(map, rng) } } -impl BspRooms { - pub fn new() -> Box { +impl BspRooms { + pub fn new() -> Box> { Box::new(BspRooms { max_split: 240, + phantom: PhantomData, }) } - fn build_rooms(&self, map: &Map, rng : &mut StdRng) -> Map { + fn build_rooms(&self, map: &Map, rng : &mut StdRng) -> Map { let mut new_map = map.clone(); let mut rects: Vec = Vec::new(); // Start with a single map-sized rectangle @@ -102,7 +107,7 @@ impl BspRooms { result } - fn is_possible(&self, rect: Rect, map: &Map) -> bool { + fn is_possible(&self, rect: Rect, map: &Map) -> bool { let mut expanded = rect; expanded.x1 -= 2; expanded.x2 += 2; @@ -139,12 +144,12 @@ impl BspRooms { #[cfg(test)] mod tests { use super::*; - use crate::map::Map; + use crate::map::{Map, NoData}; #[test] fn no_corridors_on_borders() { let mut rng = StdRng::seed_from_u64(907647352); - let gen = BspRooms::new(); + let gen = BspRooms::::new(); let map = gen.modify_map(&mut rng, &Map::new(80, 50)); for i in 0..80 { assert!(map.at(i, 0).is_blocked()); diff --git a/src/filter/cellular_automata.rs b/src/filter/cellular_automata.rs index 365ab61..d35eff3 100644 --- a/src/filter/cellular_automata.rs +++ b/src/filter/cellular_automata.rs @@ -9,11 +9,11 @@ //! Example usage: //! ``` //! use rand::prelude::*; -//! use mapgen::{Map, MapFilter}; +//! use mapgen::{Map, MapFilter, NoData}; //! use mapgen::filter::CellularAutomata; //! //! let mut rng = StdRng::seed_from_u64(100); -//! let gen = CellularAutomata::new(); +//! let gen = CellularAutomata::::new(); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! //! assert_eq!(map.width, 80); @@ -21,30 +21,37 @@ //! ``` //! +use std::marker::PhantomData; + use rand::prelude::*; +use crate::BuilderData; use crate::MapFilter; use crate::{Map, Tile}; /// Map filter -pub struct CellularAutomata { +pub struct CellularAutomata { num_iteraction: u32, + phantom: PhantomData, } -impl MapFilter for CellularAutomata { - fn modify_map(&self, _rng: &mut StdRng, map: &Map) -> Map { +impl MapFilter for CellularAutomata { + fn modify_map(&self, _rng: &mut StdRng, map: &Map) -> Map { self.build(map) } } -impl CellularAutomata { +impl CellularAutomata { /// Create generator which will create map with the given dimension. - pub fn new() -> Box { - Box::new(CellularAutomata { num_iteraction: 15}) + pub fn new() -> Box> { + Box::new(CellularAutomata { + num_iteraction: 15, + phantom: PhantomData, + }) } /// Generate map - fn build(&self, map: &Map) -> Map { + fn build(&self, map: &Map) -> Map { let mut new_map = map.clone(); for _ in 0..self.num_iteraction { new_map = apply_iteration(&new_map); @@ -55,7 +62,7 @@ impl CellularAutomata { } -fn apply_iteration(map: &Map) -> Map { +fn apply_iteration(map: &Map) -> Map { let mut new_map = map.clone(); for y in 1..map.height-1 { @@ -85,11 +92,13 @@ fn apply_iteration(map: &Map) -> Map { /// ------------------------------------------------------------------------------------------------ #[cfg(test)] mod tests { + use crate::map::NoData; + use super::*; #[test] fn test_iteration_wal() { - let map = Map::new(3, 3); + let map = Map::::new(3, 3); let new_map = apply_iteration(&map); assert!(new_map.at(1, 1).is_blocked()); } @@ -97,7 +106,7 @@ mod tests { #[test] fn test_iteration_floor() { - let mut map = Map::new(3, 3); + let mut map = Map::::new(3, 3); for i in 0..3 { for j in 0..2 { map.set_tile(i, j, Tile::floor()); diff --git a/src/filter/cull_unreachable.rs b/src/filter/cull_unreachable.rs index 6f8c6e8..c180341 100644 --- a/src/filter/cull_unreachable.rs +++ b/src/filter/cull_unreachable.rs @@ -4,28 +4,34 @@ //! It will add wall on every tile which is not accessible from the starting point. //! +use std::marker::PhantomData; + use rand::prelude::StdRng; use crate::MapFilter; -use crate::{Map, Tile}; +use crate::{BuilderData, Map, Tile}; use crate::dijkstra::DijkstraMap; /// Remove unreachable areas from the map. -pub struct CullUnreachable {} +pub struct CullUnreachable { + phantom: PhantomData, +} -impl MapFilter for CullUnreachable { - fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { +impl MapFilter for CullUnreachable { + fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { self.build(map) } } -impl CullUnreachable { +impl CullUnreachable { #[allow(dead_code)] - pub fn new() -> Box { - Box::new(CullUnreachable{}) + pub fn new() -> Box> { + Box::new(CullUnreachable { + phantom: PhantomData, + }) } - fn build(&self, map: &Map) -> Map { + fn build(&self, map: &Map) -> Map { let mut new_map = map.clone(); let dijkstra_map = DijkstraMap::new(map); @@ -51,7 +57,7 @@ mod tests { use super::*; use super::MapFilter; use crate::geometry::Point; - use crate::map::Map; + use crate::map::{Map, NoData}; #[test] fn test_culling() { @@ -60,14 +66,14 @@ mod tests { # # # ########## "; - let mut map = Map::from_string(map_str); + let mut map = Map::::from_string(map_str); map.starting_point = Some(Point::new(9, 1)); let expected_map_str = " ########## #### # ########## "; - let expected_map = Map::from_string(expected_map_str); + let expected_map = Map::::from_string(expected_map_str); let modifier = CullUnreachable::new(); diff --git a/src/filter/distant_exit.rs b/src/filter/distant_exit.rs index 498d577..9d622d3 100644 --- a/src/filter/distant_exit.rs +++ b/src/filter/distant_exit.rs @@ -5,7 +5,9 @@ //! use std::f32; +use std::marker::PhantomData; use rand::prelude::StdRng; +use crate::BuilderData; use crate::geometry::Point; use crate::MapFilter; 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. -pub struct DistantExit {} +pub struct DistantExit { + phantom: PhantomData, +} -impl MapFilter for DistantExit { - fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { +impl MapFilter for DistantExit { + fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { self.build(map) } } -impl DistantExit { +impl DistantExit { #[allow(dead_code)] - pub fn new() -> Box { - Box::new(DistantExit{}) + pub fn new() -> Box> { + Box::new(DistantExit { + phantom: PhantomData, + }) } - fn build(&self, map: &Map) -> Map { + fn build(&self, map: &Map) -> Map { let mut new_map = map.clone(); let mut best_idx = 0; @@ -55,7 +61,7 @@ mod tests { use super::*; use super::MapFilter; use crate::geometry::Point; - use crate::map::Map; + use crate::map::{Map, NoData}; #[test] fn test_exit() { @@ -65,7 +71,7 @@ mod tests { # # # ########## "; - let mut map = Map::from_string(map_str); + let mut map = Map::::from_string(map_str); map.starting_point = Some(Point::new(9, 2)); let modifier = DistantExit::new(); diff --git a/src/filter/drunkard.rs b/src/filter/drunkard.rs index 9c8b341..17b4661 100644 --- a/src/filter/drunkard.rs +++ b/src/filter/drunkard.rs @@ -1,11 +1,11 @@ //! Example generator usage: //! ``` //! use rand::prelude::*; -//! use mapgen::{Map, MapFilter}; +//! use mapgen::{Map, MapFilter, NoData}; //! use mapgen::filter::DrunkardsWalk; //! //! let mut rng = StdRng::seed_from_u64(100); -//! let gen = DrunkardsWalk::open_area(); +//! let gen = DrunkardsWalk::::open_area(); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! //! assert_eq!(map.width, 80); @@ -13,9 +13,12 @@ //! ``` //! +use std::marker::PhantomData; + use rand::prelude::*; use crate::MapFilter; use crate::{ + BuilderData, map::{Map, Symmetry, Tile}, geometry::Point, random::Rng @@ -25,57 +28,59 @@ use crate::{ #[derive(PartialEq, Copy, Clone)] pub enum DrunkSpawnMode { StartingPoint, Random } -pub struct DrunkardsWalk { +pub struct DrunkardsWalk { spawn_mode : DrunkSpawnMode, drunken_lifetime : i32, floor_percent: f32, brush_size: usize, - symmetry: Symmetry + symmetry: Symmetry, + phantom: PhantomData, } -impl MapFilter for DrunkardsWalk { - fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { +impl MapFilter for DrunkardsWalk { + fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { self.build(rng, map) } } -impl DrunkardsWalk { +impl DrunkardsWalk { pub fn new( spawn_mode: DrunkSpawnMode, drunken_lifetime: i32, floor_percent: f32, brush_size: usize, - symmetry: Symmetry) -> Box + symmetry: Symmetry) -> Box> { Box::new(DrunkardsWalk{ spawn_mode, drunken_lifetime, floor_percent, brush_size, - symmetry + symmetry, + phantom: PhantomData, }) } - pub fn open_area() -> Box { + pub fn open_area() -> Box> { Self::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None) } - pub fn open_halls() -> Box { + pub fn open_halls() -> Box> { Self::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None) } - pub fn winding_passages() -> Box { + pub fn winding_passages() -> Box> { Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None) } - pub fn fat_passages() -> Box { + pub fn fat_passages() -> Box> { Self::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None) } - pub fn fearful_symmetry() -> Box { + pub fn fearful_symmetry() -> Box> { 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) -> Map { let mut new_map = map.clone(); // Set a central starting point let starting_position = Point::new( new_map.width / 2, new_map.height / 2 ); diff --git a/src/filter/maze.rs b/src/filter/maze.rs index eab7d9f..b915fce 100644 --- a/src/filter/maze.rs +++ b/src/filter/maze.rs @@ -1,11 +1,11 @@ //! Example generator usage: //! ``` //! use rand::prelude::*; -//! use mapgen::{Map, MapFilter}; +//! use mapgen::{Map, MapFilter, NoData}; //! use mapgen::filter::MazeBuilder; //! //! let mut rng = StdRng::seed_from_u64(100); -//! let gen = MazeBuilder::new(); +//! let gen = MazeBuilder::::new(); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! //! assert_eq!(map.width, 80); @@ -13,29 +13,35 @@ //! ``` //! +use std::marker::PhantomData; + use rand::prelude::*; use crate::MapFilter; use crate::{ - map::{Map, Tile}, + map::{BuilderData, Map, Tile}, random::Rng }; -pub struct MazeBuilder {} +pub struct MazeBuilder { + phantom: PhantomData, +} -impl MapFilter for MazeBuilder { - fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { +impl MapFilter for MazeBuilder { + fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { self.build(rng, map) } } -impl MazeBuilder { - pub fn new() -> Box { - Box::new(MazeBuilder{}) +impl MazeBuilder { + pub fn new() -> Box> { + Box::new(MazeBuilder { + phantom: PhantomData, + }) } #[allow(clippy::map_entry)] - fn build(&self, rng: &mut StdRng, map: &Map) -> Map { + fn build(&self, rng: &mut StdRng, map: &Map) -> Map { let mut new_map = map.clone(); let mut maze = Grid::new((map.width as i32/ 2)-2, (map.height as i32/ 2)-2, rng); maze.generate_maze(&mut new_map); @@ -91,24 +97,26 @@ impl Cell { } } -struct Grid<'a> { +struct Grid<'a, D: BuilderData> { width: i32, height: i32, cells: Vec, backtrace: Vec, current: usize, - rng : &'a mut StdRng + rng : &'a mut StdRng, + phantom: PhantomData, } -impl<'a> Grid<'a> { - fn new(width: i32, height:i32, rng: &mut StdRng) -> Grid { +impl<'a, D: BuilderData> Grid<'a, D> { + fn new(width: i32, height:i32, rng: &mut StdRng) -> Grid { let mut grid = Grid{ width, height, cells: Vec::new(), backtrace: Vec::new(), current: 0, - rng + rng, + phantom: PhantomData, }; for row in 0..height { @@ -162,7 +170,7 @@ impl<'a> Grid<'a> { None } - fn generate_maze(&mut self, map: &mut Map) { + fn generate_maze(&mut self, map: &mut Map) { let mut i = 0; loop { 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) { // Clear the map for i in map.tiles.iter_mut() { *i = Tile::wall(); } diff --git a/src/filter/noise_generator.rs b/src/filter/noise_generator.rs index d7d6a7c..ec8a24a 100644 --- a/src/filter/noise_generator.rs +++ b/src/filter/noise_generator.rs @@ -4,11 +4,11 @@ //! Example usage: //! ``` //! use rand::prelude::*; -//! use mapgen::{Map, MapFilter}; +//! use mapgen::{Map, MapFilter, NoData}; //! use mapgen::filter::NoiseGenerator; //! //! let mut rng = StdRng::seed_from_u64(100); -//! let gen = NoiseGenerator::uniform(); +//! let gen = NoiseGenerator::::uniform(); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! //! assert_eq!(map.width, 80); @@ -16,39 +16,44 @@ //! ``` //! +use std::marker::PhantomData; + use rand::prelude::*; use crate::MapFilter; -use crate::{Map, Tile}; +use crate::{BuilderData, Map, Tile}; /// Map noise generator -pub struct NoiseGenerator { +pub struct NoiseGenerator { prob: f32, + phantom: PhantomData, } -impl MapFilter for NoiseGenerator { - fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { +impl MapFilter for NoiseGenerator { + fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { self.build(map, rng) } } -impl NoiseGenerator { +impl NoiseGenerator { /// Create noise with custom probability - pub fn new(prob: f32) -> Box { + pub fn new(prob: f32) -> Box> { Box::new(NoiseGenerator { prob, + phantom: PhantomData, }) } /// Create uniform noise (Probablity 0.5) - pub fn uniform() -> Box { + pub fn uniform() -> Box> { Box::new(NoiseGenerator { prob: 0.5, + phantom: PhantomData, }) } /// Generate map - fn build(&self, map: &Map, rng: &mut StdRng) -> Map { + fn build(&self, map: &Map, rng: &mut StdRng) -> Map { let mut new_map = map.clone(); let p = (self.prob * 100.0) as u32; for y in 1..new_map.height-1 { diff --git a/src/filter/rooms_corridors_nearest.rs b/src/filter/rooms_corridors_nearest.rs index 7bbe717..74b44c9 100644 --- a/src/filter/rooms_corridors_nearest.rs +++ b/src/filter/rooms_corridors_nearest.rs @@ -1,26 +1,32 @@ //! Connect nearest rooms on the map with corridors //! use rand::prelude::StdRng; +use crate::BuilderData; use crate::MapFilter; use crate::Map; use std::collections::HashSet; +use std::marker::PhantomData; -pub struct NearestCorridors {} +pub struct NearestCorridors { + phantom: PhantomData, +} -impl MapFilter for NearestCorridors { - fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { +impl MapFilter for NearestCorridors { + fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { self.corridors(map) } } -impl NearestCorridors { +impl NearestCorridors { - pub fn new() -> Box { - Box::new(NearestCorridors{}) + pub fn new() -> Box> { + Box::new(NearestCorridors { + phantom: PhantomData, + }) } - fn corridors(&self, map: &Map) -> Map { + fn corridors(&self, map: &Map) -> Map { let mut new_map = map.clone(); let mut connected : HashSet = HashSet::new(); diff --git a/src/filter/simple_rooms.rs b/src/filter/simple_rooms.rs index 4335547..73000f7 100644 --- a/src/filter/simple_rooms.rs +++ b/src/filter/simple_rooms.rs @@ -6,11 +6,11 @@ //! Example generator usage: //! ``` //! use rand::prelude::*; -//! use mapgen::{MapFilter, Map}; +//! use mapgen::{MapFilter, Map, NoData}; //! use mapgen::filter::SimpleRooms; //! //! let mut rng = StdRng::seed_from_u64(100); -//! let gen = SimpleRooms::new(); +//! let gen = SimpleRooms::::new(); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! //! assert_eq!(map.width, 80); @@ -18,36 +18,41 @@ //! ``` //! +use std::marker::PhantomData; + use rand::prelude::*; +use crate::BuilderData; use crate::MapFilter; use crate::geometry::Rect; use crate::random::Rng; use crate::Map; -pub struct SimpleRooms { +pub struct SimpleRooms { max_rooms: usize, min_room_size: usize, max_room_size: usize, + phantom: PhantomData, } -impl MapFilter for SimpleRooms { - fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { +impl MapFilter for SimpleRooms { + fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { self.build_rooms(map, rng) } } -impl SimpleRooms { - pub fn new() -> Box { +impl SimpleRooms { + pub fn new() -> Box> { Box::new(SimpleRooms{ max_rooms: 30, 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, rng : &mut StdRng) -> Map { let mut new_map = map.clone(); // Create room dimensions diff --git a/src/filter/starting_point.rs b/src/filter/starting_point.rs index bed4f69..7f6c5d1 100644 --- a/src/filter/starting_point.rs +++ b/src/filter/starting_point.rs @@ -6,12 +6,12 @@ //! Example modifier usage: //! ``` //! use rand::prelude::*; -//! use mapgen::{MapFilter, Map, Tile}; +//! use mapgen::{MapFilter, Map, NoData, Tile}; //! use mapgen::filter::starting_point::{AreaStartingPosition, XStart, YStart}; //! use mapgen::geometry::Point; //! //! let mut rng = StdRng::seed_from_u64(100); -//! let mut map = Map::new(80, 50); +//! let mut map = Map::::new(80, 50); //! map.set_tile(10, 10, Tile::floor()); //! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP); //! let new_map = modifier.modify_map(&mut rng, &map); @@ -20,7 +20,10 @@ //! ``` //! +use std::marker::PhantomData; + use rand::prelude::StdRng; +use crate::BuilderData; use crate::MapFilter; use crate::geometry::Point; use crate::Map; @@ -33,26 +36,28 @@ pub enum XStart { LEFT, CENTER, RIGHT } pub enum YStart { TOP, CENTER, BOTTOM } /// Add starting position to the map -pub struct AreaStartingPosition { +pub struct AreaStartingPosition { x : XStart, - y : YStart + y : YStart, + phantom: PhantomData, } -impl MapFilter for AreaStartingPosition { - fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { +impl MapFilter for AreaStartingPosition { + fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map { self.build(map) } } -impl AreaStartingPosition { +impl AreaStartingPosition { /// Create new modifier with given region - pub fn new(x : XStart, y : YStart) -> Box { + pub fn new(x : XStart, y : YStart) -> Box> { Box::new(AreaStartingPosition{ - x, y + x, y, + phantom: PhantomData, }) } - fn build(&self, map : &Map) -> Map { + fn build(&self, map : &Map) -> Map { let seed_x = match self.x { XStart::LEFT => 1, XStart::CENTER => map.width / 2, @@ -101,7 +106,7 @@ mod tests { use super::*; use super::MapFilter; use crate::geometry::Point; - use crate::map::Map; + use crate::map::{Map, NoData}; #[test] fn test_exit() { @@ -111,7 +116,7 @@ mod tests { # # # # ########## "; - let mut map = Map::from_string(map_str); + let mut map = Map::::from_string(map_str); map.starting_point = Some(Point::new(9, 2)); let modifier = AreaStartingPosition::new(XStart::CENTER, YStart::TOP); diff --git a/src/filter/voronoi.rs b/src/filter/voronoi.rs index adb650a..1a1b647 100644 --- a/src/filter/voronoi.rs +++ b/src/filter/voronoi.rs @@ -1,11 +1,11 @@ //! Example generator usage: //! ``` //! use rand::prelude::*; -//! use mapgen::{Map, MapFilter}; +//! use mapgen::{Map, MapFilter, NoData}; //! use mapgen::filter::VoronoiHive; //! //! let mut rng = StdRng::seed_from_u64(100); -//! let gen = VoronoiHive::new(); +//! let gen = VoronoiHive::::new(); //! let map = gen.modify_map(&mut rng, &Map::new(80, 50)); //! //! assert_eq!(map.width, 80); @@ -13,34 +13,38 @@ //! ``` //! +use std::marker::PhantomData; + use rand::prelude::*; use crate::MapFilter; use crate::{ - map::{Map, Tile}, + map::{BuilderData, Map, Tile}, random::Rng, geometry::Point, }; -pub struct VoronoiHive { +pub struct VoronoiHive { n_seeds: usize, + phantom: PhantomData, } -impl MapFilter for VoronoiHive { - fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { +impl MapFilter for VoronoiHive { + fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map { self.build(rng, map) } } -impl VoronoiHive { - pub fn new() -> Box { +impl VoronoiHive { + pub fn new() -> Box> { Box::new(VoronoiHive{ n_seeds: 64, + phantom: PhantomData, }) } - fn build(&self, rng: &mut StdRng, map: &Map) -> Map { + fn build(&self, rng: &mut StdRng, map: &Map) -> Map { let mut new_map = map.clone(); let seeds = self.generate_seeds(rng, map.width, map.height); diff --git a/src/lib.rs b/src/lib.rs index f0ab09e..44f3066 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! //! Example //! ``` -//! use mapgen::{MapFilter, MapBuilder, Map, Tile}; +//! use mapgen::{MapFilter, MapBuilder, Map, NoData, Tile}; //! use mapgen::filter::{ //! NoiseGenerator, //! CellularAutomata, @@ -16,7 +16,7 @@ //! }; //! use mapgen::geometry::Point; //! -//! let map = MapBuilder::new(80, 50) +//! let map = MapBuilder::::new(80, 50) //! .with(NoiseGenerator::uniform()) //! .with(CellularAutomata::new()) //! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) @@ -33,7 +33,7 @@ pub mod geometry; pub mod map; pub mod metric; -pub use map::{Map, Symmetry, Tile}; +pub use map::{BuilderData, Map, NoData, Symmetry, Tile}; pub use filter::*; pub (crate) mod dijkstra; @@ -45,20 +45,20 @@ use rand::prelude::*; /// Trait which should be implemented by map modifier. /// Modifier takes initiall map and apply changes to it. -pub trait MapFilter { - fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map; +pub trait MapFilter { + fn modify_map(&self, rng: &mut StdRng, map: &Map) -> Map; } /// Used to chain MapBuilder and MapModifiers to create the final map. -pub struct MapBuilder { +pub struct MapBuilder { width: usize, height: usize, - modifiers: Vec>, + modifiers: Vec>>, } -impl MapBuilder { +impl MapBuilder { /// Create Map Builder with initial map generator - pub fn new(width: usize, height: usize) -> MapBuilder { + pub fn new(width: usize, height: usize) -> MapBuilder { MapBuilder { width, height, @@ -66,20 +66,20 @@ impl MapBuilder { } } - pub fn with(&mut self, modifier : Box) -> &mut MapBuilder { + pub fn with(&mut self, modifier : Box>) -> &mut MapBuilder { self.modifiers.push(modifier); self } /// Build map using random number seeded with system time - pub fn build(&mut self) -> Map { + pub fn build(&mut self) -> Map { 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); self.build_with_rng(&mut rng) } /// 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 { let mut map = Map::new(self.width, self.height); // Build additional layers in turn @@ -97,6 +97,8 @@ impl MapBuilder { /// ------------------------------------------------------------------------------------------------ #[cfg(test)] mod tests { + use crate::map::NoData; + use super::*; use filter::{ CellularAutomata, @@ -106,7 +108,7 @@ mod tests { #[test] fn test_ca_map() { - let map = MapBuilder::new(80, 50) + let map = MapBuilder::::new(80, 50) .with(NoiseGenerator::new(0.55)) .with(CellularAutomata::new()) .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) diff --git a/src/map.rs b/src/map.rs index 8339ae1..36551f9 100644 --- a/src/map.rs +++ b/src/map.rs @@ -21,9 +21,18 @@ pub struct Tile { 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 #[derive(Default, Clone)] -pub struct Map { +pub struct Map { pub tiles : Vec, pub width : usize, pub height : usize, @@ -31,6 +40,7 @@ pub struct Map { pub exit_point: Option, pub rooms: Vec, pub corridors: Vec>, + pub data: D, } impl Tile { @@ -59,10 +69,10 @@ impl Tile { } } -impl Map { +impl Map { /// 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 { let map_tile_count = width*height; Map{ tiles : vec![Tile::wall(); map_tile_count], @@ -71,12 +81,13 @@ impl Map { starting_point: None, exit_point: None, rooms: Vec::new(), - corridors: Vec::new() + corridors: Vec::new(), + data: Default::default() } } /// Create map from given string - pub fn from_string(map_string: &str) -> Map { + pub fn from_string(map_string: &str) -> Map { let lines: Vec<&str> = map_string.split("\n") .map(|l| l.trim()) .filter(|l| l.len() > 0) @@ -235,7 +246,7 @@ impl Map { } } -impl fmt::Display for Map { +impl fmt::Display for Map { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for y in 0..self.height { let bytes: Vec = (0..self.width) @@ -257,7 +268,7 @@ mod tests { #[test] fn test_new_map() { - let map = Map::new(10, 10); + let map = Map::::new(10, 10); for i in 0..10 { for j in 0..10 { assert!(map.at(i, j).is_blocked); @@ -272,7 +283,7 @@ mod tests { # # ########## "; - let map = Map::from_string(map_str); + let map = Map::::from_string(map_str); assert_eq!(map.width, 10); assert_eq!(map.height, 3); @@ -295,7 +306,7 @@ mod tests { # # ########## "; - let map = Map::from_string(map_str); + let map = Map::::from_string(map_str); let exists = map.get_available_exits(1, 1); let expected_exists = vec![(2, 1, 1.0), (1, 2, 1.0), (2, 2, 1.45)]; assert_eq!(exists, expected_exists); @@ -303,7 +314,7 @@ mod tests { #[test] fn test_create_room() { - let mut map = Map::new(5, 5); + let mut map = Map::::new(5, 5); map.add_room(Rect::new(1, 1, 3, 3)); for x in 0..map.width { for y in 0..map.height { @@ -323,13 +334,13 @@ mod tests { # # # ########## "; - let mut map = Map::from_string(map_str); + let mut map = Map::::from_string(map_str); let expected_map_str = " ########## # # ########## "; - let expected_map = Map::from_string(expected_map_str); + let expected_map = Map::::from_string(expected_map_str); 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::::from_string(map_str); let exists = map.get_available_exits(0, 0); assert_eq!(exists.len(), 1); diff --git a/src/metric.rs b/src/metric.rs index 4736fcb..9dbe0fa 100644 --- a/src/metric.rs +++ b/src/metric.rs @@ -4,6 +4,7 @@ //! and the provide generator score as an average. //! +use super::BuilderData; use super::map::Map; use super::dijkstra::DijkstraMap; @@ -11,7 +12,7 @@ use super::dijkstra::DijkstraMap; /// This metric calculates the percentage of walkable cells (Floor). /// If this number is very low (like < 10%) then it means that the map /// is probably to degenerated and shouldn't be used -pub fn density(map: &Map) -> f32 { +pub fn density(map: &Map) -> f32 { let floor_count = map.tiles.iter() .filter(|&t| t.is_walkable()) .count(); @@ -22,7 +23,7 @@ pub fn density(map: &Map) -> f32 { /// Calculate the length of the shortes path from the starting point /// to the exit. /// If this path is very short, then the map is probably degenerated. -pub fn path_length(map: &Map) -> f32 { +pub fn path_length(map: &Map) -> f32 { if map.starting_point.is_none() { return 0.0 } @@ -43,12 +44,12 @@ pub fn path_length(map: &Map) -> f32 { #[cfg(test)] mod tests { use super::*; - use crate::geometry::Point; + use crate::{geometry::Point, map::NoData}; #[test] fn test_density_no_floor() { - let map = Map::new(10, 10); + let map = Map::::new(10, 10); let score = density(&map); assert_eq!(score, 0.0); } @@ -60,7 +61,7 @@ mod tests { # ## # ########## "; - let map = Map::from_string(map_str); + let map = Map::::from_string(map_str); let score = density(&map); assert_eq!(score, 0.2); } @@ -72,7 +73,7 @@ mod tests { # ## # ########## "; - let map = Map::from_string(map_str); + let map = Map::::from_string(map_str); let score = path_length(&map); assert_eq!(score, 0.0); } @@ -85,7 +86,7 @@ mod tests { # # ########## "; - let mut map = Map::from_string(map_str); + let mut map = Map::::from_string(map_str); map.starting_point = Some(Point::new(1,1)); map.exit_point = Some(Point::new(8,1));