1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! ## Executor
//!
//! This module contains the definition of an [`Executor`], used to run an [`Algorithm`] and have a graphical output in the terminal.
//! This type is supposed to be created using the builder pattern (c.f. [`Executor`]`::build`).

use std::{
    collections::{HashMap, HashSet},
    thread,
    time::Duration,
};

use crate::{Algorithm, Maze, Pos};

use self::builder::{
    maze_state::{BuildableMazeState, Unprovided},
    new_builder, ExecutorBuilder,
};

/// A guess to pass to the current [`Executor`] at the end of every `progress` call.
pub struct Guess(Vec<Pos>);

/// 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<Pos>) -> 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()
    }
}

mod builder;

/// A structure holding a [`Maze`] and iteratively solving it with a provided [`Algorithm`].
pub struct Executor<Algo>
where
    Algo: Algorithm,
{
    delay: Duration,
    maze: Maze,
    algorithm: Algo,
}

impl<A> Executor<A>
where
    A: Algorithm,
{
    /// Constructor.
    fn new(maze: Maze, algorithm: A, delay: Duration) -> Self {
        Self {
            maze,
            algorithm,
            delay,
        }
    }

    pub fn build<'f, F, MS>(algorithm: A, builder: F) -> Self
    where
        MS: BuildableMazeState,
        F: Fn(ExecutorBuilder<Unprovided>) -> ExecutorBuilder<MS>,
    {
        let operation = builder;
        let builder = (operation)(new_builder());
        let (maze, delay) = builder.build();
        Self::new(maze, algorithm, delay)
    }

    /// 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,
            delay,
        } = 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(*delay);
            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<Pos>, tick: usize, path: &Vec<Pos>) {
        let mut overlay = HashMap::new();
        for position in tried {
            overlay.insert(*position, '░');
        }
        for position in path {
            overlay.insert(*position, '█');
        }
        overlay.insert(maze.start(), 'S');
        overlay.insert(maze.end(), 'E');
        overlay.insert(*path.last().unwrap(), 'G');

        let grid = maze.display(Some(overlay));
        let text = format!("tick {tick}:\n{grid}\n");

        // DIRTY!
        // print the frame on top of the previous one
        if tick > 0 {
            let count = text.lines().count();
            let up = termion::cursor::Up(count as u16);
            print!("{up}")
        }

        print!("{text}");
    }
}