1
\$\begingroup\$

So I have this binary spaced partition leaf:

#[derive(Clone)] pub struct Leaf { container: Rect, left: Option<Box<Leaf>>, right: Option<Box<Leaf>>, room: Option<Rect>, split_vertical: Option<bool>, } impl Leaf { fn new(container: Rect) -> Leaf { Leaf { container, left: None, right: None, room: None, split_vertical: None, } } fn split( &mut self, grid: Vec<Vec<i32>>, mut random_generator: StdRng, min_container_size: i32, debug_game: bool, ) -> bool { if self.left.is_some() && self.right.is_some() { return false; } let mut split_vertical: bool = random_generator.gen::<bool>(); if (self.container.width > self.container.height) && ((self.container.width.unwrap() / self.container.height.unwrap()) as f64 >= 1.25) { split_vertical = true; } else if (self.container.height > self.container.width) && ((self.container.height.unwrap() / self.container.width.unwrap()) as f64 >= 1.25) { split_vertical = false; } let max_size: i32 = if split_vertical { self.container.width.unwrap() - min_container_size } else { self.container.height.unwrap() - min_container_size }; if max_size <= min_container_size { return false; } let mut pos: i32 = random_generator.gen_range(min_container_size..max_size); if split_vertical { pos += self.container.top_left.x; if debug_game { println!("split vertical debug"); } self.left = Option::from(Box::new(Leaf { container: Rect { top_left: Point { x: self.container.top_left.x, y: self.container.top_left.y, }, bottom_right: Point { x: pos - 1, y: self.container.bottom_right.y, }, center: None, width: None, height: None, }, left: None, right: None, room: None, split_vertical: None, })); self.right = Option::from(Box::new(Leaf { container: Rect { top_left: Point { x: pos + 1, y: self.container.top_left.y, }, bottom_right: Point { x: self.container.bottom_right.x, y: self.container.bottom_right.y, }, center: None, width: None, height: None, }, left: None, right: None, room: None, split_vertical: None, })) } else { pos += self.container.top_left.y; if debug_game { println!("split horizontal debug"); } self.left = Option::from(Box::new(Leaf { container: Rect { top_left: Point { x: self.container.top_left.x, y: self.container.top_left.y, }, bottom_right: Point { x: self.container.bottom_right.x, y: pos - 1, }, center: None, width: None, height: None, }, left: None, right: None, room: None, split_vertical: None, })); self.right = Option::from(Box::new(Leaf { container: Rect { top_left: Point { x: self.container.top_left.x, y: pos - 1, }, bottom_right: Point { x: self.container.bottom_right.x, y: self.container.bottom_right.y, }, center: None, width: None, height: None, }, left: None, right: None, room: None, split_vertical: None, })) } self.split_vertical = Option::from(split_vertical); return true; } fn create_room( &mut self, grid: Vec<Vec<i32>>, mut random_generator: StdRng, min_room_size: i32, room_ratio: f32, ) -> bool { if self.left.is_some() && self.right.is_some() { return false; } let width: i32 = random_generator.gen_range(min_room_size..self.container.width.unwrap()); let height: i32 = random_generator.gen_range(min_room_size..self.container.height.unwrap()); let x_pos: i32 = random_generator .gen_range(self.container.top_left.x..self.container.bottom_right.x - width); let y_pos: i32 = random_generator .gen_range(self.container.top_left.y..self.container.bottom_right.y - height); let rect: Rect = Rect { top_left: Point { x: x_pos, y: y_pos }, bottom_right: Point { x: x_pos + width - 1, y: y_pos + height - 1, }, center: None, width: None, height: None, }; if ((min(rect.width.unwrap(), rect.height.unwrap()) as f32) / (max(rect.width.unwrap(), rect.height.unwrap()) as f32)) < room_ratio { return false; } rect.place_rect(grid); self.room = Option::from(rect); return true; } } 

And the Point and Rect structs:

