lisudoku_solver/solver/
checker.rs

1use crate::solver::Solver;
2use crate::types::{Area, CellPosition, KropkiDotType, KropkiDot, Arrow};
3use std::collections::HashSet;
4use std::mem::swap;
5use super::logical_solver::{technique::Technique, top_bottom_candidates::TopBottomCandidates};
6
7impl Solver {
8  pub fn check_solved(&self) -> bool {
9    self.check_grid_valid(false)
10  }
11
12  pub fn check_partially_solved(&self) -> bool {
13    self.check_grid_valid(true)
14  }
15
16  fn check_grid_valid(&self, allow_empty: bool) -> bool {
17    for CellPosition { row, col } in self.get_area_cells(&Area::Grid) {
18      let value = self.grid[row][col];
19      if value == 0 {
20        if !allow_empty {
21          return false
22        }
23      } else if value < 1 || value > self.constraints.grid_size as u32 {
24        return false
25      }
26    }
27
28    for area in self.get_all_areas(true, true, true) {
29      if !self.check_area_valid(&area) {
30        return false
31      }
32    }
33
34    for arrow in &self.constraints.arrows {
35      if !self.check_arrow_valid(arrow) {
36        return false
37      }
38    }
39
40    if self.constraints.anti_knight && !self.check_anti_knight_valid() {
41      return false
42    }
43
44    if self.constraints.anti_king && !self.check_anti_king_valid() {
45      return false
46    }
47
48    if !self.check_odd_cells() {
49      return false
50    }
51
52    if !self.check_even_cells() {
53      return false
54    }
55
56    if self.constraints.top_bottom && !self.check_top_bottom_valid() {
57      return false
58    }
59
60    true
61  }
62
63  fn check_area_valid(&self, area: &Area) -> bool {
64    match area {
65      &Area::Row(_) | &Area::Column(_) | &Area::Region(_) |
66        &Area::PrimaryDiagonal | &Area::SecondaryDiagonal => self.check_area_region_valid(area),
67      &Area::KillerCage(killer_cage_index) => self.check_killer_area_valid(area, killer_cage_index),
68      &Area::Thermo(_) => self.check_thermo_area_valid(area),
69      &Area::KropkiDot(kropki_dot_index) => self.check_kropki_dot_valid(kropki_dot_index),
70      &Area::Grid | &Area::Arrow(_) => unimplemented!(),
71    }
72  }
73
74  fn check_area_region_valid(&self, area: &Area) -> bool {
75    let mut values = HashSet::new();
76    let mut candidates = HashSet::new();
77
78    let area_cells = self.get_area_cells(area);
79    for cell in &area_cells {
80      let value = self.grid[cell.row][cell.col];
81      if value == 0 {
82        candidates.extend(self.compute_cell_candidates(cell));
83        continue
84      }
85      if values.contains(&value) {
86        return false
87      }
88      values.insert(value);
89    }
90
91    candidates.extend(values);
92    // Can't place some value in this area so there is no solution
93    if candidates.len() < area_cells.len() {
94      return false
95    }
96
97    true
98  }
99
100  fn check_thermo_area_valid(&self, area: &Area) -> bool {
101    let mut crt_max_value: u32 = 0;
102
103    for CellPosition { row, col } in self.get_area_cells(area) {
104      let value = self.grid[row][col];
105      if value == 0 {
106        continue
107      }
108      if value <= crt_max_value {
109        return false
110      }
111      crt_max_value = value
112    }
113
114    true
115  }
116
117  fn check_arrow_valid(&self, arrow: &Arrow) -> bool {
118    let (arrow_sum, arrow_full) = self.arrow_arrow_sum(arrow);
119    let (circle_number, circle_full) = self.arrow_circle_number(arrow);
120
121    if arrow_full && circle_full {
122      return arrow_sum == circle_number
123    }
124
125    if circle_full {
126      return arrow_sum <= circle_number
127    }
128
129    true
130  }
131
132  fn check_anti_knight_valid(&self) -> bool {
133    for cell in self.get_area_cells(&Area::Grid) {
134      let value = self.grid[cell.row][cell.col];
135      if value == 0 {
136        continue
137      }
138
139      for peer in self.get_knight_peers(&cell) {
140        let peer_value = self.grid[peer.row][peer.col];
141        if peer_value == 0 {
142          continue
143        }
144        if value == peer_value {
145          return false
146        }
147      }
148    }
149
150    true
151  }
152
153  fn check_anti_king_valid(&self) -> bool {
154    for cell in self.get_area_cells(&Area::Grid) {
155      let value = self.grid[cell.row][cell.col];
156      if value == 0 {
157        continue
158      }
159
160      for peer in self.get_king_peers(&cell) {
161        let peer_value = self.grid[peer.row][peer.col];
162        if peer_value == 0 {
163          continue
164        }
165        if value == peer_value {
166          return false
167        }
168      }
169    }
170
171    true
172  }
173
174  fn check_killer_area_valid(&self, area: &Area, killer_cage_index: usize) -> bool {
175    if !self.check_area_region_valid(area) {
176      return false
177    }
178
179    let mut sum: u32 = 0;
180    let mut any_zero = false;
181    for cell in self.get_area_cells(&area) {
182      let value = self.grid[cell.row][cell.col];
183      if value == 0 {
184        any_zero = true;
185      }
186      sum += value;
187    }
188
189    let killer_cage = &self.constraints.killer_cages[killer_cage_index];
190    if let Some(killer_sum) = killer_cage.sum {
191      if sum != killer_sum && !any_zero || sum > killer_sum {
192        return false
193      }
194    }
195
196    true
197  }
198
199  fn check_kropki_dot_valid(&self, kropki_dot_index: usize) -> bool {
200    let kropki_dot = &self.constraints.kropki_dots[kropki_dot_index];
201    let KropkiDot { dot_type, cell_1, cell_2 } = kropki_dot;
202    let mut value1 = self.grid[cell_1.row][cell_1.col];
203    let mut value2 = self.grid[cell_2.row][cell_2.col];
204    if value1 > value2 {
205      swap(&mut value1, &mut value2);
206    }
207    if value1 == 0 {
208      return true
209    }
210
211    match dot_type {
212      KropkiDotType::Consecutive => {
213        return value1 + 1 == value2
214      },
215      KropkiDotType::Double => {
216        return value1 * 2 == value2
217      },
218      KropkiDotType::Negative => {
219        return value1 + 1 != value2 && value1 * 2 != value2
220      },
221    }
222  }
223
224  fn check_odd_cells(&self) -> bool {
225    self.constraints.odd_cells.iter().all(|cell| {
226      let value = self.grid[cell.row][cell.col];
227      value == 0 || value % 2 == 1
228    })
229  }
230
231  fn check_even_cells(&self) -> bool {
232    self.constraints.even_cells.iter().all(|cell| {
233      let value = self.grid[cell.row][cell.col];
234      value == 0 || value % 2 == 0
235    })
236  }
237
238  fn check_top_bottom_valid(&self) -> bool {
239    TopBottomCandidates::new(true).run(&self).is_empty()
240  }
241}