use super::Algorithm;
use crate::utils::types::Coords;
use crate::maze::grid::{Grid, cell::Cell};
use rand::prelude::*;
pub struct HuntAndKill {
hunt_start_index: usize,
}
impl HuntAndKill {
pub const fn new() -> HuntAndKill {
HuntAndKill {
hunt_start_index: 0,
}
}
fn walk<R: Rng>(&self, coords: Coords, grid: &mut Grid, rng: &mut R) -> Option<Coords> {
let mut directions = [Cell::NORTH, Cell::SOUTH, Cell::WEST, Cell::EAST];
directions.shuffle(rng);
for dir in directions {
if let Ok(next_coords) = grid.get_next_cell_coords(coords, dir) {
if !grid.is_cell_visited(next_coords) {
return grid.carve_passage(coords, dir).ok();
}
}
}
None
}
fn hunt(&mut self, grid: &mut Grid) -> Option<Coords> {
let directions = [Cell::NORTH, Cell::SOUTH, Cell::WEST, Cell::EAST];
for y in self.hunt_start_index..grid.height() {
let mut unvisited_cells_count = 0;
for x in 0..grid.width() {
if grid.is_cell_visited((x, y)) {
continue;
} else {
unvisited_cells_count += 1;
}
for dir in directions {
if let Ok(next_coords) = grid.get_next_cell_coords((x, y), dir) {
if grid.is_cell_visited(next_coords) {
grid.carve_passage((x, y), dir).ok();
return Some((x, y));
}
}
}
}
if unvisited_cells_count == 0 {
self.hunt_start_index = y + 1;
}
}
None
}
}
impl Default for HuntAndKill {
fn default() -> Self {
Self::new()
}
}
impl Algorithm for HuntAndKill {
fn generate(&mut self, grid: &mut Grid, rng: &mut StdRng) {
let start_coords = get_start_coords(grid, rng);
let mut x = start_coords.0;
let mut y = start_coords.1;
loop {
if let Some((nx, ny)) = self.walk((x, y), grid, rng) {
x = nx;
y = ny;
} else if let Some((nx, ny)) = self.hunt(grid) {
x = nx;
y = ny;
} else {
break;
}
}
}
}
fn get_start_coords<R: Rng>(grid: &Grid, rng: &mut R) -> Coords {
let y = rng.random_range(0..grid.height());
let x = rng.random_range(0..grid.width());
(x, y)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_call() {
let algo = HuntAndKill::default();
assert_eq!(0, algo.hunt_start_index);
}
}