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?
1 Answer 1
#[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,
}))));
-
\$\begingroup\$ How would you store and instantiated division struct into the
left
orright
leafs? Surely, you would need more code duplication within the if else statement \$\endgroup\$Aspect11– Aspect112023年01月06日 14:26:57 +00:00Commented Jan 6, 2023 at 14:26 -
\$\begingroup\$ @Aspect11, I'm afraid I don't understand your question. \$\endgroup\$Winston Ewert– Winston Ewert2023年01月07日 02:28:46 +00:00Commented Jan 7, 2023 at 2:28
-
\$\begingroup\$ How would you store the division into the left/right children? \$\endgroup\$Aspect11– Aspect112023年01月07日 14:10:42 +00:00Commented 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\$Winston Ewert– Winston Ewert2023年01月07日 16:45:44 +00:00Commented 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\$Winston Ewert– Winston Ewert2023年01月07日 17:08:16 +00:00Commented Jan 7, 2023 at 17:08