diff --git a/src/algorithm.rs b/src/algorithm.rs index 5d04763..ad7cc32 100644 --- a/src/algorithm.rs +++ b/src/algorithm.rs @@ -4,86 +4,7 @@ //! Already existing implementations of that trait can be found in the [`crate::implementations`] module. //! -use std::{ - collections::{HashMap, HashSet}, - thread, - time::Duration, -}; - -use crate::{Maze, Pos}; - -/// A guess to pass to the current [`Executor`] at the end of every `progress` call. -pub struct Guess(Vec); - -/// An insight given to the [`Algorithm`] on every `progress` call. -/// On the first time about the starting point and every consecutive call about the tail of the previous guess. -pub struct Insight<'p> { - position: Pos, - paths: &'p [Pos], -} - -impl<'p> Insight<'p> { - fn new(position: Pos, paths: &'p [Pos]) -> Self { - Self { paths, position } - } - - fn from_position(position: Pos, maze: &'p Maze) -> Self { - let paths = maze.paths_from(position); - Self::new(position, paths) - } - - /// The position of the insight. - pub fn position(&self) -> Pos { - self.position - } - - /// the paths from that position. - pub fn paths(&self) -> &[Pos] { - self.paths - } -} - -/// A context given to the [`Algorithm`] on every `progress` call, provide informations about the maze and method to create a [`Guess`]. -pub struct Context<'m> { - maze: &'m Maze, -} - -impl<'m> Context<'m> { - fn new(maze: &'m Maze) -> Self { - Self { maze } - } - - /// Constructor for [`Guess`]. - /// Takes a path, that is a vector of positions from the starting point to the position to discover on the next call to `progress`. - pub fn guess(&self, pos: Vec) -> Guess { - Guess(pos) - } - - /// Returns the position of the `start` of the [`Maze`]. - pub fn start(&self) -> Pos { - self.maze.start() - } - - /// Returns the position of the `end` of the [`Maze`]. - pub fn end(&self) -> Pos { - self.maze.end() - } - - /// Returns the `width` of the [`Maze`]. - pub fn width(&self) -> isize { - self.maze.width() - } - - /// Returns the `height` of the [`Maze`]. - pub fn height(&self) -> isize { - self.maze.width() - } - - /// Returns a tuple containing both the `width` and `height` of the [`Maze`]. - pub fn size(&self) -> (isize, isize) { - self.maze.size() - } -} +use crate::{Context, Guess, Insight}; /// Trait encapsulating the behavior of an algorithm solving mazes. /// Implementing this trait is done by providing a `progress` method which gets called iteratively on each steps of a [`Maze`] resolution. @@ -93,78 +14,3 @@ pub trait Algorithm { /// `ctx` is a view on the [`Maze`], useful for accessing properties of the maze. fn progress(&mut self, insight: &Insight, ctx: &mut Context) -> Guess; } - -/// A structure holding a [`Maze`] and iteratively solving it with a provided [`Algorithm`]. -pub struct Executor -where - Algo: Algorithm, -{ - maze: Maze, - algorithm: Algo, -} - -impl Executor -where - A: Algorithm, -{ - /// Constructor. - pub fn new(maze: Maze, algorithm: A) -> Self { - Self { maze, algorithm } - } - - /// Submit the maze to the [`Algorithm`] and iteratively progress through the maze driven by said algorithm. - pub fn run(&mut self) { - let Self { maze, algorithm } = self; - let mut insight = Insight::from_position(maze.start(), &maze); - let mut tick = 0; - let mut tried = HashSet::new(); - loop { - let mut context = Context::new(maze); - let Guess(guess) = algorithm.progress(&insight, &mut context); - // TODO: - // - extract metrics from the context - // - check if path is actually a path - guess.iter().for_each(|&p| { - tried.insert(p); - }); - let tail = *guess.last().expect("returned an empty path"); - - // draw - Self::draw(maze, &tried, tick, &guess); - thread::sleep(Duration::from_millis(100)); - tick += 1; - - // check for next iteration - if maze.is_end(tail) { - break; - } else { - insight = Insight::from_position(tail, maze) - } - } - } - - fn draw(maze: &Maze, tried: &HashSet, tick: usize, path: &Vec) { - let mut overlay = HashMap::new(); - for position in tried { - overlay.insert(*position, 'T'); - } - - for position in path { - overlay.insert(*position, '#'); - } - - overlay.insert(*path.last().unwrap(), 'G'); - - let text = maze.display(Some(overlay)); - - // DIRTY! - // print the frame on top of the previous one - if tick > 0 { - let count = text.lines().count() + 1; - let up = termion::cursor::Up(count as u16); - print!("{up}") - } - - print!("tick {tick}:\n{text}\n"); - } -} diff --git a/src/executor.rs b/src/executor.rs new file mode 100644 index 0000000..14d9173 --- /dev/null +++ b/src/executor.rs @@ -0,0 +1,155 @@ +use std::{ + collections::{HashMap, HashSet}, + thread, + time::Duration, +}; + +use crate::{Algorithm, Maze, Pos}; + +/// A guess to pass to the current [`Executor`] at the end of every `progress` call. +pub struct Guess(Vec); + +/// An insight given to the [`Algorithm`] on every `progress` call. +/// On the first time about the starting point and every consecutive call about the tail of the previous guess. +pub struct Insight<'p> { + position: Pos, + paths: &'p [Pos], +} + +impl<'p> Insight<'p> { + fn new(position: Pos, paths: &'p [Pos]) -> Self { + Self { paths, position } + } + + fn from_position(position: Pos, maze: &'p Maze) -> Self { + let paths = maze.paths_from(position); + Self::new(position, paths) + } + + /// The position of the insight. + pub fn position(&self) -> Pos { + self.position + } + + /// the paths from that position. + pub fn paths(&self) -> &[Pos] { + self.paths + } +} + +/// A context given to the [`Algorithm`] on every `progress` call, provide informations about the maze and method to create a [`Guess`]. +pub struct Context<'m> { + maze: &'m Maze, +} + +impl<'m> Context<'m> { + fn new(maze: &'m Maze) -> Self { + Self { maze } + } + + /// Constructor for [`Guess`]. + /// Takes a path, that is a vector of positions from the starting point to the position to discover on the next call to `progress`. + pub fn guess(&self, pos: Vec) -> Guess { + Guess(pos) + } + + /// Returns the position of the `start` of the [`Maze`]. + pub fn start(&self) -> Pos { + self.maze.start() + } + + /// Returns the position of the `end` of the [`Maze`]. + pub fn end(&self) -> Pos { + self.maze.end() + } + + /// Returns the `width` of the [`Maze`]. + pub fn width(&self) -> isize { + self.maze.width() + } + + /// Returns the `height` of the [`Maze`]. + pub fn height(&self) -> isize { + self.maze.width() + } + + /// Returns a tuple containing both the `width` and `height` of the [`Maze`]. + pub fn size(&self) -> (isize, isize) { + self.maze.size() + } +} + +/// A structure holding a [`Maze`] and iteratively solving it with a provided [`Algorithm`]. +pub struct Executor +where + Algo: Algorithm, +{ + maze: Maze, + algorithm: Algo, +} + +impl Executor +where + A: Algorithm, +{ + /// Constructor. + pub fn new(maze: Maze, algorithm: A) -> Self { + Self { maze, algorithm } + } + + /// Submit the maze to the [`Algorithm`] and iteratively progress through the maze driven by said algorithm. + pub fn run(&mut self) { + let Self { maze, algorithm } = self; + let mut insight = Insight::from_position(maze.start(), &maze); + let mut tick = 0; + let mut tried = HashSet::new(); + loop { + let mut context = Context::new(maze); + let Guess(guess) = algorithm.progress(&insight, &mut context); + // TODO: + // - extract metrics from the context + // - check if path is actually a path + guess.iter().for_each(|&p| { + tried.insert(p); + }); + let tail = *guess.last().expect("returned an empty path"); + + // draw + Self::draw(maze, &tried, tick, &guess); + thread::sleep(Duration::from_millis(100)); + tick += 1; + + // check for next iteration + if maze.is_end(tail) { + break; + } else { + insight = Insight::from_position(tail, maze) + } + } + } + + fn draw(maze: &Maze, tried: &HashSet, tick: usize, path: &Vec) { + let mut overlay = HashMap::new(); + for position in tried { + overlay.insert(*position, 'T'); + } + + for position in path { + overlay.insert(*position, '#'); + } + + overlay.insert(*path.last().unwrap(), 'G'); + + let text = maze.display(Some(overlay)); + + // DIRTY! + // print the frame on top of the previous one + if tick > 0 { + let count = text.lines().count() + 1; + let up = termion::cursor::Up(count as u16); + print!("{up}") + } + + print!("tick {tick}:\n{text}\n"); + } +} diff --git a/src/implementations.rs b/src/implementations.rs index 893fa4e..1f5d035 100644 --- a/src/implementations.rs +++ b/src/implementations.rs @@ -12,7 +12,7 @@ pub use depth_first::DepthFirst; #[test] fn depth_first() { - use crate::{algorithm::Executor, generate}; + use crate::{generate, Executor}; let algorithm = DepthFirst::new(); let maze = generate(20, 20); let mut executor = Executor::new(maze, algorithm); @@ -21,7 +21,7 @@ fn depth_first() { #[test] fn breath_first() { - use crate::{algorithm::Executor, generate}; + use crate::{generate, Executor}; let algorithm = BreathFirst::new(); let maze = generate(20, 20); let mut executor = Executor::new(maze, algorithm); diff --git a/src/implementations/breath_first.rs b/src/implementations/breath_first.rs index f1ed559..c394811 100644 --- a/src/implementations/breath_first.rs +++ b/src/implementations/breath_first.rs @@ -1,9 +1,6 @@ use std::collections::{HashSet, VecDeque}; -use crate::{ - algorithm::{Context, Guess, Insight}, - Algorithm, Pos, -}; +use crate::{Algorithm, Context, Guess, Insight, Pos}; /// [`Algorithm`] traversing the [`crate::Maze`] as a common graph. /// Storing each possible paths form shortest to longest and extending the shortest ones first. diff --git a/src/implementations/depth_first.rs b/src/implementations/depth_first.rs index 8912829..df87e86 100644 --- a/src/implementations/depth_first.rs +++ b/src/implementations/depth_first.rs @@ -1,9 +1,6 @@ use std::collections::HashSet; -use crate::{ - algorithm::{Context, Guess, Insight}, - Algorithm, Pos, -}; +use crate::{Algorithm, Context, Guess, Insight, Pos}; /// Frame of the stack used by a [`DepthFirst`] to retain its path and possible branches. pub struct Frame { diff --git a/src/lib.rs b/src/lib.rs index a154335..ad82e27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,10 +7,12 @@ //! mod algorithm; +mod executor; pub mod implementations; mod labyrinth; mod position; -pub use algorithm::{Algorithm, Executor}; +pub use algorithm::Algorithm; +pub use executor::{Context, Executor, Guess, Insight}; pub use labyrinth::{generator::generate, Maze}; pub use position::Pos;