Task

For a maze generated by this task, write a function
that finds (and displays) the shortest path between two cells.



Note that because these mazes are generated by the Depth-first search algorithm, they contain no circular paths,
and a simple depth-first tree search can be used.




use rand::{thread_rng, Rng, rngs::ThreadRng};
 
const WIDTH: usize = 16;
const HEIGHT: usize = 16;
 
#[derive(Clone, Copy, PartialEq)]
struct Cell {
    col: usize,
    row: usize,
}
 
impl Cell {
    fn from(col: usize, row: usize) -> Cell {
        Cell {col, row}
    }
}
 
struct Maze {
    cells: [[bool; HEIGHT]; WIDTH],         //cell visited/non visited
    walls_h: [[bool; WIDTH]; HEIGHT + 1],   //horizontal walls existing/removed
    walls_v: [[bool; WIDTH + 1]; HEIGHT],   //vertical walls existing/removed
    thread_rng: ThreadRng,                  //Random numbers generator
}
 
impl Maze {
 
    ///Inits the maze, with all the cells unvisited and all the walls active
    fn new() -> Maze {
        Maze { 
            cells: [[true; HEIGHT]; WIDTH], 
            walls_h: [[true; WIDTH]; HEIGHT + 1],
            walls_v: [[true; WIDTH + 1]; HEIGHT],
            thread_rng: thread_rng(),
        }
    }
 
    ///Randomly chooses the starting cell
    fn first(&mut self) -> Cell {
        Cell::from(self.thread_rng.gen_range(0, WIDTH), self.thread_rng.gen_range(0, HEIGHT))
    }
 
    ///Opens the enter and exit doors
    fn open_doors(&mut self) {
        let from_top: bool = self.thread_rng.gen();
        let limit = if from_top { WIDTH } else { HEIGHT };
        let door = self.thread_rng.gen_range(0, limit);
        let exit = self.thread_rng.gen_range(0, limit);
        if from_top { 
            self.walls_h[0][door] = false;
            self.walls_h[HEIGHT][exit] = false;
        } else {
            self.walls_v[door][0] = false;
            self.walls_v[exit][WIDTH] = false;
        }
    }
 
    ///Removes a wall between the two Cell arguments
    fn remove_wall(&mut self, cell1: &Cell, cell2: &Cell) {
        if cell1.row == cell2.row {
            self.walls_v[cell1.row][if cell1.col > cell2.col { cell1.col } else { cell2.col }] = false;
        } else { 
            self.walls_h[if cell1.row > cell2.row { cell1.row } else { cell2.row }][cell1.col] = false;
        };
    }
 
    ///Returns a random non-visited neighbor of the Cell passed as argument
    fn neighbor(&mut self, cell: &Cell) -> Option<Cell> {
        self.cells[cell.col][cell.row] = false;
        let mut neighbors = Vec::new();
        if cell.col > 0 && self.cells[cell.col - 1][cell.row] { neighbors.push(Cell::from(cell.col - 1, cell.row)); }
        if cell.row > 0 && self.cells[cell.col][cell.row - 1] { neighbors.push(Cell::from(cell.col, cell.row - 1)); }
        if cell.col < WIDTH - 1 && self.cells[cell.col + 1][cell.row] { neighbors.push(Cell::from(cell.col + 1, cell.row)); }
        if cell.row < HEIGHT - 1 && self.cells[cell.col][cell.row + 1] { neighbors.push(Cell::from(cell.col, cell.row + 1)); }
        if neighbors.is_empty() {
            None
        } else {
            let next = neighbors.get(self.thread_rng.gen_range(0, neighbors.len())).unwrap();
            self.remove_wall(cell, next);
            Some(*next)
        }
    }
 
    ///Builds the maze (runs the Depth-first search algorithm)
    fn build(&mut self) {
        let mut cell_stack: Vec<Cell> = Vec::new();
        let mut next = self.first();
        loop {
            while let Some(cell) = self.neighbor(&next) {
                cell_stack.push(cell);
                next = cell;
            }
            match cell_stack.pop() {
                Some(cell) => next = cell,
                None => break,
            }
        }
    }
 
    ///MAZE SOLVING: Find the starting cell of the solution
    fn solution_first(&self) -> Option<Cell> {
        for (i, wall) in self.walls_h[0].iter().enumerate() {
            if !wall {
                return Some(Cell::from(i, 0));
            }
        }
        for (i, wall) in self.walls_v.iter().enumerate() {
            if !wall[0] {
                return Some(Cell::from(0, i));
            }
        }
        None
    }
 
    ///MAZE SOLVING: Find the last cell of the solution
    fn solution_last(&self) -> Option<Cell> {
        for (i, wall) in self.walls_h[HEIGHT].iter().enumerate() {
            if !wall {
                return Some(Cell::from(i, HEIGHT - 1));
            }
        }
        for (i, wall) in self.walls_v.iter().enumerate() {
            if !wall[WIDTH] {
                return Some(Cell::from(WIDTH - 1, i));
            }
        }
        None
    }
 
