From 2dd9e5c7db6b9a5141038ce0e54c0f5a2f229a22 Mon Sep 17 00:00:00 2001 From: Chubbygummibear Date: Mon, 2 Oct 2023 13:44:01 -0700 Subject: [PATCH 1/7] procgen stuff --- Cargo.toml | 4 + dmsrc/binary-space-partition.dm | 24 ++ dmsrc/random-room-placement.dm | 24 ++ src/binary_space_partition.rs | 449 ++++++++++++++++++++++++++++++++ src/lib.rs | 4 + src/random_room_placement.rs | 288 ++++++++++++++++++++ 6 files changed, 793 insertions(+) create mode 100644 dmsrc/binary-space-partition.dm create mode 100644 dmsrc/random-room-placement.dm create mode 100644 src/binary_space_partition.rs create mode 100644 src/random_room_placement.rs diff --git a/Cargo.toml b/Cargo.toml index d21f14bf..c4b7279f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ concat-string = { version = "1.0.1", optional = true } [features] default = [ "acreplace", + "binary_space_partition", "cellularnoise", "dmi", "file", @@ -69,6 +70,7 @@ default = [ "json", "log", "noise", + "random_room_placement", "sql", "time", "toml", @@ -77,6 +79,7 @@ default = [ # default features acreplace = ["aho-corasick"] +binary_space_partition = ["rand", "rayon", "serde", "serde_json", "sha2"] cellularnoise = ["rand", "rayon"] dmi = ["png", "image"] file = [] @@ -84,6 +87,7 @@ git = ["git2", "chrono"] http = ["reqwest", "serde", "serde_json", "once_cell", "jobs"] json = ["serde", "serde_json"] log = ["chrono", "flume"] +random_room_placement = ["rand", "rayon", "serde", "serde_json", "sha2"] sql = ["mysql", "serde", "serde_json", "once_cell", "dashmap", "jobs"] time = [] toml = ["serde", "serde_json", "toml-dep"] diff --git a/dmsrc/binary-space-partition.dm b/dmsrc/binary-space-partition.dm new file mode 100644 index 00000000..30ba68c3 --- /dev/null +++ b/dmsrc/binary-space-partition.dm @@ -0,0 +1,24 @@ +/** + * This proc generates rooms in a specified area of random size and placement. Essential for procedurally generated areas, BSP works by cutting a given area in half, + * then cutting one of those subsections in half, and repeating this process until a minimum size is reached, then backtracking to other subsections that are not of + * the minimum size yet. These cuts are offset by small random amounts so that the sections are all varied in size and shape. + * + * BSP excels at creating rooms or areas with a relatively even distribution over an area, so there won't be too much blank open area. However if you discard rooms that + * overlap pre-existing map structures or areas, you may still get blank areas where nothing interesting appears. + * + * Return: + * * a json list of room data to be processed by json_decode in byond and further processed there. + * + * Arguments: + * * width: the width of the area to generate in + * * height: the height of the area to generate in + * * hash: the rng seed the generator will use for this instance + * * map_subsection_min_size: The minimum size of a map subsection. When using this for rooms with walls, the minimum possible square will be a 5x5 room. Barring walls, + * this will be a 3x3 room. The maximum size will be 9x9, because a further cut could reduce this size beneath the minimum size. + * * map_subsection_min_room_width: The minimum room width once the subsections are finalized. Room width and height are random between this amount, and the subsection + * max size + * * map_subsection_min_room_height: The minimum room height once the subsections are finalized. Room width and height are random between this amount, and the subsection + * max size + */ +#define rustg_bsp_generate(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height) \ + RUSTG_CALL(RUST_G, "bsp_generate")(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height) diff --git a/dmsrc/random-room-placement.dm b/dmsrc/random-room-placement.dm new file mode 100644 index 00000000..48e6ceb2 --- /dev/null +++ b/dmsrc/random-room-placement.dm @@ -0,0 +1,24 @@ +/** + * This proc generates rooms in a specified area of random size and placement. Used in procedural generation, but far less intensively than Binary Space Partitioning + * due to Random Room Placement being far more simple and unreliable for area coverage. These rooms will not overlap one another, but that is the only logic + * they do. The room dimensions returned by this call are hardcoded to be the dimensions of maint ruins so that I could sprinkle pre-generated areas over + * the binary space rooms that are random. + * These dimensions are: + * * 3x3 + * * 3x5 + * * 5x3 + * * 5x4 + * * 10x5 + * * 10x10 + * + * Return: + * * a json list of room data to be processed by json_decode in byond and further processed there. + * + * Arguments: + * * width: the width of the area to generate in + * * height: the height of the area to generate in + * * desired_room_count: the number of rooms you want generated and returned + * * hash: the rng seed the generator will use for this instance + */ +#define rustg_random_room_generate(width, height, desired_room_count, hash) \ + RUSTG_CALL(RUST_G, "random_room_generate")(width, height, desired_room_count, hash) diff --git a/src/binary_space_partition.rs b/src/binary_space_partition.rs new file mode 100644 index 00000000..f1ba9cc2 --- /dev/null +++ b/src/binary_space_partition.rs @@ -0,0 +1,449 @@ +use std::{fmt, cmp}; +use std::fmt::Write; +use rand::distributions::WeightedIndex; +use rand::prelude::*; +use rand::rngs::StdRng; +use rand::Rng; +use serde::Serialize; +use crate::error::Result; + +struct BspLevel { + level: Level, + map_subsection_min_size: i32, + map_subsection_min_room_width: i32, + map_subsection_min_room_height: i32, +} + + +byond_fn!(fn bsp_generate(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height) { + match bsp_gen(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height) { + Ok(s) => Some(s), + Err(e) => Some(format!("{e}")) + } +}); +fn bsp_gen(width_as_str: &str, + height_as_str: &str, + hash_as_str: &str, + map_subsection_min_size_as_str: &str, + map_subsection_min_room_width_as_str: &str, + map_subsection_min_room_height_as_str: &str) + -> Result{ + let default_hash: u64 = rand::thread_rng().gen(); + let width = width_as_str.parse::()?; + let height = height_as_str.parse::()?; + let mut map_subsection_min_size = map_subsection_min_size_as_str.parse::()?; + let map_subsection_min_room_width = map_subsection_min_room_width_as_str.parse::()?; + let map_subsection_min_room_height = map_subsection_min_room_height_as_str.parse::()?; + if map_subsection_min_size < map_subsection_min_room_width || map_subsection_min_size < map_subsection_min_room_height{ + map_subsection_min_size = cmp::max(map_subsection_min_room_width, map_subsection_min_room_height) + 1 + } + //let seed: &str = Alphanumeric.sample_string(&mut rand::thread_rng(), 32).as_str(); + + let mut rng: StdRng = SeedableRng::seed_from_u64(hash_as_str.parse::()?.try_into().unwrap_or(default_hash)); + + + let level = BspLevel::new(width, height, &mut rng, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height); + + let mut string = String::new(); + for room in &level.all_rooms { + let serialized = serde_json::to_string(&room).unwrap(); + let _ = write!(string, "{}", serialized); + //println!("serialized = {}", serialized); + } + + Ok(format!("{}",serde_json::to_string(&level.all_rooms)?)) +} + +impl BspLevel { + fn new( + width: i32, + height: i32, + rng: &mut StdRng, + map_subsection_min_size: i32, + map_subsection_min_room_width: i32, + map_subsection_min_room_height: i32, + ) -> Level { + let level = Level::new(width, height); + + let mut map = BspLevel { level, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height }; + + map.place_rooms(rng); + + map.level + } + + fn place_rooms(&mut self, rng: &mut StdRng) { + let mut root = Leaf::new(0, 0, self.level.width, self.level.height, self.map_subsection_min_size, self.map_subsection_min_room_width,self.map_subsection_min_room_height); + root.generate(rng); + + root.create_rooms(rng); + + for leaf in root.iter() { + if leaf.is_leaf() { + if let Some(room) = leaf.get_room() { + + self.level.add_room(&room); + } + } + + for corridor in &leaf.corridors { + self.level.add_room(&corridor); + } + } + + } +} + +struct Leaf { + min_size: i32, + x: i32, + y: i32, + width: i32, + height: i32, + min_room_width: i32, + min_room_height: i32, + left_child: Option>, + right_child: Option>, + room: Option, + corridors: Vec, +} + +impl Leaf { + fn new(x: i32, y: i32, width: i32, height: i32, min_size: i32, min_room_width: i32, min_room_height: i32,) -> Self { + Leaf { + min_size, + x, + y, + width, + height, + min_room_width, + min_room_height, + left_child: None, + right_child: None, + room: None, + corridors: vec![], + } + } + + fn is_leaf(&self) -> bool { + match self.left_child { + None => match self.right_child { + None => true, + Some(_) => false, + }, + Some(_) => false, + } + } + + fn generate(&mut self, rng: &mut StdRng) { + if self.is_leaf() { + if self.split(rng) { + self.left_child.as_mut().unwrap().generate(rng); + self.right_child.as_mut().unwrap().generate(rng); + } + } + } + + fn split(&mut self, rng: &mut StdRng) -> bool { + // if width >25% height, split vertically + // if height >25% width, split horz + // otherwise random + + // this is the random choice + let mut split_horz = match rng.gen_range(0..2) { + 0 => false, + _ => true, + }; + + // then override with width/height check + if self.width > self.height && (self.width as f32 / self.height as f32) >= 1.25 { + split_horz = false; + } else if self.height > self.width && (self.height as f32 / self.width as f32) >= 1.25 { + split_horz = true; + } + + let max = match split_horz { + true => self.height - self.min_size, + false => self.width - self.min_size, + }; + + // the current area is small enough, so stop splitting + if max <= self.min_size { + return false; + } + + let split_pos = rng.gen_range(self.min_size..max); + if split_horz { + self.left_child = Some(Box::new(Leaf::new( + self.x, + self.y, + self.width, + split_pos, + self.min_size, + self.min_room_width, + self.min_room_height, + ))); + self.right_child = Some(Box::new(Leaf::new( + self.x, + self.y + split_pos, + self.width, + self.height - split_pos, + self.min_size, + self.min_room_width, + self.min_room_height, + ))); + } else { + self.left_child = Some(Box::new(Leaf::new( + self.x, + self.y, + split_pos, + self.height, + self.min_size, + self.min_room_width, + self.min_room_height, + ))); + self.right_child = Some(Box::new(Leaf::new( + self.x + split_pos, + self.y, + self.width - split_pos, + self.height, + self.min_size, + self.min_room_width, + self.min_room_height, + ))); + } + + true + } + + fn create_rooms(&mut self, rng: &mut StdRng) { + if let Some(ref mut room) = self.left_child { + room.as_mut().create_rooms(rng); + }; + + if let Some(ref mut room) = self.right_child { + room.as_mut().create_rooms(rng); + }; + + // if last level, add a room + if self.is_leaf() { + let width = rng.gen_range(self.min_room_width..=self.width); + let height = rng.gen_range(self.min_room_height..=self.height); + let x = rng.gen_range(0..=self.width - width); + let y = rng.gen_range(0..=self.height - height); + let choices = [0, 4]; + let weights = [1, 4]; + let dist = WeightedIndex::new(&weights).unwrap(); + let mut rng = thread_rng(); + let room_layout = choices[dist.sample(&mut rng)]; + + self.room = Some(Room::new( + format!("extra room"), + x + self.x, + y + self.y, + width, + height, + room_layout, + )); + } + + } + + fn get_room(&self) -> Option { + if self.is_leaf() { + return self.room.clone(); + } + + let mut left_room: Option = None; + let mut right_room: Option = None; + + if let Some(ref room) = self.left_child { + left_room = room.get_room(); + } + + if let Some(ref room) = self.right_child { + right_room = room.get_room(); + } + + match (left_room, right_room) { + (None, None) => None, + (Some(room), _) => Some(room), + (_, Some(room)) => Some(room), + } + } + + fn iter(&self) -> LeafIterator { + LeafIterator::new(&self) + } +} + +struct LeafIterator<'a> { + current_node: Option<&'a Leaf>, + right_nodes: Vec<&'a Leaf>, +} + +impl<'a> LeafIterator<'a> { + fn new(root: &'a Leaf) -> LeafIterator<'a> { + let mut iter = LeafIterator { + right_nodes: vec![], + current_node: None, + }; + + iter.add_subtrees(root); + iter + } + + // set the current node to the one provided + // and add any child leaves to the node vec + fn add_subtrees(&mut self, node: &'a Leaf) { + if let Some(ref left) = node.left_child { + self.right_nodes.push(&*left); + } + if let Some(ref right) = node.right_child { + self.right_nodes.push(&*right); + } + + self.current_node = Some(node); + } +} + +impl<'a> Iterator for LeafIterator<'a> { + type Item = &'a Leaf; + + fn next(&mut self) -> Option { + let result = self.current_node.take(); + if let Some(rest) = self.right_nodes.pop() { + self.add_subtrees(rest); + } + + match result { + Some(leaf) => Some(&*leaf), + None => None, + } + } +} + +pub struct Level { + width: i32, + height: i32, + board: Vec>, + all_rooms: Vec, + increment: i32, + //hash: String, +} + +impl Level { + fn new( + width: i32, + height: i32, + ) -> Self { + let mut new_level = Level { + width, + height, + board: Vec::new(), + all_rooms: Vec::new(), + increment: 0, + }; + new_level.update_board(); + new_level + } + + fn update_board(&mut self) -> Vec> { + let mut new_board = Vec::new(); + self.increment+=1; + for _ in 0..self.height { + let space_tile = 0; + //let wall_tile = 1; + let floor_tile = 5; + let gen_floor_first = true; + + let mut row = vec![floor_tile; self.width as usize]; + if !gen_floor_first { + row = vec![space_tile; self.width as usize]; + } + + new_board.push(row); + } + for room in &self.all_rooms { + for row in 0..room.height { + for col in 0..room.width { + let y = (room.y + row) as usize; + let x = (room.x + col) as usize; + if row == 0 || col == 0 || row == room.height - 1 || col == room.width - 1 { + // might just let byond handle the walls + new_board[y][x] = 1; + } else { + new_board[y][x] = room.room_type; + } + } + } + } + self.board = new_board.clone(); + //draw(self, "increments", &self.increment.to_string()).unwrap(); + new_board + } + + fn add_room(&mut self, room: &Room) { + self.all_rooms.push(room.clone()); + self.update_board(); + + } +} + +impl fmt::Display for Level { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for row in 0..self.height as usize { + for col in 0..self.width as usize { + write!(f, "{}", self.board[row][col])? + } + // write!(f, "\n")? + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Point { + x: i32, + y: i32, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Room { + id: String, + x: i32, + y: i32, + x2: i32, + y2: i32, + width: i32, + height: i32, + center: Point, + room_type: i32, +} + +impl Room { + pub fn new(id: String, x: i32, y: i32, width: i32, height: i32, room_type: i32) -> Self { + Room { + id, + x, + y, + x2: x + width, + y2: y + height, + width, + height, + center: Point { + x: x + (width / 2), + y: y + (height / 2), + }, + room_type, + } + } + + pub fn intersects(&self, other: &Self) -> bool { + self.x <= other.x2 && self.x2 >= other.x && self.y <= other.y2 && self.y2 >= other.y + } + pub fn get_distance_to(&self, other: &Point) -> i32 { + (((other.x - self.center.x).pow(2) + (other.y - self.center.y).pow(2)) as f64).sqrt() as i32 + } +} diff --git a/src/lib.rs b/src/lib.rs index c6c07e9e..7c2088c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,8 @@ mod jobs; pub mod acreplace; #[cfg(feature = "cellularnoise")] pub mod cellularnoise; +#[cfg(feature = "binary_space_partition")] +pub mod binary_space_partition; #[cfg(feature = "dbpnoise")] pub mod dbpnoise; #[cfg(feature = "dmi")] @@ -37,6 +39,8 @@ pub mod log; pub mod noise_gen; #[cfg(feature = "pathfinder")] pub mod pathfinder; +#[cfg(feature = "random_room_placement")] +pub mod random_room_placement; #[cfg(feature = "redis_pubsub")] pub mod redis_pubsub; #[cfg(feature = "sql")] diff --git a/src/random_room_placement.rs b/src/random_room_placement.rs new file mode 100644 index 00000000..f6d735d6 --- /dev/null +++ b/src/random_room_placement.rs @@ -0,0 +1,288 @@ +use std::fmt; +use std::fmt::Write; +use rand::distributions::WeightedIndex; +use rand::prelude::*; +use rand::rngs::StdRng; +use rand::Rng; +use serde::Serialize; +use crate::error::Result; + +struct RandomRoomLevel { + level: Level, +} + +byond_fn!(fn random_room_generate(width, height, desired_room_count, hash) { + match random_room_gen(width, height, desired_room_count, hash) { + Ok(s) => Some(s), + Err(e) => Some(format!("{e}")) + } +}); +fn random_room_gen(width_as_str: &str, + height_as_str: &str, + desired_room_count_as_str: &str, + hash_as_str: &str, + ) + -> Result{ + let default_hash: u64 = rand::thread_rng().gen(); + let width = width_as_str.parse::()?; + let height = height_as_str.parse::()?; + let desired_room_count = desired_room_count_as_str.parse::()?; + + //let seed: &str = Alphanumeric.sample_string(&mut rand::thread_rng(), 32).as_str(); + + let mut rng: StdRng = SeedableRng::seed_from_u64(hash_as_str.parse::()?.try_into().unwrap_or(default_hash)); + + + let level = RandomRoomLevel::new(width, height, desired_room_count, &mut rng); + + + let mut string = String::new(); + for room in &level.all_rooms { + let serialized = serde_json::to_string(&room).unwrap(); + let _ = write!(string, "{}", serialized); + } + + Ok(format!("{}",serde_json::to_string(&level.all_rooms)?)) +} + +impl RandomRoomLevel { + fn new( + width: i32, + height: i32, + desired_room_count: i32, + rng: &mut StdRng, + + ) -> Level { + let level = Level::new(width, height); + + let mut map = RandomRoomLevel { level }; + + map.place_rooms_random(desired_room_count, rng); + map.level + } + + fn place_rooms_random(&mut self, desired_room_count: i32, rng: &mut StdRng) { + let max_rooms = desired_room_count as usize; + let max_attempts = 15; + let mut attempts = 0; + while self.level.all_rooms.iter().filter(|&rm| rm.room_type == 3).count() <= max_rooms && attempts <= max_attempts { + attempts += 1; + let mut x = rng.gen_range(0..self.level.width); + let mut y = rng.gen_range(0..self.level.height); + + let choices = [ + RoomDimensions::Maint3x3, + RoomDimensions::Maint3x5, + RoomDimensions::Maint5x3, + RoomDimensions::Maint5x4, + RoomDimensions::Maint10x5, + RoomDimensions::Maint10x10, + ]; + let weights = [4, 3, 4, 3, 2, 1]; + let dist = WeightedIndex::new(&weights).unwrap(); + //let mut rng = thread_rng(); + let room_layout = &choices[dist.sample(rng)]; + let width = room_layout.get_width(); + let height = room_layout.get_height(); + + if x + width > self.level.width { + x = self.level.width - width; + } + + if y + height > self.level.height { + y = self.level.height - height; + } + + let mut collides = false; + let room = Room::new(format!("ruin room: {}",self.level.all_rooms.iter().filter(|&rm| rm.room_type == 3).count()), x, y, width, height, 3); + + for other_room in &self.level.all_rooms { + if room.intersects(&other_room){ + collides = true; + break; + } + } + + if !collides { + self.level.add_room(&room); + attempts = 0; + } + } + } +} + +pub struct Level { + width: i32, + height: i32, + board: Vec>, + all_rooms: Vec, + increment: i32, + //hash: String, +} + +impl Level { + fn new( + width: i32, + height: i32, + ) -> Self { + let mut new_level = Level { + width, + height, + board: Vec::new(), + all_rooms: Vec::new(), + increment: 0, + }; + new_level.update_board(); + new_level + } + + fn update_board(&mut self) -> Vec> { + let mut new_board = Vec::new(); + self.increment+=1; + for _ in 0..self.height { + let space_tile = 0; + //let wall_tile = 1; + let floor_tile = 5; + let gen_floor_first = true; + + let mut row = vec![floor_tile; self.width as usize]; + if !gen_floor_first { + row = vec![space_tile; self.width as usize]; + } + // if gen_floor_first { + // if index == 0 || index == self.height - 1 { + // row = vec![wall_tile; self.width as usize]; + // } + + // row[0] = wall_tile; + // row[self.width as usize - 1] = wall_tile; + // } + + new_board.push(row); + } + for room in &self.all_rooms { + for row in 0..room.height { + for col in 0..room.width { + let y = (room.y + row) as usize; + let x = (room.x + col) as usize; + if row == 0 || col == 0 || row == room.height - 1 || col == room.width - 1 { + // might just let byond handle the walls + new_board[y][x] = 1; + } else { + new_board[y][x] = room.room_type; + } + } + } + } + self.board = new_board.clone(); + //draw(self, "increments", &self.increment.to_string()).unwrap(); + new_board + } + + fn add_room(&mut self, room: &Room) { + // match room.room_type { + // 2 => self.mandatory_rooms.push(room.clone()), + // _ => self.rooms.push(room.clone()), + // } + self.all_rooms.push(room.clone()); + self.update_board(); + + } + +} + +impl fmt::Display for Level { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for row in 0..self.height as usize { + for col in 0..self.width as usize { + write!(f, "{}", self.board[row][col])? + } + // write!(f, "\n")? + } + + Ok(()) + } +} + + +#[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] +pub enum RoomDimensions { + Maint3x3, + Maint3x5, + Maint5x3, + Maint5x4, + Maint10x5, + Maint10x10, +} +impl RoomDimensions { + fn get_height(&self) -> i32 { + let height: i32; + match *self { + RoomDimensions::Maint3x3 => height = 3, + RoomDimensions::Maint3x5 => height = 5, + RoomDimensions::Maint5x3 => height = 3, + RoomDimensions::Maint5x4 => height = 4, + RoomDimensions::Maint10x5 => height = 5, + RoomDimensions::Maint10x10 => height = 10, + } + return height + 2 + } + + fn get_width(&self) -> i32 { + let width: i32; + match *self { + RoomDimensions::Maint3x3 => width = 3, + RoomDimensions::Maint3x5 => width = 3, + RoomDimensions::Maint5x3 => width = 5, + RoomDimensions::Maint5x4 => width = 5, + RoomDimensions::Maint10x5 => width = 10, + RoomDimensions::Maint10x10 => width = 10, + } + return width + 2; + } +} + +#[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Point { + x: i32, + y: i32, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Room { + id: String, + x: i32, + y: i32, + x2: i32, + y2: i32, + width: i32, + height: i32, + center: Point, + room_type: i32, +} + +impl Room { + pub fn new(id: String, x: i32, y: i32, width: i32, height: i32, room_type: i32) -> Self { + Room { + id, + x, + y, + x2: x + width, + y2: y + height, + width, + height, + center: Point { + x: x + (width / 2), + y: y + (height / 2), + }, + room_type, + } + } + + pub fn intersects(&self, other: &Self) -> bool { + self.x <= other.x2 && self.x2 >= other.x && self.y <= other.y2 && self.y2 >= other.y + } + pub fn get_distance_to(&self, other: &Point) -> i32 { + (((other.x - self.center.x).pow(2) + (other.y - self.center.y).pow(2)) as f64).sqrt() as i32 + } +} From 90f4bbcc76a61ae1fc7319d5b2c945d253f3fc87 Mon Sep 17 00:00:00 2001 From: Chubbygummibear Date: Mon, 2 Oct 2023 14:01:14 -0700 Subject: [PATCH 2/7] alphabetize me captn --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7c2088c9..e3ed4024 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,10 +13,10 @@ mod jobs; #[cfg(feature = "acreplace")] pub mod acreplace; -#[cfg(feature = "cellularnoise")] -pub mod cellularnoise; #[cfg(feature = "binary_space_partition")] pub mod binary_space_partition; +#[cfg(feature = "cellularnoise")] +pub mod cellularnoise; #[cfg(feature = "dbpnoise")] pub mod dbpnoise; #[cfg(feature = "dmi")] From d5dbf6215f4f1cf49d6ec6c17b83b4ebaff02c98 Mon Sep 17 00:00:00 2001 From: Chubbygummibear Date: Thu, 5 Oct 2023 14:48:07 -0700 Subject: [PATCH 3/7] remove debug lines, remove unnecessary format proc, add procs to readme --- README.md | 2 ++ src/binary_space_partition.rs | 17 ++++------------- src/random_room_placement.rs | 16 +++------------- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 91e439bc..a27cc64e 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ To get additional features, pass a list to `--features`, for example `--features The default features are: * acreplace: Aho-Corasick string matching and replacement. +* binary_space_partition: Function to generate "rooms" more or less evenly distributed over a given area. * cellularnoise: Function to generate cellular automata-based noise. * dmi: DMI manipulations which are impossible from within BYOND. Used by the asset cache subsystem to improve load times. @@ -97,6 +98,7 @@ The default features are: * json: Function to check JSON validity. * log: Faster log output. * noise: 2d Perlin noise. +* random_room_placement: Function to generate "rooms" randomly placed in a given area, only taking care to not overlap one another. * sql: Asynchronous MySQL/MariaDB client library. * time: High-accuracy time measuring. * toml: TOML parser. diff --git a/src/binary_space_partition.rs b/src/binary_space_partition.rs index f1ba9cc2..e635856d 100644 --- a/src/binary_space_partition.rs +++ b/src/binary_space_partition.rs @@ -1,5 +1,4 @@ use std::{fmt, cmp}; -use std::fmt::Write; use rand::distributions::WeightedIndex; use rand::prelude::*; use rand::rngs::StdRng; @@ -16,11 +15,9 @@ struct BspLevel { byond_fn!(fn bsp_generate(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height) { - match bsp_gen(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height) { - Ok(s) => Some(s), - Err(e) => Some(format!("{e}")) - } + bsp_gen(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height).ok() }); + fn bsp_gen(width_as_str: &str, height_as_str: &str, hash_as_str: &str, @@ -34,6 +31,7 @@ fn bsp_gen(width_as_str: &str, let mut map_subsection_min_size = map_subsection_min_size_as_str.parse::()?; let map_subsection_min_room_width = map_subsection_min_room_width_as_str.parse::()?; let map_subsection_min_room_height = map_subsection_min_room_height_as_str.parse::()?; + if map_subsection_min_size < map_subsection_min_room_width || map_subsection_min_size < map_subsection_min_room_height{ map_subsection_min_size = cmp::max(map_subsection_min_room_width, map_subsection_min_room_height) + 1 } @@ -44,14 +42,7 @@ fn bsp_gen(width_as_str: &str, let level = BspLevel::new(width, height, &mut rng, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height); - let mut string = String::new(); - for room in &level.all_rooms { - let serialized = serde_json::to_string(&room).unwrap(); - let _ = write!(string, "{}", serialized); - //println!("serialized = {}", serialized); - } - - Ok(format!("{}",serde_json::to_string(&level.all_rooms)?)) + Ok(serde_json::to_string(&level.all_rooms)?) } impl BspLevel { diff --git a/src/random_room_placement.rs b/src/random_room_placement.rs index f6d735d6..27ff9e08 100644 --- a/src/random_room_placement.rs +++ b/src/random_room_placement.rs @@ -1,5 +1,4 @@ use std::fmt; -use std::fmt::Write; use rand::distributions::WeightedIndex; use rand::prelude::*; use rand::rngs::StdRng; @@ -12,11 +11,9 @@ struct RandomRoomLevel { } byond_fn!(fn random_room_generate(width, height, desired_room_count, hash) { - match random_room_gen(width, height, desired_room_count, hash) { - Ok(s) => Some(s), - Err(e) => Some(format!("{e}")) - } + random_room_gen(width, height, desired_room_count, hash).ok() }); + fn random_room_gen(width_as_str: &str, height_as_str: &str, desired_room_count_as_str: &str, @@ -35,14 +32,7 @@ fn random_room_gen(width_as_str: &str, let level = RandomRoomLevel::new(width, height, desired_room_count, &mut rng); - - let mut string = String::new(); - for room in &level.all_rooms { - let serialized = serde_json::to_string(&room).unwrap(); - let _ = write!(string, "{}", serialized); - } - - Ok(format!("{}",serde_json::to_string(&level.all_rooms)?)) + Ok(serde_json::to_string(&level.all_rooms)?) } impl RandomRoomLevel { From 07ee3788eaeed0f4e1e947bea281450c34c02587 Mon Sep 17 00:00:00 2001 From: Chubbygummibear Date: Thu, 5 Oct 2023 15:30:49 -0700 Subject: [PATCH 4/7] probably better compare logic --- src/binary_space_partition.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/binary_space_partition.rs b/src/binary_space_partition.rs index e635856d..c2dee52b 100644 --- a/src/binary_space_partition.rs +++ b/src/binary_space_partition.rs @@ -28,13 +28,14 @@ fn bsp_gen(width_as_str: &str, let default_hash: u64 = rand::thread_rng().gen(); let width = width_as_str.parse::()?; let height = height_as_str.parse::()?; - let mut map_subsection_min_size = map_subsection_min_size_as_str.parse::()?; let map_subsection_min_room_width = map_subsection_min_room_width_as_str.parse::()?; let map_subsection_min_room_height = map_subsection_min_room_height_as_str.parse::()?; - if map_subsection_min_size < map_subsection_min_room_width || map_subsection_min_size < map_subsection_min_room_height{ - map_subsection_min_size = cmp::max(map_subsection_min_room_width, map_subsection_min_room_height) + 1 - } + let map_subsection_min_size = cmp::max( + map_subsection_min_size_as_str.parse::()?, + cmp::max(map_subsection_min_room_width, map_subsection_min_room_height) + 1 + ); + //let seed: &str = Alphanumeric.sample_string(&mut rand::thread_rng(), 32).as_str(); let mut rng: StdRng = SeedableRng::seed_from_u64(hash_as_str.parse::()?.try_into().unwrap_or(default_hash)); From ec5d448d50767d86613428ebd128f7cf5c7932b6 Mon Sep 17 00:00:00 2001 From: Chubbygummibear Date: Fri, 13 Oct 2023 15:16:04 -0700 Subject: [PATCH 5/7] random room placement cleanup --- src/random_room_placement.rs | 143 +++++++++++++++-------------------- 1 file changed, 62 insertions(+), 81 deletions(-) diff --git a/src/random_room_placement.rs b/src/random_room_placement.rs index 27ff9e08..8c8232f8 100644 --- a/src/random_room_placement.rs +++ b/src/random_room_placement.rs @@ -21,27 +21,24 @@ fn random_room_gen(width_as_str: &str, ) -> Result{ let default_hash: u64 = rand::thread_rng().gen(); - let width = width_as_str.parse::()?; - let height = height_as_str.parse::()?; - let desired_room_count = desired_room_count_as_str.parse::()?; - - //let seed: &str = Alphanumeric.sample_string(&mut rand::thread_rng(), 32).as_str(); + let width = width_as_str.parse::()?; + let height = height_as_str.parse::()?; + let desired_room_count = desired_room_count_as_str.parse::()?; let mut rng: StdRng = SeedableRng::seed_from_u64(hash_as_str.parse::()?.try_into().unwrap_or(default_hash)); let level = RandomRoomLevel::new(width, height, desired_room_count, &mut rng); - Ok(serde_json::to_string(&level.all_rooms)?) + Ok(serde_json::to_string(&level.rooms)?) } impl RandomRoomLevel { fn new( - width: i32, - height: i32, - desired_room_count: i32, + width: usize, + height: usize, + desired_room_count: usize, rng: &mut StdRng, - ) -> Level { let level = Level::new(width, height); @@ -51,11 +48,11 @@ impl RandomRoomLevel { map.level } - fn place_rooms_random(&mut self, desired_room_count: i32, rng: &mut StdRng) { + fn place_rooms_random(&mut self, desired_room_count: usize, rng: &mut StdRng) { let max_rooms = desired_room_count as usize; let max_attempts = 15; let mut attempts = 0; - while self.level.all_rooms.iter().filter(|&rm| rm.room_type == 3).count() <= max_rooms && attempts <= max_attempts { + while self.level.rooms.len() <= max_rooms && attempts <= max_attempts { attempts += 1; let mut x = rng.gen_range(0..self.level.width); let mut y = rng.gen_range(0..self.level.height); @@ -84,9 +81,9 @@ impl RandomRoomLevel { } let mut collides = false; - let room = Room::new(format!("ruin room: {}",self.level.all_rooms.iter().filter(|&rm| rm.room_type == 3).count()), x, y, width, height, 3); + let room = Room::new(format!("ruin room: {}", self.level.rooms.len()), x, y, width, height); - for other_room in &self.level.all_rooms { + for other_room in &self.level.rooms { if room.intersects(&other_room){ collides = true; break; @@ -102,79 +99,70 @@ impl RandomRoomLevel { } pub struct Level { - width: i32, - height: i32, - board: Vec>, - all_rooms: Vec, - increment: i32, - //hash: String, + width: usize, + height: usize, + board: Vec>, + rooms: Vec, + increment: usize, +} + +#[derive(Debug, Clone, Copy)] +pub enum TileType { + Space = 0, + Floor = 1, + Wall = 2, } impl Level { fn new( - width: i32, - height: i32, + width: usize, + height: usize, ) -> Self { let mut new_level = Level { width, height, board: Vec::new(), - all_rooms: Vec::new(), + rooms: Vec::new(), increment: 0, }; new_level.update_board(); new_level } - fn update_board(&mut self) -> Vec> { + fn update_board(&mut self) -> Vec> { let mut new_board = Vec::new(); self.increment+=1; for _ in 0..self.height { - let space_tile = 0; - //let wall_tile = 1; - let floor_tile = 5; let gen_floor_first = true; - let mut row = vec![floor_tile; self.width as usize]; + let mut row = vec![TileType::Floor as usize; self.width as usize]; if !gen_floor_first { - row = vec![space_tile; self.width as usize]; + row = vec![TileType::Space as usize; self.width as usize]; } - // if gen_floor_first { - // if index == 0 || index == self.height - 1 { - // row = vec![wall_tile; self.width as usize]; - // } - // row[0] = wall_tile; - // row[self.width as usize - 1] = wall_tile; - // } new_board.push(row); } - for room in &self.all_rooms { + for room in &self.rooms { for row in 0..room.height { for col in 0..room.width { let y = (room.y + row) as usize; let x = (room.x + col) as usize; if row == 0 || col == 0 || row == room.height - 1 || col == room.width - 1 { // might just let byond handle the walls - new_board[y][x] = 1; + new_board[y][x] = TileType::Wall as usize; } else { - new_board[y][x] = room.room_type; + new_board[y][x] = TileType::Floor as usize; } } } } self.board = new_board.clone(); - //draw(self, "increments", &self.increment.to_string()).unwrap(); new_board } fn add_room(&mut self, room: &Room) { - // match room.room_type { - // 2 => self.mandatory_rooms.push(room.clone()), - // _ => self.rooms.push(room.clone()), - // } - self.all_rooms.push(room.clone()); + self.rooms.push(room.clone()); self.update_board(); } @@ -187,7 +175,6 @@ impl fmt::Display for Level { for col in 0..self.width as usize { write!(f, "{}", self.board[row][col])? } - // write!(f, "\n")? } Ok(()) @@ -205,54 +192,49 @@ pub enum RoomDimensions { Maint10x10, } impl RoomDimensions { - fn get_height(&self) -> i32 { - let height: i32; - match *self { - RoomDimensions::Maint3x3 => height = 3, - RoomDimensions::Maint3x5 => height = 5, - RoomDimensions::Maint5x3 => height = 3, - RoomDimensions::Maint5x4 => height = 4, - RoomDimensions::Maint10x5 => height = 5, - RoomDimensions::Maint10x10 => height = 10, - } - return height + 2 + fn get_height(&self) -> usize { + return match *self { + RoomDimensions::Maint3x3 => 3, + RoomDimensions::Maint3x5 => 5, + RoomDimensions::Maint5x3 => 3, + RoomDimensions::Maint5x4 => 4, + RoomDimensions::Maint10x5 => 5, + RoomDimensions::Maint10x10 => 10, + } + 2 //add 2 because the dimensions are equal to the inside of the room, and we need the dimensions with the walls in mind } - fn get_width(&self) -> i32 { - let width: i32; - match *self { - RoomDimensions::Maint3x3 => width = 3, - RoomDimensions::Maint3x5 => width = 3, - RoomDimensions::Maint5x3 => width = 5, - RoomDimensions::Maint5x4 => width = 5, - RoomDimensions::Maint10x5 => width = 10, - RoomDimensions::Maint10x10 => width = 10, - } - return width + 2; + fn get_width(&self) -> usize { + return match *self { + RoomDimensions::Maint3x3 => 3, + RoomDimensions::Maint3x5 => 3, + RoomDimensions::Maint5x3 => 5, + RoomDimensions::Maint5x4 => 5, + RoomDimensions::Maint10x5 => 10, + RoomDimensions::Maint10x10 => 10, + } + 2; //add 2 because the dimensions are equal to the inside of the room, and we need the dimensions with the walls in mind } } #[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Serialize)] pub struct Point { - x: i32, - y: i32, + x: usize, + y: usize, } #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] pub struct Room { id: String, - x: i32, - y: i32, - x2: i32, - y2: i32, - width: i32, - height: i32, + x: usize, + y: usize, + x2: usize, + y2: usize, + width: usize, + height: usize, center: Point, - room_type: i32, } impl Room { - pub fn new(id: String, x: i32, y: i32, width: i32, height: i32, room_type: i32) -> Self { + pub fn new(id: String, x: usize, y: usize, width: usize, height: usize) -> Self { Room { id, x, @@ -265,14 +247,13 @@ impl Room { x: x + (width / 2), y: y + (height / 2), }, - room_type, } } pub fn intersects(&self, other: &Self) -> bool { self.x <= other.x2 && self.x2 >= other.x && self.y <= other.y2 && self.y2 >= other.y } - pub fn get_distance_to(&self, other: &Point) -> i32 { - (((other.x - self.center.x).pow(2) + (other.y - self.center.y).pow(2)) as f64).sqrt() as i32 + pub fn get_distance_to(&self, other: &Point) -> usize { + (((other.x - self.center.x).pow(2) + (other.y - self.center.y).pow(2)) as f64).sqrt() as usize } } From 16917f11ca67c27d46df70e12726c79ee6670fa7 Mon Sep 17 00:00:00 2001 From: Chubbygummibear Date: Fri, 13 Oct 2023 15:42:32 -0700 Subject: [PATCH 6/7] more cleanup --- src/binary_space_partition.rs | 190 +++++++++++++++++++--------------- src/random_room_placement.rs | 67 ++++++------ 2 files changed, 139 insertions(+), 118 deletions(-) diff --git a/src/binary_space_partition.rs b/src/binary_space_partition.rs index c2dee52b..2185cf8b 100644 --- a/src/binary_space_partition.rs +++ b/src/binary_space_partition.rs @@ -1,63 +1,80 @@ -use std::{fmt, cmp}; -use rand::distributions::WeightedIndex; +use crate::error::Result; use rand::prelude::*; use rand::rngs::StdRng; use rand::Rng; use serde::Serialize; -use crate::error::Result; +use std::{cmp, fmt}; struct BspLevel { level: Level, - map_subsection_min_size: i32, - map_subsection_min_room_width: i32, - map_subsection_min_room_height: i32, + map_subsection_min_size: usize, + map_subsection_min_room_width: usize, + map_subsection_min_room_height: usize, } - byond_fn!(fn bsp_generate(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height) { bsp_gen(width, height, hash, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height).ok() }); -fn bsp_gen(width_as_str: &str, +fn bsp_gen( + width_as_str: &str, height_as_str: &str, hash_as_str: &str, map_subsection_min_size_as_str: &str, map_subsection_min_room_width_as_str: &str, - map_subsection_min_room_height_as_str: &str) - -> Result{ + map_subsection_min_room_height_as_str: &str, +) -> Result { let default_hash: u64 = rand::thread_rng().gen(); - let width = width_as_str.parse::()?; - let height = height_as_str.parse::()?; - let map_subsection_min_room_width = map_subsection_min_room_width_as_str.parse::()?; - let map_subsection_min_room_height = map_subsection_min_room_height_as_str.parse::()?; + let width = width_as_str.parse::()?; + let height = height_as_str.parse::()?; + let map_subsection_min_room_width = map_subsection_min_room_width_as_str.parse::()?; + let map_subsection_min_room_height = map_subsection_min_room_height_as_str.parse::()?; + //map subsections that the BSP algorithm creates should never be smaller than the minimum desired room size they will contain. This will crash the server let map_subsection_min_size = cmp::max( - map_subsection_min_size_as_str.parse::()?, - cmp::max(map_subsection_min_room_width, map_subsection_min_room_height) + 1 + map_subsection_min_size_as_str.parse::()?, + cmp::max( + map_subsection_min_room_width, + map_subsection_min_room_height, + ) + 1, ); - //let seed: &str = Alphanumeric.sample_string(&mut rand::thread_rng(), 32).as_str(); - - let mut rng: StdRng = SeedableRng::seed_from_u64(hash_as_str.parse::()?.try_into().unwrap_or(default_hash)); - + let mut rng: StdRng = SeedableRng::seed_from_u64( + hash_as_str + .parse::()? + .try_into() + .unwrap_or(default_hash), + ); - let level = BspLevel::new(width, height, &mut rng, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height); + let level = BspLevel::new( + width, + height, + &mut rng, + map_subsection_min_size, + map_subsection_min_room_width, + map_subsection_min_room_height, + ); - Ok(serde_json::to_string(&level.all_rooms)?) + Ok(serde_json::to_string(&level.rooms)?) } impl BspLevel { fn new( - width: i32, - height: i32, + width: usize, + height: usize, rng: &mut StdRng, - map_subsection_min_size: i32, - map_subsection_min_room_width: i32, - map_subsection_min_room_height: i32, + map_subsection_min_size: usize, + map_subsection_min_room_width: usize, + map_subsection_min_room_height: usize, ) -> Level { let level = Level::new(width, height); - let mut map = BspLevel { level, map_subsection_min_size, map_subsection_min_room_width, map_subsection_min_room_height }; + let mut map = BspLevel { + level, + map_subsection_min_size, + map_subsection_min_room_width, + map_subsection_min_room_height, + }; map.place_rooms(rng); @@ -65,7 +82,15 @@ impl BspLevel { } fn place_rooms(&mut self, rng: &mut StdRng) { - let mut root = Leaf::new(0, 0, self.level.width, self.level.height, self.map_subsection_min_size, self.map_subsection_min_room_width,self.map_subsection_min_room_height); + let mut root = Leaf::new( + 0, + 0, + self.level.width, + self.level.height, + self.map_subsection_min_size, + self.map_subsection_min_room_width, + self.map_subsection_min_room_height, + ); root.generate(rng); root.create_rooms(rng); @@ -73,7 +98,6 @@ impl BspLevel { for leaf in root.iter() { if leaf.is_leaf() { if let Some(room) = leaf.get_room() { - self.level.add_room(&room); } } @@ -82,18 +106,17 @@ impl BspLevel { self.level.add_room(&corridor); } } - } } struct Leaf { - min_size: i32, - x: i32, - y: i32, - width: i32, - height: i32, - min_room_width: i32, - min_room_height: i32, + min_size: usize, + x: usize, + y: usize, + width: usize, + height: usize, + min_room_width: usize, + min_room_height: usize, left_child: Option>, right_child: Option>, room: Option, @@ -101,7 +124,15 @@ struct Leaf { } impl Leaf { - fn new(x: i32, y: i32, width: i32, height: i32, min_size: i32, min_room_width: i32, min_room_height: i32,) -> Self { + fn new( + x: usize, + y: usize, + width: usize, + height: usize, + min_size: usize, + min_room_width: usize, + min_room_height: usize, + ) -> Self { Leaf { min_size, x, @@ -223,22 +254,15 @@ impl Leaf { let height = rng.gen_range(self.min_room_height..=self.height); let x = rng.gen_range(0..=self.width - width); let y = rng.gen_range(0..=self.height - height); - let choices = [0, 4]; - let weights = [1, 4]; - let dist = WeightedIndex::new(&weights).unwrap(); - let mut rng = thread_rng(); - let room_layout = choices[dist.sample(&mut rng)]; self.room = Some(Room::new( - format!("extra room"), + format!("bsp room"), x + self.x, y + self.y, width, height, - room_layout, )); } - } fn get_room(&self) -> Option { @@ -315,70 +339,67 @@ impl<'a> Iterator for LeafIterator<'a> { } } +#[derive(Debug, Clone, Copy)] +pub enum TileType { + Space = 0, + Floor = 1, + Wall = 2, +} pub struct Level { - width: i32, - height: i32, - board: Vec>, - all_rooms: Vec, - increment: i32, - //hash: String, + width: usize, + height: usize, + board: Vec>, + rooms: Vec, + increment: usize, } impl Level { - fn new( - width: i32, - height: i32, - ) -> Self { + fn new(width: usize, height: usize) -> Self { let mut new_level = Level { width, height, board: Vec::new(), - all_rooms: Vec::new(), + rooms: Vec::new(), increment: 0, }; new_level.update_board(); new_level } - fn update_board(&mut self) -> Vec> { + fn update_board(&mut self) -> Vec> { let mut new_board = Vec::new(); - self.increment+=1; + self.increment += 1; for _ in 0..self.height { - let space_tile = 0; - //let wall_tile = 1; - let floor_tile = 5; let gen_floor_first = true; - let mut row = vec![floor_tile; self.width as usize]; + let mut row = vec![TileType::Floor as usize; self.width as usize]; if !gen_floor_first { - row = vec![space_tile; self.width as usize]; + row = vec![TileType::Space as usize; self.width as usize]; } new_board.push(row); } - for room in &self.all_rooms { + for room in &self.rooms { for row in 0..room.height { for col in 0..room.width { let y = (room.y + row) as usize; let x = (room.x + col) as usize; if row == 0 || col == 0 || row == room.height - 1 || col == room.width - 1 { // might just let byond handle the walls - new_board[y][x] = 1; + new_board[y][x] = TileType::Wall as usize; } else { - new_board[y][x] = room.room_type; + new_board[y][x] = TileType::Floor as usize; } } } } self.board = new_board.clone(); - //draw(self, "increments", &self.increment.to_string()).unwrap(); new_board } fn add_room(&mut self, room: &Room) { - self.all_rooms.push(room.clone()); + self.rooms.push(room.clone()); self.update_board(); - } } @@ -388,7 +409,7 @@ impl fmt::Display for Level { for col in 0..self.width as usize { write!(f, "{}", self.board[row][col])? } - // write!(f, "\n")? + write!(f, "\n")? } Ok(()) @@ -397,25 +418,24 @@ impl fmt::Display for Level { #[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Serialize)] pub struct Point { - x: i32, - y: i32, + x: usize, + y: usize, } #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] pub struct Room { id: String, - x: i32, - y: i32, - x2: i32, - y2: i32, - width: i32, - height: i32, + x: usize, + y: usize, + x2: usize, + y2: usize, + width: usize, + height: usize, center: Point, - room_type: i32, } impl Room { - pub fn new(id: String, x: i32, y: i32, width: i32, height: i32, room_type: i32) -> Self { + pub fn new(id: String, x: usize, y: usize, width: usize, height: usize) -> Self { Room { id, x, @@ -428,14 +448,14 @@ impl Room { x: x + (width / 2), y: y + (height / 2), }, - room_type, } } pub fn intersects(&self, other: &Self) -> bool { self.x <= other.x2 && self.x2 >= other.x && self.y <= other.y2 && self.y2 >= other.y } - pub fn get_distance_to(&self, other: &Point) -> i32 { - (((other.x - self.center.x).pow(2) + (other.y - self.center.y).pow(2)) as f64).sqrt() as i32 + pub fn get_distance_to(&self, other: &Point) -> usize { + (((other.x - self.center.x).pow(2) + (other.y - self.center.y).pow(2)) as f64).sqrt() + as usize } } diff --git a/src/random_room_placement.rs b/src/random_room_placement.rs index 8c8232f8..c91ca2fc 100644 --- a/src/random_room_placement.rs +++ b/src/random_room_placement.rs @@ -1,10 +1,10 @@ -use std::fmt; +use crate::error::Result; use rand::distributions::WeightedIndex; use rand::prelude::*; use rand::rngs::StdRng; use rand::Rng; use serde::Serialize; -use crate::error::Result; +use std::fmt; struct RandomRoomLevel { level: Level, @@ -14,19 +14,23 @@ byond_fn!(fn random_room_generate(width, height, desired_room_count, hash) { random_room_gen(width, height, desired_room_count, hash).ok() }); -fn random_room_gen(width_as_str: &str, +fn random_room_gen( + width_as_str: &str, height_as_str: &str, desired_room_count_as_str: &str, hash_as_str: &str, - ) - -> Result{ +) -> Result { let default_hash: u64 = rand::thread_rng().gen(); let width = width_as_str.parse::()?; let height = height_as_str.parse::()?; let desired_room_count = desired_room_count_as_str.parse::()?; - let mut rng: StdRng = SeedableRng::seed_from_u64(hash_as_str.parse::()?.try_into().unwrap_or(default_hash)); - + let mut rng: StdRng = SeedableRng::seed_from_u64( + hash_as_str + .parse::()? + .try_into() + .unwrap_or(default_hash), + ); let level = RandomRoomLevel::new(width, height, desired_room_count, &mut rng); @@ -34,12 +38,7 @@ fn random_room_gen(width_as_str: &str, } impl RandomRoomLevel { - fn new( - width: usize, - height: usize, - desired_room_count: usize, - rng: &mut StdRng, - ) -> Level { + fn new(width: usize, height: usize, desired_room_count: usize, rng: &mut StdRng) -> Level { let level = Level::new(width, height); let mut map = RandomRoomLevel { level }; @@ -81,10 +80,17 @@ impl RandomRoomLevel { } let mut collides = false; - let room = Room::new(format!("ruin room: {}", self.level.rooms.len()), x, y, width, height); + + let room = Room::new( + format!("RRPS room: {}", self.level.rooms.len()), + x, + y, + width, + height, + ); for other_room in &self.level.rooms { - if room.intersects(&other_room){ + if room.intersects(&other_room) { collides = true; break; } @@ -98,6 +104,13 @@ impl RandomRoomLevel { } } +#[derive(Debug, Clone, Copy)] +pub enum TileType { + Space = 0, + Floor = 1, + Wall = 2, +} + pub struct Level { width: usize, height: usize, @@ -106,18 +119,8 @@ pub struct Level { increment: usize, } -#[derive(Debug, Clone, Copy)] -pub enum TileType { - Space = 0, - Floor = 1, - Wall = 2, -} - impl Level { - fn new( - width: usize, - height: usize, - ) -> Self { + fn new(width: usize, height: usize) -> Self { let mut new_level = Level { width, height, @@ -131,7 +134,7 @@ impl Level { fn update_board(&mut self) -> Vec> { let mut new_board = Vec::new(); - self.increment+=1; + self.increment += 1; for _ in 0..self.height { let gen_floor_first = true; @@ -140,7 +143,6 @@ impl Level { row = vec![TileType::Space as usize; self.width as usize]; } - new_board.push(row); } for room in &self.rooms { @@ -164,9 +166,7 @@ impl Level { fn add_room(&mut self, room: &Room) { self.rooms.push(room.clone()); self.update_board(); - } - } impl fmt::Display for Level { @@ -175,13 +175,13 @@ impl fmt::Display for Level { for col in 0..self.width as usize { write!(f, "{}", self.board[row][col])? } + write!(f, "\n")? } Ok(()) } } - #[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] pub enum RoomDimensions { Maint3x3, @@ -200,7 +200,7 @@ impl RoomDimensions { RoomDimensions::Maint5x4 => 4, RoomDimensions::Maint10x5 => 5, RoomDimensions::Maint10x10 => 10, - } + 2 //add 2 because the dimensions are equal to the inside of the room, and we need the dimensions with the walls in mind + } + 2; //add 2 because the dimensions are equal to the inside of the room, and we need the dimensions with the walls in mind } fn get_width(&self) -> usize { @@ -254,6 +254,7 @@ impl Room { self.x <= other.x2 && self.x2 >= other.x && self.y <= other.y2 && self.y2 >= other.y } pub fn get_distance_to(&self, other: &Point) -> usize { - (((other.x - self.center.x).pow(2) + (other.y - self.center.y).pow(2)) as f64).sqrt() as usize + (((other.x - self.center.x).pow(2) + (other.y - self.center.y).pow(2)) as f64).sqrt() + as usize } } From d583aba5fc5cdcc52124ee18e2605f2552968f9b Mon Sep 17 00:00:00 2001 From: Chubbygummibear <46236974+Chubbygummibear@users.noreply.github.com> Date: Fri, 3 Nov 2023 17:15:22 -0700 Subject: [PATCH 7/7] Update src/binary_space_partition.rs simplify is_leaf Co-authored-by: Ashleigh Carr --- src/binary_space_partition.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/binary_space_partition.rs b/src/binary_space_partition.rs index 2185cf8b..0e1248e3 100644 --- a/src/binary_space_partition.rs +++ b/src/binary_space_partition.rs @@ -149,13 +149,7 @@ impl Leaf { } fn is_leaf(&self) -> bool { - match self.left_child { - None => match self.right_child { - None => true, - Some(_) => false, - }, - Some(_) => false, - } + self.left_child.is_none() && self.right_child.is_none() } fn generate(&mut self, rng: &mut StdRng) {