minicrossterm/examples/Crossterm 0.2.2 - New Version (Not finished)/program_examples/first_depth_search/algorithm.rs

157 lines
4.6 KiB
Rust

//! Implementation of the first depth search algorithm
use super::variables::{Direction, Position};
use super::messages::END_MESSAGE;
use super::map::Map;
use crossterm::style::Color;
use crossterm::Crossterm;
use super::rand;
use super::rand::distributions::{IndependentSample, Range};
use std::io::{stdout, Write};
use std::{thread, time};
pub struct FirstDepthSearch<'crossterm>
{
direction: Direction,
map: Map,
stack: Vec<Position>,
root_pos: Position,
is_terminated: bool,
crossterm: &'crossterm Crossterm
}
impl<'crossterm> FirstDepthSearch<'crossterm>
{
pub fn new(map: Map, start_pos: Position, crossterm: &'crossterm Crossterm) -> FirstDepthSearch<'crossterm>
{
FirstDepthSearch
{
direction: Direction::Up,
map: map,
stack: Vec::new(),
root_pos: start_pos,
is_terminated: false,
crossterm: crossterm,
}
}
pub fn start(&mut self)
{
self.is_terminated = false;
// push first position on the stack
self.stack.push(self.root_pos);
let mut cursor = self.crossterm.cursor();
cursor.hide();
// loop until there are now items left in the stack.
loop {
if self.stack.len() == 0
{
break;
}
self.choose_random_neighbor();
if self.is_terminated
{
break;
}
self.update_position();
let cell = self.crossterm.paint(" ").on(Color::Blue);
let pos = self.root_pos.clone();
let x = pos.x as u16;
let y = pos.y as u16;
cursor.goto(x,y).print(cell);
::std::io::stdout().flush();
thread::sleep(time::Duration::from_millis(2));
}
}
/// With this function we are choosing an random neighbor that we havent visited yet.
fn choose_random_neighbor(&mut self)
{
let mut available_directions: Vec<Direction> = Vec::with_capacity(4);
// check every direction if the direction is not visited we can add it to the list.
// note that if the y or x is 0 that we don't want to subtract because we get an subtract overflow.
if self.root_pos.y != 0 && !self.map.is_cell_visited(self.root_pos.x, self.root_pos.y - 1)
{
available_directions.push(Direction::Up)
}
if !&self.map.is_cell_visited(self.root_pos.x, self.root_pos.y + 1)
{
available_directions.push(Direction::Down)
}
if self.root_pos.x != 0 && !self.map.is_cell_visited(self.root_pos.x - 1, self.root_pos.y)
{
available_directions.push(Direction::Left)
}
if !&self.map.is_cell_visited(self.root_pos.x + 1, self.root_pos.y)
{
available_directions.push(Direction::Right)
}
let directions_count = available_directions.len();
// if there are no directions left we need to backtrack until we find directions to go to.
if directions_count != 0
{
let step = Range::new(0, directions_count);
let mut rng = rand::thread_rng();
let choice = step.ind_sample(&mut rng);
// set the current direction to the new random generated direction.
self.direction = available_directions[choice];
}
else {
self.find_first_possible_direction();
}
}
/// Find direction to go to if there is no direction pop the current position of the stack for back tracking to the previous position.
fn find_first_possible_direction(&mut self)
{
// if there are no elements left in the stack that means we have visited all cell and we van terminate the program.
if let Some(previous_cell) = &self.stack.pop()
{
// update root pos to previous cell and continue searching for new neighbours
self.root_pos = *previous_cell;
self.choose_random_neighbor();
}
else {
self.is_terminated = true;
}
}
/// update the root position to the new direction we went in
fn update_position(&mut self)
{
match self.direction
{
Direction::Up => self.root_pos.y -= 1,
Direction::Down => self.root_pos.y += 1,
Direction::Left => self.root_pos.x -= 1,
Direction::Right => self.root_pos.x += 1,
_ => panic!()
};
self.map.set_visited(self.root_pos.x, self.root_pos.y);
self.stack.push(self.root_pos);
}
}