#[derive(PartialEq, Eq, Hash, Clone, Copy)] pub struct Point { pub x: i32, pub y: i32, } impl Point { fn new(x: i32, y: i32) -> Point { Point { x, y } } pub fn sum(&self, other: &Point) -> (i32, i32) { (self.x + other.x, self.y + other.y) } pub fn abs_diff(&self, other: &Point) -> (i32, i32) { ((self.x - other.x).abs(), (self.y - other.y).abs()) } } #[derive(Clone)] pub struct Rect { pub top_left: Point, pub bottom_right: Point, pub center: Option<Point>, pub width: Option<i32>, pub height: Option<i32>, } impl Rect { fn new(top_left: Point, bottom_right: Point) -> Rect { let sum: (i32, i32) = top_left.sum(&bottom_right); let diff: (i32, i32) = bottom_right.abs_diff(&top_left); Rect { top_left, bottom_right, center: Option::from(Point { x: ((sum.0 as f32) / 2.0).round() as i32, y: ((sum.1 as f32) / 2.0).round() as i32, }), width: Option::from(diff.0), height: Option::from(diff.1), } } pub fn get_distance_to(&self, other: &Rect) -> i32 { return max( (self.center.unwrap().x - other.center.unwrap().x).abs(), (self.center.unwrap().y - other.center.unwrap().y).abs(), ); } pub fn place_rect(&self, grid: Vec<Vec<i32>>) {} } 

However, I'm fairly new to Rust and am not sure how to optimise this further than I already have. I know there's some repetition (especially with creating the child leafs) and wanted to know if there was a way to simplify it, speed it up, and make it more idiomatic?

\$\endgroup\$

    1 Answer 1

    2
    \$\begingroup\$
    #[derive(Clone)] pub struct Leaf { container: Rect, left: Option<Box<Leaf>>, right: Option<Box<Leaf>>, room: Option<Rect>, split_vertical: Option<bool>, } 

    The left, right, and split_vertical options are all linked. They are all set together. So I'd do something like:

    #[derive(Clone)] pub struct Division { left: Leaf, right: Leaf, split_vertical: bool } #[derive(Clone)] pub struct Leaf { container: Rect, division: Option<Box<Division>>, room: Option<Rect>, } 

    This will allow you to simplify the code quite a bit as you don't have to create multiple options/boxes/unwrap separate fields.

     self.left = Option::from(Box::new(Leaf { container: Rect { top_left: Point { x: self.container.top_left.x, y: self.container.top_left.y, }, bottom_right: Point { x: self.container.bottom_right.x, y: pos - 1, }, center: None, width: None, height: None, }, left: None, right: None, room: None, split_vertical: None, })); 

    You already have Leaf::new and Rect::new which could be used to avoid most of the boilerplate that you have here.

     self.left = Option::from(Box::new(Leaf::new(Rect::new(Point { x: self.container.top_left.x, y: self.container.top_left.y, }, Point { x: self.container.bottom_right.x, y: pos - 1, })))); 
    \$\endgroup\$
    5
    • \$\begingroup\$How would you store and instantiated division struct into the left or right leafs? Surely, you would need more code duplication within the if else statement\$\endgroup\$
      – Aspect11
      CommentedJan 6, 2023 at 14:26
    • \$\begingroup\$@Aspect11, I'm afraid I don't understand your question.\$\endgroup\$CommentedJan 7, 2023 at 2:28
    • \$\begingroup\$How would you store the division into the left/right children?\$\endgroup\$
      – Aspect11
      CommentedJan 7, 2023 at 14:10
    • \$\begingroup\$@Aspect11 the division contains the left/right children. So I don't understand why you are asking about store the division into the children\$\endgroup\$CommentedJan 7, 2023 at 16:45
    • \$\begingroup\$@Aspect11, since there's clear some point of confusion and I can't figure out what you are asking, I went around and rewrote your code with my suggestions here: play.rust-lang.org/…, hopefully that will clarify what you are asking.\$\endgroup\$CommentedJan 7, 2023 at 17:08

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.