lisudoku_solver/solver/
checker.rs1use 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 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}