    ///MAZE SOLVING: Get the next candidate cell
    fn solution_next(&mut self, cell: &Cell) -> Option<Cell> {
        self.cells[cell.col][cell.row] = false;
        let mut neighbors = Vec::new();
        if cell.col > 0 && self.cells[cell.col - 1][cell.row] && !self.walls_v[cell.row][cell.col] { neighbors.push(Cell::from(cell.col - 1, cell.row)); }
        if cell.row > 0 && self.cells[cell.col][cell.row - 1] && !self.walls_h[cell.row][cell.col] { neighbors.push(Cell::from(cell.col, cell.row - 1)); }
        if cell.col < WIDTH - 1 && self.cells[cell.col + 1][cell.row] && !self.walls_v[cell.row][cell.col + 1] { neighbors.push(Cell::from(cell.col + 1, cell.row)); }
        if cell.row < HEIGHT - 1 && self.cells[cell.col][cell.row + 1] && !self.walls_h[cell.row + 1][cell.col] { neighbors.push(Cell::from(cell.col, cell.row + 1)); }
        if neighbors.is_empty() {
            None
        } else {
            let next = neighbors.get(self.thread_rng.gen_range(0, neighbors.len())).unwrap();
            Some(*next)
        }
    }
 
    ///MAZE SOLVING: solve the maze
    ///Uses self.cells to store the solution cells (true)
    fn solve(&mut self) {
        self.cells = [[true; HEIGHT]; WIDTH];
        let mut solution: Vec<Cell> = Vec::new();
        let mut next = self.solution_first().unwrap();
        solution.push(next);
        let last = self.solution_last().unwrap();
        'main: loop {
            while let Some(cell) = self.solution_next(&next) {
                solution.push(cell);
                if cell == last {
                    break 'main;
                }
                next = cell;
            }
            solution.pop().unwrap();
            next = *solution.last().unwrap();
        }
        self.cells = [[false; HEIGHT]; WIDTH];
        for cell in solution {
            self.cells[cell.col][cell.row] = true;
        }
    }
 
    ///MAZE SOLVING: Ask if cell is part of the solution (cells[col][row] == true)
    fn is_solution(&self, col: usize, row: usize) -> bool {
        self.cells[col][row]
    }
 
    ///Displays a wall
    ///MAZE SOLVING: Leave space for printing '*' if cell is part of the solution 
    /// (only when painting vertical walls)
    /// 
    // fn paint_wall(h_wall: bool, active: bool) {
    //     if h_wall {
    //         print!("{}", if active { "+---" } else { "+   " });
    //     } else {
    //         print!("{}", if active { "|   " } else { "    " });
    //     }
    // }
    fn paint_wall(h_wall: bool, active: bool, with_solution: bool) {
        if h_wall {
            print!("{}", if active { "+---" } else { "+   " });
        } else {
            print!("{}{}", if active { "|" } else { " " }, if with_solution { "" } else { "   " });
        }
    }
 
    ///MAZE SOLVING: Paint * if cell is part of the solution
    fn paint_solution(is_part: bool) {
        print!("{}", if is_part { " * " } else {"   "});
    }
 
    ///Displays a final wall for a row
    fn paint_close_wall(h_wall: bool) {
        if h_wall { println!("+") } else { println!() }
    }
 
    ///Displays a whole row of walls
    ///MAZE SOLVING: Displays a whole row of walls and, optionally, the included solution cells.
    // fn paint_row(&self, h_walls: bool, index: usize) {
    //     let iter = if h_walls { self.walls_h[index].iter() } else { self.walls_v[index].iter() };
    //     for &wall in iter {
    //         Maze::paint_wall(h_walls, wall);
    //     }
    //     Maze::paint_close_wall(h_walls);
    // }
    fn paint_row(&self, h_walls: bool, index: usize, with_solution: bool) {
        let iter = if h_walls { self.walls_h[index].iter() } else { self.walls_v[index].iter() };
        for (col, &wall) in iter.enumerate() {
            Maze::paint_wall(h_walls, wall, with_solution);
            if !h_walls && with_solution && col < WIDTH  {
                Maze::paint_solution(self.is_solution(col, index));
            }
        }
        Maze::paint_close_wall(h_walls);
    }
 
    ///Paints the maze
    ///MAZE SOLVING: Displaying the solution is an option
    // fn paint(&self) {
    //     for i in 0 .. HEIGHT {
    //         self.paint_row(true, i);
    //         self.paint_row(false, i);
    //     }
    //     self.paint_row(true, HEIGHT);
    // }
    fn paint(&self, with_solution: bool) {
        for i in 0 .. HEIGHT {
            self.paint_row(true, i, with_solution);
            self.paint_row(false, i, with_solution);
        }
        self.paint_row(true, HEIGHT, with_solution);
    }
}
 
fn main() {
    let mut maze = Maze::new();
    maze.build();
    maze.open_doors();
 
    println!("The maze:");
    maze.paint(false);
 
    maze.solve();
    println!("The maze, solved:");
    maze.paint(true);
}