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

View File

@ -1,12 +1,13 @@
use std::time::{SystemTime, UNIX_EPOCH};
use rand::prelude::*;
use mapgen::*;
use rand::prelude::*;
use std::time::{SystemTime, UNIX_EPOCH};
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 gen = BspRooms::<NoData>::new();
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
println!("{:}", &map);
}
}

View File

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

View File

@ -1,10 +1,10 @@
//! Calculate Dijkstra influence map
//!
//!
//! http://www.roguebasin.com/index.php?title=The_Incredible_Power_of_Dijkstra_Maps
//!
//!
//! This algorithm calculates cost (distance) of moving from the given starting point
//! to the each point on the map. Point which are not reachable will get f32::MAX value.
//!
//!
//! Example generator usage:
//! ---
//! use rand::prelude::*;
@ -14,21 +14,20 @@
//! map::{Map, TileType},
//! starting_point::{AreaStartingPosition, XStart, YStart}
//! };
//!
//!
//! let mut rng = StdRng::seed_from_u64(100);
//! let mut map = Map::new(80, 50);
//! map.set_tile(10, 10, TileType::Floor);
//! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP);
//! let new_map = modifier.modify_map(&mut rng, &map);
//!
//!
//! assert_eq!(new_map.starting_point, Some(Point::new(10, 10)));
//! ---
//!
//!
use std::{collections::VecDeque, marker::PhantomData};
use std::f32::MAX;
use super::map::{BuilderData, Map};
use std::f32::MAX;
use std::{collections::VecDeque, marker::PhantomData};
/// Representation of a Dijkstra flow map.
/// map is a vector of floats, having a size equal to size_x * size_y (one per tile).
@ -43,9 +42,9 @@ pub struct DijkstraMap<D> {
}
impl<D: BuilderData> DijkstraMap<D> {
//! Construct a new Dijkstra map, ready to run.
//! Construct a new Dijkstra map, ready to run.
pub fn new(map: &Map<D>) -> DijkstraMap<D> {
let len = map.width * map.height;
let len = map.width * map.height;
let tiles = vec![MAX; len];
let mut d = DijkstraMap {
tiles: tiles,
@ -79,8 +78,12 @@ impl<D: BuilderData> DijkstraMap<D> {
let idx = self.xy_idx(x, y);
let new_depth = depth + add_depth;
let prev_depth = self.tiles[idx];
if new_depth >= prev_depth { continue; }
if new_depth >= self.max_depth { continue; }
if new_depth >= prev_depth {
continue;
}
if new_depth >= self.max_depth {
continue;
}
self.tiles[idx] = 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 {
(y * self.size_x ) + x
(y * self.size_x) + x
}
}
@ -112,13 +115,19 @@ mod tests {
map.starting_point = Some(Point::new(8, 1));
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_y, 3);
for i in 0..10 {
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;
if i < 3 || i == 9 {
assert_eq!(dm.tiles[idx], MAX);
@ -139,10 +148,9 @@ mod tests {
let mut map = Map::<NoData>::from_string(map_str);
map.starting_point = Some(Point::new(2, 2));
let dm = DijkstraMap::new(&map);
let expected = [MAX, MAX, MAX, MAX,
MAX, 1.45, 1.0, MAX,
MAX, 1.0, 0.0, MAX,
MAX, MAX, MAX, MAX];
let expected = [
MAX, MAX, MAX, MAX, MAX, 1.45, 1.0, MAX, MAX, 1.0, 0.0, MAX, MAX, MAX, MAX, MAX,
];
assert_eq!(dm.tiles, expected);
}
@ -158,13 +166,14 @@ mod tests {
let mut map = Map::<NoData>::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,
MAX, 7.45, 6.45, 5.45, 4.45, 3.45, 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];
let expected = [
MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, MAX, 7.45, 6.45, 5.45, 4.45, 3.45,
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,
];
for (v, e) in dm.tiles.iter().zip(expected.iter()) {
assert!(f32::abs(v - e) <= 0.01);
}
}
}
}

View File

@ -1,8 +1,8 @@
//! Random rooms map generator.
//!
//! Try to generate rooms of different size to fill the map area.
//!
//! Try to generate rooms of different size to fill the map area.
//! Rooms will not overlap.
//!
//!
//! Example generator usage:
//! ```
//! use rand::prelude::*;
@ -10,24 +10,23 @@
//! use mapgen::filter::{
//! BspInterior
//! };
//!
//!
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = BspInterior::<NoData>::new();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//!
//!
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//!
//!
use std::marker::PhantomData;
use rand::prelude::*;
use crate::{BuilderData, MapFilter};
use crate::geometry::{Point, Rect};
use crate::random::Rng;
use crate::Map;
use crate::{BuilderData, MapFilter};
use rand::prelude::*;
pub struct BspInterior<D: BuilderData> {
min_room_size: usize,
@ -41,9 +40,8 @@ impl<D: BuilderData> MapFilter<D> for BspInterior<D> {
}
impl<D: BuilderData> BspInterior<D> {
pub fn new() -> Box<BspInterior<D>> {
Box::new(BspInterior{
Box::new(BspInterior {
min_room_size: 8,
phantom: PhantomData,
})
@ -52,10 +50,10 @@ impl<D: BuilderData> BspInterior<D> {
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone();
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];
// Divide the first room
self.add_subrects(first_room, rng, &mut rects);
self.add_subrects(first_room, rng, &mut rects);
let rooms_copy = rects.clone();
for r in rooms_copy.iter() {
@ -64,9 +62,9 @@ impl<D: BuilderData> BspInterior<D> {
}
// 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 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_y = rng.random_range(room.y1, room.y2);
let end_x = rng.random_range(next_room.x1, next_room.x2);
@ -74,7 +72,7 @@ impl<D: BuilderData> BspInterior<D> {
new_map.add_corridor(Point::new(start_x, start_y), Point::new(end_x, end_y));
}
new_map
new_map
}
fn add_subrects(&self, rect: Rect, rng: &mut StdRng, rects: &mut Vec<Rect>) {
@ -84,7 +82,7 @@ impl<D: BuilderData> BspInterior<D> {
}
// Calculate boundaries
let width = rect.x2 - rect.x1;
let width = rect.x2 - rect.x1;
let height = rect.y2 - rect.y1;
let half_width = width / 2;
let half_height = height / 2;
@ -93,46 +91,52 @@ impl<D: BuilderData> BspInterior<D> {
if split <= 2 {
// Horizontal split
let h1 = Rect::new( rect.x1, rect.y1, half_width-1, height );
rects.push( h1 );
if half_width > self.min_room_size { self.add_subrects(h1, 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); }
let h1 = Rect::new(rect.x1, rect.y1, half_width - 1, height);
rects.push(h1);
if half_width > self.min_room_size {
self.add_subrects(h1, 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 {
// 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);
if half_height > self.min_room_size { self.add_subrects(v1, rng, rects); }
let v2 = Rect::new( rect.x1, rect.y1 + half_height, width, half_height );
if half_height > self.min_room_size {
self.add_subrects(v1, rng, rects);
}
let v2 = Rect::new(rect.x1, rect.y1 + half_height, width, half_height);
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
/// ------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
use crate::{Map, map::NoData};
use crate::{map::NoData, Map};
#[test]
fn no_corridors_on_borders() {
let mut rng = StdRng::seed_from_u64(907647352);
let mut rng = StdRng::seed_from_u64(907647352);
let gen = BspInterior::<NoData>::new();
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
for i in 0..80 {
assert!(map.at(i, 0).is_blocked());
assert!(map.at(i, 49).is_blocked());
}
}
for j in 0..50 {
assert!(map.at(0, j).is_blocked());
assert!(map.at(79, j).is_blocked());
}
}
}
}
}

View File

@ -1,32 +1,31 @@
//! Random rooms map generator.
//!
//! Try to generate rooms of different size to fill the map area.
//!
//! Try to generate rooms of different size to fill the map area.
//! Rooms will not overlap.
//!
//!
//! Example generator usage:
//! ```
//! use rand::prelude::*;
//! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::BspRooms;
//!
//!
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = BspRooms::<NoData>::new();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//!
//!
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//!
//!
use std::marker::PhantomData;
use rand::prelude::*;
use crate::BuilderData;
use crate::MapFilter;
use crate::geometry::Rect;
use crate::random::Rng;
use crate::BuilderData;
use crate::Map;
use crate::MapFilter;
use rand::prelude::*;
pub struct BspRooms<D: BuilderData> {
max_split: usize,
@ -34,7 +33,7 @@ pub struct BspRooms<D: BuilderData> {
}
impl<D: BuilderData> MapFilter<D> for BspRooms<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_rooms(map, rng)
}
}
@ -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 rects: Vec<Rect> = Vec::new();
// 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];
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_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_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.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(
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
}
fn get_random_rect(&self, rng : &mut StdRng, rects: &Vec<Rect>) -> Rect {
if rects.len() == 1 { return rects[0]; }
fn get_random_rect(&self, rng: &mut StdRng, rects: &Vec<Rect>) -> Rect {
if rects.len() == 1 {
return rects[0];
}
let idx = rng.random_range(0, rects.len());
rects[idx]
}
@ -117,15 +133,25 @@ impl<D: BuilderData> BspRooms<D> {
let mut can_build = true;
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 x in expanded.x1 ..= expanded.x2 {
if x > map.width - 2 { can_build = false; }
if y > map.height - 2 { can_build = false; }
if x < 1 { can_build = false; }
if y < 1 { can_build = false; }
for y in expanded.y1..=expanded.y2 {
for x in expanded.x1..=expanded.x2 {
if x > map.width - 2 {
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 map.at(x as usize, y as usize).is_walkable() {
can_build = false;
@ -148,17 +174,16 @@ mod tests {
#[test]
fn no_corridors_on_borders() {
let mut rng = StdRng::seed_from_u64(907647352);
let mut rng = StdRng::seed_from_u64(907647352);
let gen = BspRooms::<NoData>::new();
let map = gen.modify_map(&mut rng, &Map::new(80, 50));
for i in 0..80 {
assert!(map.at(i, 0).is_blocked());
assert!(map.at(i, 49).is_blocked());
}
}
for j in 0..50 {
assert!(map.at(0, j).is_blocked());
assert!(map.at(79, j).is_blocked());
}
}
}
}
}

View File

@ -1,33 +1,32 @@
//! Cellular automata map filter.
//!
//!
//! Check this [article](http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels)
//! for more information about the algorithm behind this generator.
//!
//!
//! This algorithm requires that map first is filtered with some noise.
//! For example `UniformNoise`. It can also be apply to any other non empty map.
//!
//!
//! Example usage:
//! ```
//! use rand::prelude::*;
//! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::CellularAutomata;
//!
//!
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = CellularAutomata::<NoData>::new();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//!
//!
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//!
//!
use std::marker::PhantomData;
use rand::prelude::*;
use crate::BuilderData;
use crate::MapFilter;
use crate::{Map, Tile};
use rand::prelude::*;
/// Map filter
pub struct CellularAutomata<D: BuilderData> {
@ -36,7 +35,7 @@ pub struct CellularAutomata<D: BuilderData> {
}
impl<D: BuilderData> MapFilter<D> for CellularAutomata<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(map)
}
}
@ -59,26 +58,31 @@ impl<D: BuilderData> CellularAutomata<D> {
new_map
}
}
fn apply_iteration<D: BuilderData>(map: &Map<D>) -> Map<D> {
let mut new_map = map.clone();
for y in 1..map.height-1 {
for x in 1..map.width-1 {
for y in 1..map.height - 1 {
for x in 1..map.width - 1 {
let idxs = [
(x-1, y-1), (x, y-1), (x+1, y-1),
(x-1, y), (x+1, y),
(x-1, y+1), (x, y+1), (x+1, y+1)];
let neighbors = idxs.iter()
(x - 1, y - 1),
(x, y - 1),
(x + 1, y - 1),
(x - 1, y),
(x + 1, y),
(x - 1, y + 1),
(x, y + 1),
(x + 1, y + 1),
];
let neighbors = idxs
.iter()
.filter(|(x, y)| map.at(*x, *y).is_blocked())
.count();
if neighbors > 4 || neighbors == 0 {
new_map.set_tile(x, y, Tile::wall())
}
else {
} else {
new_map.set_tile(x, y, Tile::floor());
}
}
@ -103,7 +107,6 @@ mod tests {
assert!(new_map.at(1, 1).is_blocked());
}
#[test]
fn test_iteration_floor() {
let mut map = Map::<NoData>::new(3, 3);
@ -115,5 +118,4 @@ mod tests {
let new_map = apply_iteration(&map);
assert!(new_map.at(1, 1).is_walkable());
}
}
}

View File

@ -1,16 +1,15 @@
//! Remove unreachable areas from the map.
//!
//!
//! This modifier reiquires starting position on the map.
//! 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::dijkstra::DijkstraMap;
use crate::MapFilter;
use crate::{BuilderData, Map, Tile};
use crate::dijkstra::DijkstraMap;
use rand::prelude::StdRng;
/// Remove unreachable areas from the map.
pub struct CullUnreachable<D: BuilderData> {
@ -18,7 +17,7 @@ pub struct CullUnreachable<D: BuilderData> {
}
impl<D: BuilderData> MapFilter<D> for CullUnreachable<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(map)
}
}
@ -53,11 +52,11 @@ impl<D: BuilderData> CullUnreachable<D> {
/// ------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use rand::prelude::*;
use super::*;
use super::MapFilter;
use super::*;
use crate::geometry::Point;
use crate::map::{Map, NoData};
use rand::prelude::*;
#[test]
fn test_culling() {
@ -75,11 +74,10 @@ mod tests {
";
let expected_map = Map::<NoData>::from_string(expected_map_str);
let modifier = CullUnreachable::new();
let mut rng = StdRng::seed_from_u64(0);
let new_map = modifier.modify_map(&mut rng, &map);
assert_eq!(new_map.tiles, expected_map.tiles);
}
}
}

View File

@ -1,18 +1,17 @@
//! Add exit point to the map
//!
//!
//! This modifier will try to add exit point as far as possible from the starting point.
//! 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::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.
pub struct DistantExit<D: BuilderData> {
@ -20,7 +19,7 @@ pub struct DistantExit<D: BuilderData> {
}
impl<D: BuilderData> MapFilter<D> for DistantExit<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
self.build(map)
}
}
@ -43,7 +42,7 @@ impl<D: BuilderData> DistantExit<D> {
if value < f32::MAX && value > best_value {
best_value = value;
best_idx = i;
}
}
}
let x = best_idx % map.width;
let y = best_idx / map.width;
@ -57,11 +56,11 @@ impl<D: BuilderData> DistantExit<D> {
/// ------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use rand::prelude::*;
use super::*;
use super::MapFilter;
use super::*;
use crate::geometry::Point;
use crate::map::{Map, NoData};
use rand::prelude::*;
#[test]
fn test_exit() {
@ -80,4 +79,4 @@ mod tests {
assert_eq!(new_map.exit_point, Some(Point::new(1, 2)));
}
}
}

View File

@ -3,34 +3,36 @@
//! use rand::prelude::*;
//! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::DrunkardsWalk;
//!
//!
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = DrunkardsWalk::<NoData>::open_area();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//!
//!
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//!
//!
use std::marker::PhantomData;
use rand::prelude::*;
use crate::MapFilter;
use crate::{
BuilderData,
map::{Map, Symmetry, Tile},
geometry::Point,
random::Rng
map::{Map, Symmetry, Tile},
random::Rng,
BuilderData,
};
use rand::prelude::*;
#[derive(PartialEq, Copy, Clone)]
pub enum DrunkSpawnMode { StartingPoint, Random }
pub enum DrunkSpawnMode {
StartingPoint,
Random,
}
pub struct DrunkardsWalk<D: BuilderData> {
spawn_mode : DrunkSpawnMode,
drunken_lifetime : i32,
spawn_mode: DrunkSpawnMode,
drunken_lifetime: i32,
floor_percent: f32,
brush_size: usize,
symmetry: Symmetry,
@ -38,19 +40,20 @@ pub struct DrunkardsWalk<D: BuilderData> {
}
impl<D: BuilderData> MapFilter<D> for DrunkardsWalk<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)
}
}
impl<D: BuilderData> DrunkardsWalk<D> {
pub fn new( spawn_mode: DrunkSpawnMode,
drunken_lifetime: i32,
floor_percent: f32,
brush_size: usize,
symmetry: Symmetry) -> Box<DrunkardsWalk<D>>
{
Box::new(DrunkardsWalk{
pub fn new(
spawn_mode: DrunkSpawnMode,
drunken_lifetime: i32,
floor_percent: f32,
brush_size: usize,
symmetry: Symmetry,
) -> Box<DrunkardsWalk<D>> {
Box::new(DrunkardsWalk {
spawn_mode,
drunken_lifetime,
floor_percent,
@ -79,18 +82,18 @@ impl<D: BuilderData> DrunkardsWalk<D> {
pub fn fearful_symmetry() -> Box<DrunkardsWalk<D>> {
Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
}
fn build(&self, rng: &mut StdRng, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone();
// 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());
let total_tiles = new_map.width * new_map.height;
let desired_floor_tiles = (self.floor_percent * total_tiles as f32) as usize;
let mut floor_tile_count = new_map.tiles.iter().filter(|a| a.is_walkable()).count();
let mut digger_count = 0;
while floor_tile_count < desired_floor_tiles {
while floor_tile_count < desired_floor_tiles {
let mut drunk_x;
let mut drunk_y;
match self.spawn_mode {
@ -111,15 +114,31 @@ impl<D: BuilderData> DrunkardsWalk<D> {
let mut drunk_life = self.drunken_lifetime;
while drunk_life > 0 {
new_map.set_tile(drunk_x, drunk_y, Tile::wall());
new_map.set_tile(drunk_x, drunk_y, Tile::wall());
new_map.paint(self.symmetry, self.brush_size, drunk_x, drunk_y);
let stagger_direction = rng.roll_dice(1, 4);
match stagger_direction {
1 => { if drunk_x > 1 { drunk_x -= 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; } }
1 => {
if drunk_x > 1 {
drunk_x -= 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;
@ -131,4 +150,4 @@ impl<D: BuilderData> DrunkardsWalk<D> {
new_map
}
}
}

View File

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

@ -1,5 +1,5 @@
//! Generators for dungeon type maps.
//!
//!
pub mod bsp_interior;
pub mod bsp_rooms;
@ -9,8 +9,8 @@ pub mod distant_exit;
pub mod drunkard;
pub mod maze;
pub mod noise_generator;
pub mod simple_rooms;
pub mod rooms_corridors_nearest;
pub mod simple_rooms;
pub mod starting_point;
pub mod voronoi;
@ -22,8 +22,7 @@ pub use distant_exit::DistantExit;
pub use drunkard::DrunkardsWalk;
pub use maze::MazeBuilder;
pub use noise_generator::NoiseGenerator;
pub use simple_rooms::SimpleRooms;
pub use rooms_corridors_nearest::NearestCorridors;
pub use simple_rooms::SimpleRooms;
pub use starting_point::{AreaStartingPosition, XStart, YStart};
pub use voronoi::VoronoiHive;

View File

@ -1,27 +1,26 @@
//! Apply noise to the map.
//! Each cell will be set to Floor with the given probabilty.
//!
//!
//! Example usage:
//! ```
//! use rand::prelude::*;
//! use mapgen::{Map, MapFilter, NoData};
//! use mapgen::filter::NoiseGenerator;
//!
//!
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = NoiseGenerator::<NoData>::uniform();
//! let map = gen.modify_map(&mut rng, &Map::new(80, 50));
//!
//!
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//!
//!
use std::marker::PhantomData;
use rand::prelude::*;
use crate::MapFilter;
use crate::{BuilderData, Map, Tile};
use rand::prelude::*;
/// Map noise generator
pub struct NoiseGenerator<D: BuilderData> {
@ -30,7 +29,7 @@ pub struct NoiseGenerator<D: BuilderData> {
}
impl<D: BuilderData> MapFilter<D> for NoiseGenerator<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(map, rng)
}
}
@ -56,15 +55,17 @@ impl<D: BuilderData> NoiseGenerator<D> {
fn build(&self, map: &Map<D>, rng: &mut StdRng) -> Map<D> {
let mut new_map = map.clone();
let p = (self.prob * 100.0) as u32;
for y in 1..new_map.height-1 {
for x in 1..new_map.width-1 {
for y in 1..new_map.height - 1 {
for x in 1..new_map.width - 1 {
let roll = rng.next_u32() % 100;
if roll > p { new_map.set_tile(x, y, Tile::floor()) }
else { new_map.set_tile(x, y, Tile::wall()) }
if roll > p {
new_map.set_tile(x, y, Tile::floor())
} else {
new_map.set_tile(x, y, Tile::wall())
}
}
}
new_map
}
}

View File

@ -1,25 +1,23 @@
//! Connect nearest rooms on the map with corridors
//!
use rand::prelude::StdRng;
//!
use crate::BuilderData;
use crate::MapFilter;
use crate::Map;
use crate::MapFilter;
use rand::prelude::StdRng;
use std::collections::HashSet;
use std::marker::PhantomData;
pub struct NearestCorridors<D: BuilderData> {
phantom: PhantomData<D>,
}
impl<D: BuilderData> MapFilter<D> for NearestCorridors<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
fn modify_map(&self, _: &mut StdRng, map: &Map<D>) -> Map<D> {
self.corridors(map)
}
}
impl<D: BuilderData> NearestCorridors<D> {
pub fn new() -> Box<NearestCorridors<D>> {
Box::new(NearestCorridors {
phantom: PhantomData,
@ -29,11 +27,11 @@ impl<D: BuilderData> NearestCorridors<D> {
fn corridors(&self, map: &Map<D>) -> Map<D> {
let mut new_map = map.clone();
let mut connected : HashSet<usize> = HashSet::new();
for (i,room) in map.rooms.iter().enumerate() {
let mut room_distance : Vec<(usize, f32)> = Vec::new();
let mut connected: HashSet<usize> = HashSet::new();
for (i, room) in map.rooms.iter().enumerate() {
let mut room_distance: Vec<(usize, f32)> = Vec::new();
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) {