amaze 0.4.1

A maze generator
Documentation
use crate::direction6::Direction6;
use crate::generators::{
    CellSelector, HexGenerationStep, HexGenerationSteps, HexGenerationVisitor, MazeGenerator6D,
    NewestCell, VecHexGenerationVisitor,
};
use crate::hex_coord::HexCoord;
use crate::wall6_grid::Wall6Grid;
use rand::prelude::SliceRandom;
use rand::rngs::StdRng;
use rand::{RngExt, SeedableRng};

pub struct GrowingTree6<S: CellSelector = NewestCell> {
    rng_seed: u64,
    selector: S,
}

impl<S> GrowingTree6<S>
where
    S: CellSelector,
{
    pub fn with_selector(selector: S) -> Self {
        Self {
            rng_seed: rand::random(),
            selector,
        }
    }

    pub fn new_from_seed_with_selector(rng_seed: u64, selector: S) -> Self {
        let rng_seed = if rng_seed == 0 {
            rand::random()
        } else {
            rng_seed
        };

        Self { rng_seed, selector }
    }

    pub fn generate(&self, width: usize, height: usize) -> Wall6Grid {
        self.generate_with_steps(width, height).0
    }

    pub fn generate_steps(&self, width: usize, height: usize) -> HexGenerationSteps {
        HexGenerationSteps::new(self.generate_with_steps(width, height).1)
    }

    fn generate_with_steps(
        &self,
        width: usize,
        height: usize,
    ) -> (Wall6Grid, Vec<HexGenerationStep>) {
        let mut grid = Wall6Grid::new(width, height);
        let mut visitor = VecHexGenerationVisitor::default();

        if width == 0 || height == 0 {
            visitor.on_step(&HexGenerationStep::Complete);
            return (grid, visitor.into_steps());
        }

        let mut visited = vec![false; width * height];
        let mut frontier: Vec<HexCoord> = Vec::new();
        let mut rng = StdRng::seed_from_u64(self.rng_seed);

        let start = HexCoord::new(
            rng.random_range(0..width) as isize,
            rng.random_range(0..height) as isize,
        );

        let start_idx = (start.r as usize) * width + start.q as usize;
        visited[start_idx] = true;
        visitor.on_step(&HexGenerationStep::Visit { cell: start });
        frontier.push(start);
        visitor.on_step(&HexGenerationStep::AddToFrontier { cell: start });

        while !frontier.is_empty() {
            let idx = self.selector.select(&mut rng, frontier.len());
            let cell = frontier[idx];
            let mut candidates = Self::unvisited_neighbors(&visited, cell, width, height);

            if candidates.is_empty() {
                frontier.swap_remove(idx);
                continue;
            }

            candidates.shuffle(&mut rng);
            let next = candidates[0];
            grid.remove_wall_between(cell, next);
            visitor.on_step(&HexGenerationStep::Carve {
                from: cell,
                to: next,
            });
            let next_idx = (next.r as usize) * width + next.q as usize;
            visited[next_idx] = true;
            visitor.on_step(&HexGenerationStep::Visit { cell: next });
            frontier.push(next);
            visitor.on_step(&HexGenerationStep::AddToFrontier { cell: next });
        }

        visitor.on_step(&HexGenerationStep::Complete);
        (grid, visitor.into_steps())
    }

    fn unvisited_neighbors(
        visited: &[bool],
        cell: HexCoord,
        width: usize,
        height: usize,
    ) -> Vec<HexCoord> {
        let mut result = Vec::with_capacity(6);
        for &dir in &Direction6::CARDINALS {
            if let Some(n) = cell.try_neighbor(dir, width, height) {
                let idx = (n.r as usize) * width + n.q as usize;
                if !visited[idx] {
                    result.push(n);
                }
            }
        }
        result
    }
}

impl<S> MazeGenerator6D for GrowingTree6<S>
where
    S: CellSelector + Default,
{
    fn new_random() -> Self {
        Self {
            rng_seed: rand::random(),
            selector: S::default(),
        }
    }

    fn new_from_seed(rng_seed: u64) -> Self {
        if rng_seed == 0 {
            Self::new_random()
        } else {
            Self {
                rng_seed,
                selector: S::default(),
            }
        }
    }

    fn generate(&self, width: usize, height: usize) -> Wall6Grid {
        self.generate(width, height)
    }

    fn generate_steps(&self, width: usize, height: usize) -> HexGenerationSteps {
        self.generate_steps(width, height)
    }

    fn name(&self) -> &'static str {
        "hex-growing-tree"
    }

    fn description(&self) -> &'static str {
        "Growing tree maze generator for hexagonal grids with configurable frontier selection"
    }
}