use crate::Maze; use hexx::{EdgeDirection, Hex}; use rand::{rngs::StdRng, seq::SliceRandom, thread_rng, Rng, RngCore, SeedableRng}; use std::collections::HashSet; pub(super) fn generate_backtracking(maze: &mut Maze, start_pos: Option, seed: Option) { if maze.is_empty() { return; } let start = start_pos.unwrap_or(Hex::ZERO); let mut visited = HashSet::new(); let mut rng: Box = seed.map_or_else( || Box::new(thread_rng()) as Box, |seed| Box::new(StdRng::seed_from_u64(seed)) as Box, ); recursive_backtrack(maze, start, &mut visited, &mut rng); } fn recursive_backtrack( maze: &mut Maze, current: Hex, visited: &mut HashSet, rng: &mut R, ) { visited.insert(current); let mut directions = EdgeDirection::ALL_DIRECTIONS; directions.shuffle(rng); for direction in directions { let neighbor = current + direction; if maze.get(&neighbor).is_some() && !visited.contains(&neighbor) { let _ = maze.remove_tile_wall(¤t, direction); let _ = maze.remove_tile_wall(&neighbor, direction.const_neg()); recursive_backtrack(maze, neighbor, visited, rng); } } } #[cfg(test)] mod test { use super::*; use crate::builder::create_hex_maze; use rstest::rstest; #[rstest] #[case(Hex::ZERO)] #[case(Hex::new(1, -1))] #[case(Hex::new(-2, 2))] fn recursive_backtrack_start_visited(#[case] start: Hex) { let mut maze = create_hex_maze(3); let mut rng = StdRng::seed_from_u64(12345); let mut visited = HashSet::new(); recursive_backtrack(&mut maze, start, &mut visited, &mut rng); assert!(visited.contains(&start), "Start position should be visited"); } #[rstest] #[case(Hex::ZERO)] #[case(Hex::new(1, -1))] #[case(Hex::new(-2, 2))] fn recursive_backtrack_walls_removed(#[case] start: Hex) { let mut maze = create_hex_maze(3); let mut rng = StdRng::seed_from_u64(12345); let mut visited = HashSet::new(); recursive_backtrack(&mut maze, start, &mut visited, &mut rng); for &pos in maze.keys() { let walls = maze.get_walls(&pos).unwrap(); assert!( walls.count() < 6, "At least one wall should be removed for each tile" ); } } #[rstest] #[case(Hex::ZERO)] #[case(Hex::new(1, -1))] #[case(Hex::new(-2, 2))] fn recursive_backtrack_connectivity(#[case] start: Hex) { let mut maze = create_hex_maze(3); let mut rng = StdRng::seed_from_u64(12345); let mut visited = HashSet::new(); recursive_backtrack(&mut maze, start, &mut visited, &mut rng); let mut to_visit = vec![start]; let mut connected = HashSet::new(); while let Some(current) = to_visit.pop() { if !connected.insert(current) { continue; } for dir in EdgeDirection::ALL_DIRECTIONS { let neighbor = current + dir; if let Some(walls) = maze.get_walls(¤t) { if !walls.contains(&dir) && maze.get(&neighbor).is_some() { to_visit.push(neighbor); } } } } assert_eq!(connected.len(), maze.len(), "All tiles should be connected"); } }