puzzle-utils 0.3.0

Puzzle solving utilities
Documentation
use crate::{structures::disjoint_set::DisjointSet, util::choose_random};

use super::{MazeDirection, MazeNode};

pub fn recursive_backtrack(width: usize, height: usize) -> Vec<MazeNode> {
    let mut maze = vec![MazeNode::new(); width * height];
    let mut connections = DisjointSet::with_size(width * height);

    let mut path = vec![0];
    let mut can_visit = vec![
        vec![
            MazeDirection::Right,
            MazeDirection::Down,
            MazeDirection::Left,
            MazeDirection::Up
        ];
        width * height
    ];
    while !path.is_empty() {
        let coordinate = path[path.len() - 1];

        match visit_next(
            coordinate,
            width,
            height,
            &mut maze,
            &mut connections,
            &mut can_visit[coordinate],
        ) {
            Some(next) => path.push(next),
            None => {
                path.pop();
            }
        };
    }

    maze
}

fn visit_next(
    coordinate: usize,
    width: usize,
    height: usize,
    maze: &mut [MazeNode],
    connections: &mut DisjointSet,
    visitable: &mut Vec<MazeDirection>,
) -> Option<usize> {
    while !visitable.is_empty() {
        let rand_idx = choose_random(visitable).expect("visitable should be non-empty");
        match rand_idx {
            MazeDirection::Right => {
                if (coordinate % width) == (width - 1) {
                    continue;
                }

                let next = coordinate + 1;

                if connections
                    .common_set(coordinate, next)
                    .expect("coordinate and next should be present in the set")
                {
                    continue;
                }

                maze[coordinate].right = false;
                connections
                    .union(coordinate, next)
                    .expect("coordinate and next should be present in the set");
                return Some(next);
            }
            MazeDirection::Down => {
                if coordinate >= width * (height - 1) {
                    continue;
                }

                let next = coordinate + width;

                if connections
                    .common_set(coordinate, next)
                    .expect("coordinate and next should be present in the set")
                {
                    continue;
                }

                maze[coordinate].down = false;
                connections
                    .union(coordinate, next)
                    .expect("coordinate and next should be present in the set");
                return Some(next);
            }
            MazeDirection::Left => {
                if coordinate % width == 0 {
                    continue;
                }

                let next = coordinate - 1;

                if connections
                    .common_set(coordinate, next)
                    .expect("coordinate and next should be present in the set")
                {
                    continue;
                }

                maze[next].right = false;
                connections
                    .union(coordinate, next)
                    .expect("coordinate and next should be present in the set");
                return Some(next);
            }
            MazeDirection::Up => {
                if coordinate < width {
                    continue;
                }

                let next = coordinate - width;

                if connections
                    .common_set(coordinate, next)
                    .expect("coordinate and next should be present in the set")
                {
                    continue;
                }

                maze[next].down = false;
                connections
                    .union(coordinate, next)
                    .expect("coordinate and next should be present in the set");
                return Some(next);
            }
        }
    }

    None
}