use crate::{errors::MazeBuilderError, GeneratorType, Maze};
use hexx::Hex;
#[allow(clippy::module_name_repetitions)]
#[derive(Default)]
pub struct MazeBuilder {
radius: Option<u16>,
seed: Option<u64>,
generator_type: GeneratorType,
start_position: Option<Hex>,
}
impl MazeBuilder {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[inline]
#[must_use]
pub const fn with_radius(mut self, radius: u16) -> Self {
self.radius = Some(radius);
self
}
#[inline]
#[must_use]
pub const fn with_seed(mut self, seed: u64) -> Self {
self.seed = Some(seed);
self
}
#[inline]
#[must_use]
pub const fn with_generator(mut self, generator_type: GeneratorType) -> Self {
self.generator_type = generator_type;
self
}
#[inline]
#[must_use]
pub const fn with_start_position(mut self, pos: Hex) -> Self {
self.start_position = Some(pos);
self
}
pub fn build(self) -> Result<Maze, MazeBuilderError> {
let radius = self.radius.ok_or(MazeBuilderError::NoRadius)?;
let mut maze = create_hex_maze(radius);
if let Some(start_pos) = self.start_position {
if maze.get(&start_pos).is_none() {
return Err(MazeBuilderError::InvalidStartPosition(start_pos));
}
}
if !maze.is_empty() {
self.generator_type
.generate(&mut maze, self.start_position, self.seed);
}
Ok(maze)
}
}
pub fn create_hex_maze(radius: u16) -> Maze {
let mut maze = Maze::new();
let radius = i32::from(radius);
for q in -radius..=radius {
let r1 = (-radius).max(-q - radius);
let r2 = radius.min(-q + radius);
for r in r1..=r2 {
let pos = Hex::new(q, r);
maze.insert(pos);
}
}
maze
}
#[cfg(test)]
mod test {
use super::*;
use claims::assert_gt;
use rstest::rstest;
#[test]
fn maze_builder_new() {
let builder = MazeBuilder::new();
assert_eq!(builder.radius, None);
assert_eq!(builder.seed, None);
assert_eq!(builder.generator_type, GeneratorType::default());
assert_eq!(builder.start_position, None);
}
#[rstest]
#[case(0, 1)] #[case(1, 7)]
#[case(2, 19)]
#[case(3, 37)]
#[case(10, 331)]
#[case(100, 30301)]
fn create_hex_maze_various_radii(#[case] radius: u16, #[case] expected_size: usize) {
let maze = create_hex_maze(radius);
assert_eq!(maze.count(), expected_size);
}
#[test]
fn create_hex_maze_large_radius() {
let large_radius = 1000;
let maze = create_hex_maze(large_radius);
assert_gt!(maze.count(), 0);
let expected_size = 3 * (large_radius as usize).pow(2) + 3 * large_radius as usize + 1;
assert_eq!(maze.count(), expected_size);
}
#[test]
fn create_hex_maze_tile_positions() {
let maze = create_hex_maze(2);
let expected_positions = [
Hex::new(0, 0),
Hex::new(1, -1),
Hex::new(1, 0),
Hex::new(0, 1),
Hex::new(-1, 1),
Hex::new(-1, 0),
Hex::new(0, -1),
Hex::new(2, -2),
Hex::new(2, -1),
Hex::new(2, 0),
Hex::new(1, 1),
Hex::new(0, 2),
Hex::new(-1, 2),
Hex::new(-2, 2),
Hex::new(-2, 1),
Hex::new(-2, 0),
Hex::new(-1, -1),
Hex::new(0, -2),
Hex::new(1, -2),
];
for pos in expected_positions.iter() {
assert!(maze.get(pos).is_some(), "Expected tile at {:?}", pos);
}
}
}