backtracking/lib.rs
1//! Find solutions with backtracking.
2
3/// A problem to be tackled with backtracking. Used by the [`Solutions`] iterator which can find
4/// solutions for ypes implementing [`Problem`].
5///
6/// Technically any problem solvable with backtracking would not need to keep any state, apart from
7/// the initial state, since all the essential input is part of the history. An empty implementation
8/// for [`Problem::what_if`] and [`Problem::undo`] would always be sufficient. Given the large
9/// search space for many of these problems, though, real world implementation are likely to keep
10/// some cached state, which is updated in these methods.
11pub trait Problem {
12 /// Describes a decision made in a problem state leading to a new candidate for a solution. E.g.
13 /// which field to jump to in a knights journey problem or which digit to write into a cell for
14 /// a sudoku puzzle.
15 type Posibility: Copy;
16 /// Final state we are interested in. E.g. The history of moves made for a knights journey, or
17 /// the final distribution of digits in the cells of a sudoku puzzle.
18 type Solution;
19
20 /// Extends `possibilities` with a set of decisions to be considered next. Implementations may
21 /// assume that the `possibilities` is empty if invoked through the `Solutions` iterator.
22 fn extend_possibilities(
23 &self,
24 possibilities: &mut Vec<Self::Posibility>,
25 history: &[Self::Posibility],
26 );
27
28 /// Undo the last decision made. If invoked by the [`Solutions`] iterator `last` is to be
29 /// guaranteed, to be the last decision made with [`do`]
30 fn undo(&mut self, last: &Self::Posibility, history: &[Self::Posibility]);
31
32 /// Update internal caches to reflect a scenario in which we would decide to execute the given
33 /// possibility.
34 fn what_if(&mut self, decision: Self::Posibility);
35
36 /// Check if the candidate state we are looking at is a solution to our probelm. If so extract
37 /// the information we are interessted in.
38 fn is_solution(&self, history: &[Self::Posibility]) -> Option<Self::Solution>;
39}
40
41/// An iterator performing backtracking to find solutions to a problem.
42pub struct Solutions<P: Problem> {
43 decisions: Vec<P::Posibility>,
44 open: Vec<Candidate<P::Posibility>>,
45 /// Keeps track of the decisions, which yielded the current problem state, starting from the
46 /// initial state.
47 history: Vec<P::Posibility>,
48 current: P,
49}
50
51impl<G: Problem> Solutions<G> {
52 pub fn new(init: G) -> Self {
53 let mut possible_moves = Vec::new();
54 init.extend_possibilities(&mut possible_moves, &[]);
55 let open = possible_moves
56 .iter()
57 .map(|pos| Candidate {
58 count: 1,
59 possibility: *pos,
60 })
61 .collect();
62 Self {
63 decisions: possible_moves,
64 open,
65 history: Vec::new(),
66 current: init,
67 }
68 }
69}
70
71impl<G: Problem> Iterator for Solutions<G> {
72 type Item = G::Solution;
73
74 fn next(&mut self) -> Option<Self::Item> {
75 while let Some(Candidate {
76 count,
77 possibility: mov,
78 }) = self.open.pop()
79 {
80 // Unroll all the moves until our current state is identical with the one which we
81 // had once we put that mov into the open list. We want to be one move behind so
82 // we need to play the move in order to get the desired state
83 for _ in 0..self.history.len() as i32 - count + 1 {
84 let last = self.history.pop().unwrap();
85 self.current.undo(&last, &self.history);
86 }
87
88 // We advance one move deeper into the search tree
89 self.current.what_if(mov);
90 self.history.push(mov);
91
92 // Emit solution
93 if let Some(solution) = self.current.is_solution(&self.history) {
94 return Some(solution);
95 }
96
97 // Extend search tree
98 self.decisions.clear();
99 self.current
100 .extend_possibilities(&mut self.decisions, &self.history);
101 self.open
102 .extend(self.decisions.iter().map(|&position| Candidate {
103 count: count + 1,
104 possibility: position,
105 }))
106 }
107 None
108 }
109}
110
111struct Candidate<P> {
112 /// Counts the number of turns made to get to this candidate. We keep track of this so we can
113 /// call undo the appropriate number of types, if we roll back to an earlier state.
114 count: i32,
115 /// Possibility which will lead to this candidate
116 possibility: P,
117}