use crate::generators::MazeGenerator2D;
use crate::grid_coord_2d::{GetCoordinateBounds2D, GridCoord2D};
use crate::visit_map_2d::VisitMap2D;
use crate::wall4_grid::Wall4Grid;
use rand::prelude::SliceRandom;
use rand::rngs::StdRng;
use rand::SeedableRng;
pub struct RecursiveBacktracker4 {
rng: StdRng,
}
impl Default for RecursiveBacktracker4 {
fn default() -> Self {
Self {
rng: StdRng::from_entropy(),
}
}
}
impl RecursiveBacktracker4 {
pub fn new_random() -> Self {
Self {
rng: StdRng::from_entropy(),
}
}
pub fn new_from_seed(rng_seed: u64) -> Self {
if rng_seed == 0 {
Self::new_random()
} else {
Self {
rng: StdRng::seed_from_u64(rng_seed),
}
}
}
pub fn generate(&self, width: usize, height: usize) -> Wall4Grid {
let mut cells = Wall4Grid::new(width, height);
let mut visit_map = VisitMap2D::new_like(&cells);
let start_coordinate = GridCoord2D::default();
let mut backtrace = Vec::default();
let rng = self.rng.clone();
Self::backtrack(
rng,
&mut cells,
&mut visit_map,
start_coordinate,
&mut backtrace,
);
cells
}
fn backtrack(
mut rng: StdRng,
cells: &mut Wall4Grid,
visit_map: &mut VisitMap2D,
mut current_cell: GridCoord2D,
backtrace: &mut Vec<GridCoord2D>,
) {
debug_assert_eq!(cells.width(), visit_map.width());
debug_assert_eq!(cells.height(), visit_map.height());
loop {
visit_map[current_cell] = true;
if let Some(selected_cell) =
Self::select_random_unvisited_neighbor(&mut rng, visit_map, current_cell)
{
backtrace.push(current_cell);
cells.remove_wall_between(current_cell, selected_cell);
current_cell = selected_cell;
continue;
}
if let Some(cell) = backtrace.pop() {
current_cell = cell;
} else {
break;
}
}
}
fn select_random_unvisited_neighbor(
rng: &mut StdRng,
visit_map: &VisitMap2D,
current_cell: GridCoord2D,
) -> Option<GridCoord2D> {
debug_assert!(current_cell.x < visit_map.width());
debug_assert!(current_cell.y < visit_map.height());
let list = visit_map.unvisited_neighbors(current_cell);
if list.is_empty() {
return None;
}
list.choose(rng).cloned()
}
}
impl MazeGenerator2D for RecursiveBacktracker4 {
fn new_random() -> Self {
RecursiveBacktracker4::new_random()
}
fn new_from_seed(rng_seed: u64) -> Self {
RecursiveBacktracker4::new_from_seed(rng_seed)
}
fn generate(&self, width: usize, height: usize) -> Wall4Grid {
self.generate(width, height)
}
}
#[cfg(all(test, feature = "unicode-renderer"))]
mod tests {
use super::*;
use crate::renderers::{UnicodeRenderStyle, UnicodeRenderer};
#[test]
fn it_works() {
let gen = RecursiveBacktracker4::default();
let grid = gen.generate(16, 16);
assert_eq!(grid.width(), 16);
assert_eq!(grid.height(), 16);
let renderer = UnicodeRenderer::new(UnicodeRenderStyle::Heavy, true);
let str = renderer.render(&grid);
println!("{}", str);
}
}