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
    Commented Jan 6, 2023 at 14:26
  • \$\begingroup\$ @Aspect11, I'm afraid I don't understand your question. \$\endgroup\$ Commented Jan 7, 2023 at 2:28
  • \$\begingroup\$ How would you store the division into the left/right children? \$\endgroup\$
    – Aspect11
    Commented Jan 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\$ Commented Jan 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\$ Commented Jan 7, 2023 at 17:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.