cargo fmt

This commit is contained in:
Nolan Darilek 2022-03-12 14:31:30 -06:00
parent efad0132ff
commit ffaad6cf48
22 changed files with 509 additions and 422 deletions

View File

@ -1,6 +1,5 @@
use rand::prelude::*;
use mapgen::*; use mapgen::*;
use rand::prelude::*;
fn main() { fn main() {
let mut rng = StdRng::seed_from_u64(907647352); let mut rng = StdRng::seed_from_u64(907647352);

View File

@ -1,10 +1,11 @@
use std::time::{SystemTime, UNIX_EPOCH};
use rand::prelude::*;
use mapgen::*; use mapgen::*;
use rand::prelude::*;
use std::time::{SystemTime, UNIX_EPOCH};
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::<NoData>::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));

View File

@ -1,17 +1,10 @@
use mapgen::{ use mapgen::{
MapBuilder,
NoData,
filter::{ filter::{
NoiseGenerator, AreaStartingPosition, CellularAutomata, CullUnreachable, NoiseGenerator, XStart, YStart,
CellularAutomata,
CullUnreachable,
AreaStartingPosition,
XStart,
YStart,
}, },
MapBuilder, NoData,
}; };
fn main() { fn main() {
let map = MapBuilder::<NoData>::new(20, 20) let map = MapBuilder::<NoData>::new(20, 20)
.with(NoiseGenerator::uniform()) .with(NoiseGenerator::uniform())

View File

@ -25,10 +25,9 @@
//! --- //! ---
//! //!
use std::{collections::VecDeque, marker::PhantomData};
use std::f32::MAX;
use super::map::{BuilderData, Map}; use super::map::{BuilderData, Map};
use std::f32::MAX;
use std::{collections::VecDeque, marker::PhantomData};
/// 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).
@ -79,8 +78,12 @@ impl<D: BuilderData> DijkstraMap<D> {
let idx = self.xy_idx(x, y); let idx = self.xy_idx(x, y);
let new_depth = depth + add_depth; let new_depth = depth + add_depth;
let prev_depth = self.tiles[idx]; let prev_depth = self.tiles[idx];
if new_depth >= prev_depth { continue; } if new_depth >= prev_depth {
if new_depth >= self.max_depth { continue; } continue;
}
if new_depth >= self.max_depth {
continue;
}
self.tiles[idx] = new_depth; self.tiles[idx] = new_depth;
open_list.push_back(((x, y), new_depth)); open_list.push_back(((x, y), new_depth));
} }
@ -88,7 +91,7 @@ impl<D: BuilderData> DijkstraMap<D> {
} }
fn xy_idx(&self, x: usize, y: usize) -> usize { fn xy_idx(&self, x: usize, y: usize) -> usize {
(y * self.size_x ) + x (y * self.size_x) + x
} }
} }
@ -112,13 +115,19 @@ mod tests {
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);
println!("{:?}", &dm.tiles.iter().map(|&v| if v == f32::MAX {9.0} else {v}).collect::<Vec<f32>>()); println!(
"{:?}",
&dm.tiles
.iter()
.map(|&v| if v == f32::MAX { 9.0 } else { v })
.collect::<Vec<f32>>()
);
assert_eq!(dm.size_x, 10); assert_eq!(dm.size_x, 10);
assert_eq!(dm.size_y, 3); assert_eq!(dm.size_y, 3);
for i in 0..10 { for i in 0..10 {
assert_eq!(dm.tiles[i], MAX); assert_eq!(dm.tiles[i], MAX);
assert_eq!(dm.tiles[2*dm.size_x + i], MAX); assert_eq!(dm.tiles[2 * dm.size_x + i], MAX);
let idx = dm.size_x + i; let idx = dm.size_x + i;
if i < 3 || i == 9 { if i < 3 || i == 9 {
assert_eq!(dm.tiles[idx], MAX); assert_eq!(dm.tiles[idx], MAX);
@ -139,10 +148,9 @@ mod tests {
let mut map = Map::<NoData>::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, 1.45, 1.0, MAX, MAX, MAX, MAX, MAX, MAX, 1.45, 1.0, MAX, MAX, 1.0, 0.0, MAX, MAX, MAX, MAX, MAX,
MAX, 1.0, 0.0, MAX, ];
MAX, MAX, MAX, MAX];
assert_eq!(dm.tiles, expected); assert_eq!(dm.tiles, expected);
} }
@ -158,10 +166,11 @@ mod tests {
let mut map = Map::<NoData>::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, 7.45, 6.45, 5.45, 4.45, 3.45, 2.45, 1.45, 1.0, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, 7.45, 6.45, 5.45, 4.45, 3.45,
MAX, 7.9, 6.9, MAX, 4.0, 3.0, 2.0, 1.0, 0.0, MAX, 2.45, 1.45, 1.0, MAX, MAX, 7.9, 6.9, MAX, 4.0, 3.0, 2.0, 1.0, 0.0, MAX, MAX, MAX, MAX,
MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX]; MAX, MAX, MAX, MAX, MAX, MAX, MAX,
];
for (v, e) in dm.tiles.iter().zip(expected.iter()) { for (v, e) in dm.tiles.iter().zip(expected.iter()) {
assert!(f32::abs(v - e) <= 0.01); assert!(f32::abs(v - e) <= 0.01);

View File

@ -22,12 +22,11 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::*;
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;
use crate::{BuilderData, MapFilter};
use rand::prelude::*;
pub struct BspInterior<D: BuilderData> { pub struct BspInterior<D: BuilderData> {
min_room_size: usize, min_room_size: usize,
@ -41,9 +40,8 @@ impl<D: BuilderData> MapFilter<D> for BspInterior<D> {
} }
impl<D: BuilderData> BspInterior<D> { impl<D: BuilderData> BspInterior<D> {
pub fn new() -> Box<BspInterior<D>> { pub fn new() -> Box<BspInterior<D>> {
Box::new(BspInterior{ Box::new(BspInterior {
min_room_size: 8, min_room_size: 8,
phantom: PhantomData, phantom: PhantomData,
}) })
@ -52,7 +50,7 @@ impl<D: BuilderData> BspInterior<D> {
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> { 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));
let first_room = rects[0]; let first_room = rects[0];
// Divide the first room // Divide the first room
self.add_subrects(first_room, rng, &mut rects); self.add_subrects(first_room, rng, &mut rects);
@ -64,9 +62,9 @@ impl<D: BuilderData> BspInterior<D> {
} }
// Now we want corridors // Now we want corridors
for i in 0..new_map.rooms.len()-1 { for i in 0..new_map.rooms.len() - 1 {
let room = new_map.rooms[i]; let room = new_map.rooms[i];
let next_room = new_map.rooms[i+1]; let next_room = new_map.rooms[i + 1];
let start_x = rng.random_range(room.x1, room.x2); let start_x = rng.random_range(room.x1, room.x2);
let start_y = rng.random_range(room.y1, room.y2); let start_y = rng.random_range(room.y1, room.y2);
let end_x = rng.random_range(next_room.x1, next_room.x2); let end_x = rng.random_range(next_room.x1, next_room.x2);
@ -93,32 +91,39 @@ impl<D: BuilderData> BspInterior<D> {
if split <= 2 { if split <= 2 {
// Horizontal split // Horizontal split
let h1 = Rect::new( rect.x1, rect.y1, half_width-1, height ); let h1 = Rect::new(rect.x1, rect.y1, half_width - 1, height);
rects.push( h1 ); rects.push(h1);
if half_width > self.min_room_size { self.add_subrects(h1, rng, rects); } if half_width > self.min_room_size {
let h2 = Rect::new( rect.x1 + half_width, rect.y1, half_width, height ); self.add_subrects(h1, rng, rects);
rects.push( h2 ); }
if half_width > self.min_room_size { self.add_subrects(h2, rng, rects); } let h2 = Rect::new(rect.x1 + half_width, rect.y1, half_width, height);
rects.push(h2);
if half_width > self.min_room_size {
self.add_subrects(h2, rng, rects);
}
} else { } else {
// Vertical split // Vertical split
let v1 = Rect::new( rect.x1, rect.y1, width, half_height-1 ); let v1 = Rect::new(rect.x1, rect.y1, width, half_height - 1);
rects.push(v1); rects.push(v1);
if half_height > self.min_room_size { self.add_subrects(v1, rng, rects); } if half_height > self.min_room_size {
let v2 = Rect::new( rect.x1, rect.y1 + half_height, width, half_height ); self.add_subrects(v1, rng, rects);
}
let v2 = Rect::new(rect.x1, rect.y1 + half_height, width, half_height);
rects.push(v2); rects.push(v2);
if half_height > self.min_room_size { self.add_subrects(v2, rng, rects); } if half_height > self.min_room_size {
self.add_subrects(v2, rng, rects);
}
} }
} }
} }
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
/// Module unit tests /// Module unit tests
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{Map, map::NoData}; use crate::{map::NoData, Map};
#[test] #[test]
fn no_corridors_on_borders() { fn no_corridors_on_borders() {
@ -134,5 +139,4 @@ mod tests {
assert!(map.at(79, j).is_blocked()); assert!(map.at(79, j).is_blocked());
} }
} }
} }

View File

@ -20,13 +20,12 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::*;
use crate::BuilderData;
use crate::MapFilter;
use crate::geometry::Rect; use crate::geometry::Rect;
use crate::random::Rng; use crate::random::Rng;
use crate::BuilderData;
use crate::Map; use crate::Map;
use crate::MapFilter;
use rand::prelude::*;
pub struct BspRooms<D: BuilderData> { pub struct BspRooms<D: BuilderData> {
max_split: usize, max_split: usize,
@ -47,11 +46,11 @@ impl<D: BuilderData> BspRooms<D> {
}) })
} }
fn build_rooms(&self, map: &Map<D>, rng : &mut StdRng) -> Map<D> { 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
rects.push( Rect::new(2, 2, new_map.width-5, new_map.height-5) ); rects.push(Rect::new(2, 2, new_map.width - 5, new_map.height - 5));
let first_room = rects[0]; let first_room = rects[0];
rects.append(&mut self.split_into_subrects(first_room)); // Divide the first room rects.append(&mut self.split_into_subrects(first_room)); // Divide the first room
@ -77,16 +76,33 @@ impl<D: BuilderData> BspRooms<D> {
let half_width = usize::max(width / 2, 1); let half_width = usize::max(width / 2, 1);
let half_height = usize::max(height / 2, 1); let half_height = usize::max(height / 2, 1);
rects.push(Rect::new( rect.x1, rect.y1, half_width, half_height )); rects.push(Rect::new(rect.x1, rect.y1, half_width, half_height));
rects.push(Rect::new( rect.x1, rect.y1 + half_height, half_width, half_height )); rects.push(Rect::new(
rects.push(Rect::new( rect.x1 + half_width, rect.y1, half_width, half_height )); rect.x1,
rects.push(Rect::new( rect.x1 + half_width, rect.y1 + half_height, half_width, half_height )); rect.y1 + half_height,
half_width,
half_height,
));
rects.push(Rect::new(
rect.x1 + half_width,
rect.y1,
half_width,
half_height,
));
rects.push(Rect::new(
rect.x1 + half_width,
rect.y1 + half_height,
half_width,
half_height,
));
rects rects
} }
fn get_random_rect(&self, rng : &mut StdRng, rects: &Vec<Rect>) -> Rect { fn get_random_rect(&self, rng: &mut StdRng, rects: &Vec<Rect>) -> Rect {
if rects.len() == 1 { return rects[0]; } if rects.len() == 1 {
return rects[0];
}
let idx = rng.random_range(0, rects.len()); let idx = rng.random_range(0, rects.len());
rects[idx] rects[idx]
} }
@ -117,15 +133,25 @@ impl<D: BuilderData> BspRooms<D> {
let mut can_build = true; let mut can_build = true;
for r in map.rooms.iter() { for r in map.rooms.iter() {
if r.intersect(&rect) { can_build = false; } if r.intersect(&rect) {
can_build = false;
}
} }
for y in expanded.y1 ..= expanded.y2 { for y in expanded.y1..=expanded.y2 {
for x in expanded.x1 ..= expanded.x2 { for x in expanded.x1..=expanded.x2 {
if x > map.width - 2 { can_build = false; } if x > map.width - 2 {
if y > map.height - 2 { can_build = false; } can_build = false;
if x < 1 { can_build = false; } }
if y < 1 { can_build = false; } if y > map.height - 2 {
can_build = false;
}
if x < 1 {
can_build = false;
}
if y < 1 {
can_build = false;
}
if can_build { if can_build {
if map.at(x as usize, y as usize).is_walkable() { if map.at(x as usize, y as usize).is_walkable() {
can_build = false; can_build = false;
@ -160,5 +186,4 @@ mod tests {
assert!(map.at(79, j).is_blocked()); assert!(map.at(79, j).is_blocked());
} }
} }
} }

View File

@ -23,11 +23,10 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::*;
use crate::BuilderData; use crate::BuilderData;
use crate::MapFilter; use crate::MapFilter;
use crate::{Map, Tile}; use crate::{Map, Tile};
use rand::prelude::*;
/// Map filter /// Map filter
pub struct CellularAutomata<D: BuilderData> { pub struct CellularAutomata<D: BuilderData> {
@ -59,26 +58,31 @@ impl<D: BuilderData> CellularAutomata<D> {
new_map new_map
} }
} }
fn apply_iteration<D: BuilderData>(map: &Map<D>) -> Map<D> { 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 {
for x in 1..map.width-1 { for x in 1..map.width - 1 {
let idxs = [ let idxs = [
(x-1, y-1), (x, y-1), (x+1, y-1), (x - 1, y - 1),
(x-1, y), (x+1, y), (x, y - 1),
(x-1, y+1), (x, y+1), (x+1, y+1)]; (x + 1, y - 1),
let neighbors = idxs.iter() (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).is_blocked()) .filter(|(x, y)| map.at(*x, *y).is_blocked())
.count(); .count();
if neighbors > 4 || neighbors == 0 { if neighbors > 4 || neighbors == 0 {
new_map.set_tile(x, y, Tile::wall()) new_map.set_tile(x, y, Tile::wall())
} } else {
else {
new_map.set_tile(x, y, Tile::floor()); new_map.set_tile(x, y, Tile::floor());
} }
} }
@ -103,7 +107,6 @@ mod tests {
assert!(new_map.at(1, 1).is_blocked()); assert!(new_map.at(1, 1).is_blocked());
} }
#[test] #[test]
fn test_iteration_floor() { fn test_iteration_floor() {
let mut map = Map::<NoData>::new(3, 3); let mut map = Map::<NoData>::new(3, 3);
@ -115,5 +118,4 @@ mod tests {
let new_map = apply_iteration(&map); let new_map = apply_iteration(&map);
assert!(new_map.at(1, 1).is_walkable()); assert!(new_map.at(1, 1).is_walkable());
} }
} }

View File

@ -6,11 +6,10 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::StdRng; use crate::dijkstra::DijkstraMap;
use crate::MapFilter; use crate::MapFilter;
use crate::{BuilderData, Map, Tile}; use crate::{BuilderData, Map, Tile};
use crate::dijkstra::DijkstraMap; use rand::prelude::StdRng;
/// Remove unreachable areas from the map. /// Remove unreachable areas from the map.
pub struct CullUnreachable<D: BuilderData> { pub struct CullUnreachable<D: BuilderData> {
@ -53,11 +52,11 @@ impl<D: BuilderData> CullUnreachable<D> {
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rand::prelude::*;
use super::*;
use super::MapFilter; use super::MapFilter;
use super::*;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::{Map, NoData}; use crate::map::{Map, NoData};
use rand::prelude::*;
#[test] #[test]
fn test_culling() { fn test_culling() {
@ -75,7 +74,6 @@ mod tests {
"; ";
let expected_map = Map::<NoData>::from_string(expected_map_str); let expected_map = Map::<NoData>::from_string(expected_map_str);
let modifier = CullUnreachable::new(); let modifier = CullUnreachable::new();
let mut rng = StdRng::seed_from_u64(0); let mut rng = StdRng::seed_from_u64(0);
let new_map = modifier.modify_map(&mut rng, &map); let new_map = modifier.modify_map(&mut rng, &map);

View File

@ -4,15 +4,14 @@
//! It means that starting point have to be set before this Modyfier will start. //! It means that starting point have to be set before this Modyfier will start.
//! //!
use crate::dijkstra::DijkstraMap;
use crate::geometry::Point;
use crate::BuilderData;
use crate::Map;
use crate::MapFilter;
use rand::prelude::StdRng;
use std::f32; use std::f32;
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::StdRng;
use crate::BuilderData;
use crate::geometry::Point;
use crate::MapFilter;
use crate::Map;
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<D: BuilderData> { pub struct DistantExit<D: BuilderData> {
@ -57,11 +56,11 @@ impl<D: BuilderData> DistantExit<D> {
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rand::prelude::*;
use super::*;
use super::MapFilter; use super::MapFilter;
use super::*;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::{Map, NoData}; use crate::map::{Map, NoData};
use rand::prelude::*;
#[test] #[test]
fn test_exit() { fn test_exit() {

View File

@ -15,22 +15,24 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::*;
use crate::MapFilter; use crate::MapFilter;
use crate::{ use crate::{
BuilderData,
map::{Map, Symmetry, Tile},
geometry::Point, geometry::Point,
random::Rng map::{Map, Symmetry, Tile},
random::Rng,
BuilderData,
}; };
use rand::prelude::*;
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone)]
pub enum DrunkSpawnMode { StartingPoint, Random } pub enum DrunkSpawnMode {
StartingPoint,
Random,
}
pub struct DrunkardsWalk<D: BuilderData> { 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,
@ -44,13 +46,14 @@ impl<D: BuilderData> MapFilter<D> for DrunkardsWalk<D> {
} }
impl<D: BuilderData> DrunkardsWalk<D> { 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<D>> symmetry: Symmetry,
{ ) -> Box<DrunkardsWalk<D>> {
Box::new(DrunkardsWalk{ Box::new(DrunkardsWalk {
spawn_mode, spawn_mode,
drunken_lifetime, drunken_lifetime,
floor_percent, floor_percent,
@ -83,7 +86,7 @@ impl<D: BuilderData> DrunkardsWalk<D> {
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> { 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);
new_map.set_tile(starting_position.x, starting_position.y, Tile::floor()); new_map.set_tile(starting_position.x, starting_position.y, Tile::floor());
let total_tiles = new_map.width * new_map.height; let total_tiles = new_map.width * new_map.height;
@ -116,10 +119,26 @@ impl<D: BuilderData> DrunkardsWalk<D> {
let stagger_direction = rng.roll_dice(1, 4); let stagger_direction = rng.roll_dice(1, 4);
match stagger_direction { match stagger_direction {
1 => { if drunk_x > 1 { drunk_x -= 1; } } 1 => {
2 => { if drunk_x < new_map.width-2 { drunk_x += 1; } } if drunk_x > 1 {
3 => { if drunk_y > 1 { drunk_y -=1; } } drunk_x -= 1;
_ => { if drunk_y < new_map.height-2 { drunk_y += 1; } } }
}
2 => {
if drunk_x < new_map.width - 2 {
drunk_x += 1;
}
}
3 => {
if drunk_y > 1 {
drunk_y -= 1;
}
}
_ => {
if drunk_y < new_map.height - 2 {
drunk_y += 1;
}
}
} }
drunk_life -= 1; drunk_life -= 1;

View File

@ -15,13 +15,12 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::*;
use crate::MapFilter; use crate::MapFilter;
use crate::{ use crate::{
map::{BuilderData, Map, Tile}, map::{BuilderData, Map, Tile},
random::Rng random::Rng,
}; };
use rand::prelude::*;
pub struct MazeBuilder<D: BuilderData> { pub struct MazeBuilder<D: BuilderData> {
phantom: PhantomData<D>, phantom: PhantomData<D>,
@ -43,7 +42,7 @@ impl<D: BuilderData> MazeBuilder<D> {
#[allow(clippy::map_entry)] #[allow(clippy::map_entry)]
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> { 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);
new_map new_map
} }
@ -51,10 +50,10 @@ impl<D: BuilderData> MazeBuilder<D> {
/* Maze code taken under MIT from https://github.com/cyucelen/mazeGenerator/ */ /* Maze code taken under MIT from https://github.com/cyucelen/mazeGenerator/ */
const TOP : usize = 0; const TOP: usize = 0;
const RIGHT : usize = 1; const RIGHT: usize = 1;
const BOTTOM : usize = 2; const BOTTOM: usize = 2;
const LEFT : usize = 3; const LEFT: usize = 3;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Cell { struct Cell {
@ -66,31 +65,28 @@ struct Cell {
impl Cell { impl Cell {
fn new(row: i32, column: i32) -> Cell { fn new(row: i32, column: i32) -> Cell {
Cell{ Cell {
row, row,
column, column,
walls: [true, true, true, true], walls: [true, true, true, true],
visited: false visited: false,
} }
} }
fn remove_walls(&mut self, next : &mut Cell) { fn remove_walls(&mut self, next: &mut Cell) {
let x = self.column - next.column; let x = self.column - next.column;
let y = self.row - next.row; let y = self.row - next.row;
if x == 1 { if x == 1 {
self.walls[LEFT] = false; self.walls[LEFT] = false;
next.walls[RIGHT] = false; next.walls[RIGHT] = false;
} } else if x == -1 {
else if x == -1 {
self.walls[RIGHT] = false; self.walls[RIGHT] = false;
next.walls[LEFT] = false; next.walls[LEFT] = false;
} } else if y == 1 {
else if y == 1 {
self.walls[TOP] = false; self.walls[TOP] = false;
next.walls[BOTTOM] = false; next.walls[BOTTOM] = false;
} } else if y == -1 {
else if y == -1 {
self.walls[BOTTOM] = false; self.walls[BOTTOM] = false;
next.walls[TOP] = false; next.walls[TOP] = false;
} }
@ -103,13 +99,13 @@ struct Grid<'a, D: BuilderData> {
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>, phantom: PhantomData<D>,
} }
impl<'a, D: BuilderData> Grid<'a, D> { impl<'a, D: BuilderData> Grid<'a, D> {
fn new(width: i32, height:i32, rng: &mut StdRng) -> Grid<D> { 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(),
@ -129,7 +125,7 @@ impl<'a, D: BuilderData> Grid<'a, D> {
} }
fn calculate_index(&self, row: i32, column: i32) -> i32 { fn calculate_index(&self, row: i32, column: i32) -> i32 {
if row < 0 || column < 0 || column > self.width-1 || row > self.height-1 { if row < 0 || column < 0 || column > self.width - 1 || row > self.height - 1 {
-1 -1
} else { } else {
column + (row * self.width) column + (row * self.width)
@ -137,16 +133,16 @@ impl<'a, D: BuilderData> Grid<'a, D> {
} }
fn get_available_neighbors(&self) -> Vec<usize> { fn get_available_neighbors(&self) -> Vec<usize> {
let mut neighbors : Vec<usize> = Vec::new(); let mut neighbors: Vec<usize> = Vec::new();
let current_row = self.cells[self.current].row; let current_row = self.cells[self.current].row;
let current_column = self.cells[self.current].column; let current_column = self.cells[self.current].column;
let neighbor_indices : [i32; 4] = [ let neighbor_indices: [i32; 4] = [
self.calculate_index(current_row -1, current_column), self.calculate_index(current_row - 1, current_column),
self.calculate_index(current_row, current_column + 1), self.calculate_index(current_row, current_column + 1),
self.calculate_index(current_row + 1, current_column), self.calculate_index(current_row + 1, current_column),
self.calculate_index(current_row, current_column - 1) self.calculate_index(current_row, current_column - 1),
]; ];
for i in neighbor_indices.iter() { for i in neighbor_indices.iter() {
@ -164,7 +160,7 @@ impl<'a, D: BuilderData> Grid<'a, D> {
if neighbors.len() == 1 { if neighbors.len() == 1 {
return Some(neighbors[0]); return Some(neighbors[0]);
} else { } else {
return Some(neighbors[(self.rng.roll_dice(1, neighbors.len())-1) as usize]); return Some(neighbors[(self.rng.roll_dice(1, neighbors.len()) - 1) as usize]);
} }
} }
None None
@ -209,17 +205,27 @@ impl<'a, D: BuilderData> Grid<'a, D> {
fn copy_to_map(&self, map: &mut Map<D>) { 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();
}
for cell in self.cells.iter() { for cell in self.cells.iter() {
let x = (cell.column as usize + 1) * 2; let x = (cell.column as usize + 1) * 2;
let y = (cell.row as usize + 1) * 2; let y = (cell.row as usize + 1) * 2;
map.set_tile(x, y, Tile::floor()); map.set_tile(x, y, Tile::floor());
if !cell.walls[TOP] { map.set_tile(x, y-1, Tile::floor()) } if !cell.walls[TOP] {
if !cell.walls[RIGHT] { map.set_tile(x+1, y, Tile::floor()) } map.set_tile(x, y - 1, Tile::floor())
if !cell.walls[BOTTOM] { map.set_tile(x, y+1, Tile::floor()) } }
if !cell.walls[LEFT] { map.set_tile(x-1, y, Tile::floor()) } if !cell.walls[RIGHT] {
map.set_tile(x + 1, y, Tile::floor())
}
if !cell.walls[BOTTOM] {
map.set_tile(x, y + 1, Tile::floor())
}
if !cell.walls[LEFT] {
map.set_tile(x - 1, y, Tile::floor())
}
} }
} }
} }

View File

@ -9,8 +9,8 @@ pub mod distant_exit;
pub mod drunkard; pub mod drunkard;
pub mod maze; pub mod maze;
pub mod noise_generator; pub mod noise_generator;
pub mod simple_rooms;
pub mod rooms_corridors_nearest; pub mod rooms_corridors_nearest;
pub mod simple_rooms;
pub mod starting_point; pub mod starting_point;
pub mod voronoi; pub mod voronoi;
@ -22,8 +22,7 @@ pub use distant_exit::DistantExit;
pub use drunkard::DrunkardsWalk; pub use drunkard::DrunkardsWalk;
pub use maze::MazeBuilder; pub use maze::MazeBuilder;
pub use noise_generator::NoiseGenerator; pub use noise_generator::NoiseGenerator;
pub use simple_rooms::SimpleRooms;
pub use rooms_corridors_nearest::NearestCorridors; pub use rooms_corridors_nearest::NearestCorridors;
pub use simple_rooms::SimpleRooms;
pub use starting_point::{AreaStartingPosition, XStart, YStart}; pub use starting_point::{AreaStartingPosition, XStart, YStart};
pub use voronoi::VoronoiHive; pub use voronoi::VoronoiHive;

View File

@ -18,10 +18,9 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::*;
use crate::MapFilter; use crate::MapFilter;
use crate::{BuilderData, Map, Tile}; use crate::{BuilderData, Map, Tile};
use rand::prelude::*;
/// Map noise generator /// Map noise generator
pub struct NoiseGenerator<D: BuilderData> { pub struct NoiseGenerator<D: BuilderData> {
@ -56,15 +55,17 @@ impl<D: BuilderData> NoiseGenerator<D> {
fn build(&self, map: &Map<D>, rng: &mut StdRng) -> Map<D> { 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 {
for x in 1..new_map.width-1 { for x in 1..new_map.width - 1 {
let roll = rng.next_u32() % 100; let roll = rng.next_u32() % 100;
if roll > p { new_map.set_tile(x, y, Tile::floor()) } if roll > p {
else { new_map.set_tile(x, y, Tile::wall()) } new_map.set_tile(x, y, Tile::floor())
} else {
new_map.set_tile(x, y, Tile::wall())
}
} }
} }
new_map new_map
} }
} }

View File

@ -1,13 +1,12 @@
//! Connect nearest rooms on the map with corridors //! Connect nearest rooms on the map with corridors
//! //!
use rand::prelude::StdRng;
use crate::BuilderData; use crate::BuilderData;
use crate::MapFilter;
use crate::Map; use crate::Map;
use crate::MapFilter;
use rand::prelude::StdRng;
use std::collections::HashSet; use std::collections::HashSet;
use std::marker::PhantomData; use std::marker::PhantomData;
pub struct NearestCorridors<D: BuilderData> { pub struct NearestCorridors<D: BuilderData> {
phantom: PhantomData<D>, phantom: PhantomData<D>,
} }
@ -19,7 +18,6 @@ impl<D: BuilderData> MapFilter<D> for NearestCorridors<D> {
} }
impl<D: BuilderData> NearestCorridors<D> { impl<D: BuilderData> NearestCorridors<D> {
pub fn new() -> Box<NearestCorridors<D>> { pub fn new() -> Box<NearestCorridors<D>> {
Box::new(NearestCorridors { Box::new(NearestCorridors {
phantom: PhantomData, phantom: PhantomData,
@ -29,11 +27,11 @@ impl<D: BuilderData> NearestCorridors<D> {
fn corridors(&self, map: &Map<D>) -> Map<D> { 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();
for (i,room) in map.rooms.iter().enumerate() { for (i, room) in map.rooms.iter().enumerate() {
let mut room_distance : Vec<(usize, f32)> = Vec::new(); let mut room_distance: Vec<(usize, f32)> = Vec::new();
let room_center = room.center(); let room_center = room.center();
for (j,other_room) in new_map.rooms.iter().enumerate() { for (j, other_room) in new_map.rooms.iter().enumerate() {
if i != j && !connected.contains(&j) { if i != j && !connected.contains(&j) {
let other_center = other_room.center(); let other_center = other_room.center();
let distance = room_center.distance_to(&other_center); let distance = room_center.distance_to(&other_center);
@ -42,7 +40,7 @@ impl<D: BuilderData> NearestCorridors<D> {
} }
if !room_distance.is_empty() { if !room_distance.is_empty() {
room_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap() ); room_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
let dest_center = new_map.rooms[room_distance[0].0].center(); let dest_center = new_map.rooms[room_distance[0].0].center();
new_map.add_corridor(room_center, dest_center); new_map.add_corridor(room_center, dest_center);
connected.insert(i); connected.insert(i);

View File

@ -20,13 +20,12 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::*;
use crate::BuilderData;
use crate::MapFilter;
use crate::geometry::Rect; use crate::geometry::Rect;
use crate::random::Rng; use crate::random::Rng;
use crate::BuilderData;
use crate::Map; use crate::Map;
use crate::MapFilter;
use rand::prelude::*;
pub struct SimpleRooms<D: BuilderData> { pub struct SimpleRooms<D: BuilderData> {
max_rooms: usize, max_rooms: usize,
@ -41,10 +40,9 @@ impl<D: BuilderData> MapFilter<D> for SimpleRooms<D> {
} }
} }
impl<D: BuilderData> SimpleRooms<D> { impl<D: BuilderData> SimpleRooms<D> {
pub fn new() -> Box<SimpleRooms<D>> { 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,
@ -52,7 +50,7 @@ impl<D: BuilderData> SimpleRooms<D> {
}) })
} }
fn build_rooms(&self, map: &Map<D>, rng : &mut StdRng) -> Map<D> { 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

@ -22,23 +22,30 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::StdRng;
use crate::BuilderData;
use crate::MapFilter;
use crate::geometry::Point; use crate::geometry::Point;
use crate::BuilderData;
use crate::Map; use crate::Map;
use crate::MapFilter;
use rand::prelude::StdRng;
/// Initial x region position /// Initial x region position
pub enum XStart { LEFT, CENTER, RIGHT } pub enum XStart {
LEFT,
CENTER,
RIGHT,
}
/// Initial y region position /// Initial y region position
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<D: BuilderData> { pub struct AreaStartingPosition<D: BuilderData> {
x : XStart, x: XStart,
y : YStart, y: YStart,
phantom: PhantomData<D>, phantom: PhantomData<D>,
} }
@ -50,43 +57,42 @@ impl<D: BuilderData> MapFilter<D> for AreaStartingPosition<D> {
impl<D: BuilderData> AreaStartingPosition<D> { 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<D>> { pub fn new(x: XStart, y: YStart) -> Box<AreaStartingPosition<D>> {
Box::new(AreaStartingPosition{ Box::new(AreaStartingPosition {
x, y, x,
y,
phantom: PhantomData, phantom: PhantomData,
}) })
} }
fn build(&self, map : &Map<D>) -> Map<D> { 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,
XStart::RIGHT => map.width - 2 XStart::RIGHT => map.width - 2,
}; };
let seed_y = match self.y { let seed_y = match self.y {
YStart::TOP => 1, YStart::TOP => 1,
YStart::CENTER => map.height / 2, YStart::CENTER => map.height / 2,
YStart::BOTTOM => map.height - 2 YStart::BOTTOM => map.height - 2,
}; };
let mut available_floors : Vec<(usize, f32)> = Vec::new(); let mut available_floors: Vec<(usize, f32)> = Vec::new();
for (idx, tiletype) in map.tiles.iter().enumerate() { for (idx, tiletype) in map.tiles.iter().enumerate() {
if tiletype.is_walkable() { if tiletype.is_walkable() {
available_floors.push( available_floors.push((
(
idx, idx,
Point::new(idx % map.width, idx / map.width) Point::new(idx % map.width, idx / map.width)
.distance_to(&Point::new(seed_x, seed_y)) .distance_to(&Point::new(seed_x, seed_y)),
) ));
);
} }
} }
if available_floors.is_empty() { if available_floors.is_empty() {
panic!("No valid floors to start on"); panic!("No valid floors to start on");
} }
available_floors.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); available_floors.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
let start_x = available_floors[0].0 % map.width; let start_x = available_floors[0].0 % map.width;
let start_y = available_floors[0].0 / map.width; let start_y = available_floors[0].0 / map.width;
@ -102,11 +108,11 @@ impl<D: BuilderData> AreaStartingPosition<D> {
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rand::prelude::*;
use super::*;
use super::MapFilter; use super::MapFilter;
use super::*;
use crate::geometry::Point; use crate::geometry::Point;
use crate::map::{Map, NoData}; use crate::map::{Map, NoData};
use rand::prelude::*;
#[test] #[test]
fn test_exit() { fn test_exit() {

View File

@ -15,21 +15,19 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use rand::prelude::*;
use crate::MapFilter; use crate::MapFilter;
use crate::{ use crate::{
geometry::Point,
map::{BuilderData, Map, Tile}, map::{BuilderData, Map, Tile},
random::Rng, random::Rng,
geometry::Point,
}; };
use rand::prelude::*;
pub struct VoronoiHive<D: BuilderData> { pub struct VoronoiHive<D: BuilderData> {
n_seeds: usize, n_seeds: usize,
phantom: PhantomData<D>, phantom: PhantomData<D>,
} }
impl<D: BuilderData> MapFilter<D> for VoronoiHive<D> { impl<D: BuilderData> MapFilter<D> for VoronoiHive<D> {
fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> { fn modify_map(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(rng, map) self.build(rng, map)
@ -38,7 +36,7 @@ impl<D: BuilderData> MapFilter<D> for VoronoiHive<D> {
impl<D: BuilderData> VoronoiHive<D> { impl<D: BuilderData> VoronoiHive<D> {
pub fn new() -> Box<VoronoiHive<D>> { pub fn new() -> Box<VoronoiHive<D>> {
Box::new(VoronoiHive{ Box::new(VoronoiHive {
n_seeds: 64, n_seeds: 64,
phantom: PhantomData, phantom: PhantomData,
}) })
@ -48,8 +46,8 @@ impl<D: BuilderData> VoronoiHive<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);
let mut voronoi_distance = vec![(0, 0.0f32) ; self.n_seeds]; let mut voronoi_distance = vec![(0, 0.0f32); self.n_seeds];
let mut voronoi_membership : Vec<i32> = vec![0 ; map.width as usize * map.height as usize]; let mut voronoi_membership: Vec<i32> = vec![0; map.width as usize * map.height as usize];
for (i, vid) in voronoi_membership.iter_mut().enumerate() { for (i, vid) in voronoi_membership.iter_mut().enumerate() {
let x = i % map.width; let x = i % map.width;
let y = i / map.width; let y = i / map.width;
@ -59,20 +57,28 @@ impl<D: BuilderData> VoronoiHive<D> {
voronoi_distance[seed] = (seed, distance); voronoi_distance[seed] = (seed, distance);
} }
voronoi_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); voronoi_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
*vid = voronoi_distance[0].0 as i32; *vid = voronoi_distance[0].0 as i32;
} }
for y in 1..new_map.height-1 { for y in 1..new_map.height - 1 {
for x in 1..new_map.width-1 { for x in 1..new_map.width - 1 {
let mut neighbors = 0; let mut neighbors = 0;
let my_idx = new_map.xy_idx(x, y); let my_idx = new_map.xy_idx(x, y);
let my_seed = voronoi_membership[my_idx]; let my_seed = voronoi_membership[my_idx];
if voronoi_membership[new_map.xy_idx(x-1, y)] != my_seed { neighbors += 1; } if voronoi_membership[new_map.xy_idx(x - 1, y)] != my_seed {
if voronoi_membership[new_map.xy_idx(x+1, y)] != my_seed { neighbors += 1; } neighbors += 1;
if voronoi_membership[new_map.xy_idx(x, y-1)] != my_seed { neighbors += 1; } }
if voronoi_membership[new_map.xy_idx(x, y+1)] != my_seed { neighbors += 1; } if voronoi_membership[new_map.xy_idx(x + 1, y)] != my_seed {
neighbors += 1;
}
if voronoi_membership[new_map.xy_idx(x, y - 1)] != my_seed {
neighbors += 1;
}
if voronoi_membership[new_map.xy_idx(x, y + 1)] != my_seed {
neighbors += 1;
}
if neighbors < 2 { if neighbors < 2 {
new_map.set_tile(x, y, Tile::floor()); new_map.set_tile(x, y, Tile::floor());
@ -88,8 +94,8 @@ impl<D: BuilderData> VoronoiHive<D> {
let mut seeds: Vec<Point> = Vec::new(); let mut seeds: Vec<Point> = Vec::new();
while seeds.len() < self.n_seeds { while seeds.len() < self.n_seeds {
let vx = rng.roll_dice(1, width-1); let vx = rng.roll_dice(1, width - 1);
let vy = rng.roll_dice(1, height-1); let vy = rng.roll_dice(1, height - 1);
let candidate = Point::new(vx, vy); let candidate = Point::new(vx, vy);
if !seeds.contains(&candidate) { if !seeds.contains(&candidate) {
seeds.push(candidate); seeds.push(candidate);
@ -98,5 +104,4 @@ impl<D: BuilderData> VoronoiHive<D> {
seeds seeds
} }
} }

View File

@ -5,13 +5,13 @@
#[derive(Default, PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(Default, PartialEq, Copy, Clone, Debug, Eq, Hash)]
pub struct Point { pub struct Point {
pub x: usize, pub x: usize,
pub y: usize pub y: usize,
} }
impl Point { impl Point {
/// Create new point /// Create new point
pub fn new(x: usize, y: usize) -> Point { pub fn new(x: usize, y: usize) -> Point {
Point {x, y} Point { x, y }
} }
/// Create new point from i32 coords /// Create new point from i32 coords
@ -30,28 +30,33 @@ impl Point {
/// Rectangle region on the map /// Rectangle region on the map
#[derive(PartialEq, Copy, Clone, Debug)] #[derive(PartialEq, Copy, Clone, Debug)]
pub struct Rect { pub struct Rect {
pub x1 : usize, pub x1: usize,
pub x2 : usize, pub x2: usize,
pub y1 : usize, pub y1: usize,
pub y2 : usize pub y2: usize,
} }
impl Rect { impl Rect {
pub fn new(x: usize, y: usize, width: usize, height: usize) -> Rect { pub fn new(x: usize, y: usize, width: usize, height: usize) -> Rect {
Rect{x1:x, y1:y, x2:x+width, y2:y+height} Rect {
x1: x,
y1: y,
x2: x + width,
y2: y + height,
}
} }
pub fn new_i32(x:i32, y: i32, width:i32, height:i32) -> Rect { pub fn new_i32(x: i32, y: i32, width: i32, height: i32) -> Rect {
Rect::new(x as usize, y as usize, width as usize, height as usize) Rect::new(x as usize, y as usize, width as usize, height as usize)
} }
/// Returns true if this overlaps with other /// Returns true if this overlaps with other
pub fn intersect(&self, other:&Rect) -> bool { pub fn intersect(&self, other: &Rect) -> bool {
self.x1 <= other.x2 && self.x2 >= other.x1 && self.y1 <= other.y2 && self.y2 >= other.y1 self.x1 <= other.x2 && self.x2 >= other.x1 && self.y1 <= other.y2 && self.y2 >= other.y1
} }
pub fn center(&self) -> Point { pub fn center(&self) -> Point {
Point::new((self.x1 + self.x2)/2, (self.y1 + self.y2)/2) Point::new((self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2)
} }
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
@ -80,7 +85,11 @@ impl Rect {
/// assert_eq!(usize_abs(3, 5), 2); /// assert_eq!(usize_abs(3, 5), 2);
/// ``` /// ```
pub fn usize_abs(x: usize, y: usize) -> usize { pub fn usize_abs(x: usize, y: usize) -> usize {
if x >= y {x - y} else {y - x} if x >= y {
x - y
} else {
y - x
}
} }
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
@ -111,5 +120,4 @@ mod tests {
assert_eq!(rect1.width(), 40); assert_eq!(rect1.width(), 40);
assert_eq!(rect1.height(), 30); assert_eq!(rect1.height(), 30);
} }
} }

View File

@ -33,15 +33,14 @@ pub mod geometry;
pub mod map; pub mod map;
pub mod metric; pub mod metric;
pub use map::{BuilderData, Map, NoData, Symmetry, Tile};
pub use filter::*; pub use filter::*;
pub use map::{BuilderData, Map, NoData, Symmetry, Tile};
pub (crate) mod dijkstra; pub(crate) mod dijkstra;
pub (crate) mod random; pub(crate) mod random;
use std::time::{SystemTime, UNIX_EPOCH};
use rand::prelude::*; use rand::prelude::*;
use std::time::{SystemTime, UNIX_EPOCH};
/// 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.
@ -66,14 +65,16 @@ impl<D: BuilderData> MapBuilder<D> {
} }
} }
pub fn with(&mut self, modifier : Box<dyn MapFilter<D>>) -> &mut MapBuilder<D> { 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<D> { 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)
} }
@ -89,7 +90,6 @@ impl<D: BuilderData> MapBuilder<D> {
map map
} }
} }
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
@ -101,9 +101,7 @@ mod tests {
use super::*; use super::*;
use filter::{ use filter::{
CellularAutomata, CellularAutomata, NoiseGenerator, {AreaStartingPosition, XStart, YStart},
NoiseGenerator,
{AreaStartingPosition, XStart, YStart},
}; };
#[test] #[test]
@ -118,5 +116,4 @@ mod tests {
assert_eq!(map.height, 50); assert_eq!(map.height, 50);
assert_eq!(map.starting_point.is_some(), true); assert_eq!(map.starting_point.is_some(), true);
} }
} }

View File

@ -7,9 +7,8 @@
//! specific game. //! specific game.
//! //!
use super::geometry::{usize_abs, Point, Rect};
use std::fmt; use std::fmt;
use super::geometry::{Point, Rect, usize_abs};
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
pub struct Tile { pub struct Tile {
@ -18,8 +17,12 @@ pub struct Tile {
} }
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone)]
pub enum Symmetry { None, Horizontal, Vertical, Both } pub enum Symmetry {
None,
Horizontal,
Vertical,
Both,
}
/// Arbitrary data associated with each map /// Arbitrary data associated with each map
pub trait BuilderData: Clone + Default {} pub trait BuilderData: Clone + Default {}
@ -33,9 +36,9 @@ impl BuilderData for NoData {}
/// Map data /// Map data
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct Map<D> { pub struct Map<D> {
pub tiles : Vec<Tile>, pub tiles: Vec<Tile>,
pub width : usize, pub width: usize,
pub height : usize, pub height: usize,
pub starting_point: Option<Point>, pub starting_point: Option<Point>,
pub exit_point: Option<Point>, pub exit_point: Option<Point>,
pub rooms: Vec<Rect>, pub rooms: Vec<Rect>,
@ -45,7 +48,7 @@ pub struct Map<D> {
impl Tile { impl Tile {
pub fn new(is_blocked: bool, index: usize) -> Tile { pub fn new(is_blocked: bool, index: usize) -> Tile {
Tile { is_blocked, index} Tile { is_blocked, index }
} }
pub fn wall() -> Tile { pub fn wall() -> Tile {
@ -70,29 +73,34 @@ impl Tile {
} }
impl<D: BuilderData> Map<D> { 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<D> { 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],
width, width,
height, height,
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() data: Default::default(),
} }
} }
/// Create map from given string /// Create map from given string
pub fn from_string(map_string: &str) -> Map<D> { 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)
.collect(); .collect();
let cols = lines.iter().map(|l| l.len()).max().get_or_insert(1).to_owned(); let cols = lines
.iter()
.map(|l| l.len())
.max()
.get_or_insert(1)
.to_owned();
let rows = lines.len(); let rows = lines.len();
let mut map = Map::new(cols, rows); let mut map = Map::new(cols, rows);
@ -122,22 +130,38 @@ impl<D: BuilderData> Map<D> {
let mut exits = Vec::new(); let mut exits = Vec::new();
// Cardinal directions // Cardinal directions
if x > 0 && self.is_exit_valid(x-1, y) { exits.push((x-1, y, 1.0)) }; if x > 0 && self.is_exit_valid(x - 1, y) {
if self.is_exit_valid(x+1, y) { exits.push((x+1, y, 1.0)) }; exits.push((x - 1, y, 1.0))
if y > 0 && self.is_exit_valid(x, y-1) { exits.push((x, y-1, 1.0)) }; };
if self.is_exit_valid(x, y+1) { exits.push((x, y+1, 1.0)) }; if self.is_exit_valid(x + 1, y) {
exits.push((x + 1, y, 1.0))
};
if y > 0 && self.is_exit_valid(x, y - 1) {
exits.push((x, y - 1, 1.0))
};
if self.is_exit_valid(x, y + 1) {
exits.push((x, y + 1, 1.0))
};
// Diagonals // Diagonals
if x > 0 && y > 0 && self.is_exit_valid(x-1, y-1) { exits.push((x-1, y-1, 1.45)); } if x > 0 && y > 0 && self.is_exit_valid(x - 1, y - 1) {
if y > 0 && self.is_exit_valid(x+1, y-1) { exits.push((x+1, y-1, 1.45)); } exits.push((x - 1, y - 1, 1.45));
if x > 0 && self.is_exit_valid(x-1, y+1) { exits.push((x-1, y+1, 1.45)); } }
if self.is_exit_valid(x+1, y+1) { exits.push((x+1, y+1, 1.45)); } if y > 0 && self.is_exit_valid(x + 1, y - 1) {
exits.push((x + 1, y - 1, 1.45));
}
if x > 0 && self.is_exit_valid(x - 1, y + 1) {
exits.push((x - 1, y + 1, 1.45));
}
if self.is_exit_valid(x + 1, y + 1) {
exits.push((x + 1, y + 1, 1.45));
}
exits exits
} }
// Check if given tile can be accessed // Check if given tile can be accessed
fn is_exit_valid(&self, x:usize, y:usize) -> bool { fn is_exit_valid(&self, x: usize, y: usize) -> bool {
self.at(x, y).is_blocked == false self.at(x, y).is_blocked == false
} }
@ -164,7 +188,7 @@ impl<D: BuilderData> Map<D> {
self.rooms.push(rect); self.rooms.push(rect);
} }
pub fn add_corridor(&mut self, from: Point, to:Point) { pub fn add_corridor(&mut self, from: Point, to: Point) {
let mut corridor = Vec::new(); let mut corridor = Vec::new();
let mut x = from.x; let mut x = from.x;
let mut y = from.y; let mut y = from.y;
@ -234,9 +258,13 @@ impl<D: BuilderData> Map<D> {
} }
_ => { _ => {
let half_brush_size = brush_size / 2; let half_brush_size = brush_size / 2;
for brush_y in y-half_brush_size .. y+half_brush_size { for brush_y in y - half_brush_size..y + half_brush_size {
for brush_x in x-half_brush_size .. x+half_brush_size { for brush_x in x - half_brush_size..x + half_brush_size {
if brush_x > 1 && brush_x < self.width-1 && brush_y > 1 && brush_y < self.height-1 { if brush_x > 1
&& brush_x < self.width - 1
&& brush_y > 1
&& brush_y < self.height - 1
{
self.set_tile(brush_x, brush_y, Tile::floor()); self.set_tile(brush_x, brush_y, Tile::floor());
} }
} }
@ -250,7 +278,7 @@ 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)
.map(|x| if self.at(x, y).is_blocked {'#'} else {' '} as u8) .map(|x| if self.at(x, y).is_blocked { '#' } else { ' ' } as u8)
.collect(); .collect();
let line = String::from_utf8(bytes).expect("Can't convert map to string"); let line = String::from_utf8(bytes).expect("Can't convert map to string");
let _ = write!(f, "{}\n", line); let _ = write!(f, "{}\n", line);
@ -347,7 +375,6 @@ mod tests {
assert_eq!(map.tiles, expected_map.tiles); assert_eq!(map.tiles, expected_map.tiles);
} }
#[test] #[test]
fn test_available_exists() { fn test_available_exists() {
let map_str = " let map_str = "

View File

@ -4,28 +4,24 @@
//! 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::dijkstra::DijkstraMap; use super::dijkstra::DijkstraMap;
use super::map::Map;
use super::BuilderData;
/// 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<D>(map: &Map<D>) -> 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()).count();
.filter(|&t| t.is_walkable())
.count();
floor_count as f32 / map.tiles.len() as f32 floor_count as f32 / map.tiles.len() as 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<D: BuilderData>(map: &Map<D>) -> 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;
} }
match map.exit_point { match map.exit_point {
@ -33,11 +29,10 @@ pub fn path_length<D: BuilderData>(map: &Map<D>) -> f32 {
Some(exit) => { Some(exit) => {
let dijkstra = DijkstraMap::new(map); let dijkstra = DijkstraMap::new(map);
dijkstra.tiles[map.xy_idx(exit.x, exit.y)] dijkstra.tiles[map.xy_idx(exit.x, exit.y)]
}, }
} }
} }
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
/// Module unit tests /// Module unit tests
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
@ -46,7 +41,6 @@ mod tests {
use super::*; use super::*;
use crate::{geometry::Point, map::NoData}; use crate::{geometry::Point, map::NoData};
#[test] #[test]
fn test_density_no_floor() { fn test_density_no_floor() {
let map = Map::<NoData>::new(10, 10); let map = Map::<NoData>::new(10, 10);
@ -87,8 +81,8 @@ mod tests {
########## ##########
"; ";
let mut map = Map::<NoData>::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));
let score = path_length(&map); let score = path_length(&map);
assert!(f32::abs(score - 7.9) <= 0.01); assert!(f32::abs(score - 7.9) <= 0.01);

View File

@ -11,7 +11,7 @@ pub trait Rng {
impl Rng for StdRng { impl Rng for StdRng {
fn roll_dice(&mut self, min: usize, max: usize) -> usize { fn roll_dice(&mut self, min: usize, max: usize) -> usize {
self.random_range(min, max+1) self.random_range(min, max + 1)
} }
fn random_range(&mut self, start: usize, end: usize) -> usize { fn random_range(&mut self, start: usize, end: usize) -> usize {
@ -29,8 +29,8 @@ impl Rng for StdRng {
/// ------------------------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------------------------
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rand::prelude::*;
use super::Rng; use super::Rng;
use rand::prelude::*;
#[test] #[test]
fn test_range() { fn test_range() {
@ -64,5 +64,4 @@ mod tests {
assert_eq!(min, 1); assert_eq!(min, 1);
assert_eq!(max, 7); assert_eq!(max, 7);
} }
} }