September Morn Update
This commit is contained in:
parent
4e2d22a71d
commit
aac6731f2c
6856 changed files with 141342 additions and 21127 deletions
|
|
@ -1,3 +1,4 @@
|
|||
-- demo\rosetta\Maze_solving.exw
|
||||
constant w = 11, h = 8
|
||||
|
||||
sequence wall = join(repeat("+",w+1),"---")&"\n",
|
||||
|
|
|
|||
253
Task/Maze-solving/Rust/maze-solving.rust
Normal file
253
Task/Maze-solving/Rust/maze-solving.rust
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue