1use crate::solver::Solver;
2use crate::types::{Area, CellPosition, InvalidStateReason, InvalidStateType, KropkiDot, KropkiDotType};
3use itertools::Itertools;
4use std::collections::{HashMap, HashSet};
5use std::mem::swap;
6use super::logical_solver::{technique::Technique, top_bottom_candidates::TopBottomCandidates};
7
8impl Solver {
9 pub fn check_solved(&self) -> (bool, Option<InvalidStateReason>) {
10 self.check_grid_valid(false)
11 }
12
13 pub fn check_partially_solved(&self) -> (bool, Option<InvalidStateReason>) {
14 self.check_grid_valid(true)
15 }
16
17 fn check_grid_valid(&self, allow_empty: bool) -> (bool, Option<InvalidStateReason>) {
18 for cell in self.get_area_cells(&Area::Grid) {
19 let value = self.grid[cell.row][cell.col];
20 if value == 0 {
21 if !allow_empty {
22 return (
23 false,
24 Some(InvalidStateReason {
25 state_type: InvalidStateType::CellEmpty,
26 area: Area::Cell(cell.row, cell.col),
27 values: vec![],
28 }),
29 )
30 }
31
32 let cell_candidates = self.compute_cell_candidates(&cell);
33 if cell_candidates.is_empty() {
34 return (
35 false,
36 Some(InvalidStateReason {
37 state_type: InvalidStateType::CellNoCandidates,
38 area: Area::Cell(cell.row, cell.col),
39 values: vec![],
40 }),
41 )
42 }
43 } else if value < 1 || value > self.constraints.grid_size as u32 {
44 return (
45 false,
46 Some(InvalidStateReason {
47 state_type: InvalidStateType::CellInvalidValue,
48 area: Area::Cell(cell.row, cell.col),
49 values: vec![],
50 }),
51 )
52 }
53 }
54
55 for area in self.get_all_areas(true, true, true, true, true) {
56 let check = self.check_area_valid(&area);
57 if !check.0 {
58 return check
59 }
60 }
61
62 for arrow_index in 0..self.constraints.arrows.len() {
63 let check = self.check_arrow_valid(arrow_index);
64 if !check.0 {
65 return check
66 }
67 }
68
69 if self.constraints.anti_knight {
70 let check = self.check_anti_knight_valid();
71 if !check.0 {
72 return check
73 }
74 }
75
76 if self.constraints.anti_king {
77 let check = self.check_anti_king_valid();
78 if !check.0 {
79 return check
80 }
81 }
82
83 let check = self.check_odd_cells();
84 if !check.0 {
85 return check
86 }
87
88 let check = self.check_even_cells();
89 if !check.0 {
90 return check
91 }
92
93 if self.constraints.top_bottom {
94 let check = self.check_top_bottom_valid();
95 if !check.0 {
96 return check
97 }
98 }
99
100 (true, None)
101 }
102
103 fn check_area_valid(&self, area: &Area) -> (bool, Option<InvalidStateReason>) {
104 match area {
105 &Area::Row(_) | &Area::Column(_) | &Area::Region(_) |
106 &Area::PrimaryDiagonal | &Area::SecondaryDiagonal => self.check_area_region_valid(area),
107 &Area::KillerCage(killer_cage_index) => self.check_killer_area_valid(area, killer_cage_index),
108 &Area::Thermo(_) => self.check_thermo_area_valid(area),
109 &Area::KropkiDot(kropki_dot_index) => self.check_kropki_dot_valid(kropki_dot_index),
110 &Area::Renban(_) => self.check_renban_valid(area),
111 &Area::Palindrome(_) => self.check_palindrome_valid(area),
112 &Area::Grid | &Area::Adhoc(_) | &Area::Cell(_, _) | &Area::Arrow(_) => unimplemented!(),
113 }
114 }
115
116 fn check_area_region_valid(&self, area: &Area) -> (bool, Option<InvalidStateReason>) {
117 let mut values = HashSet::new();
118 let mut candidates = HashSet::new();
119
120 let area_cells = self.get_area_cells(area);
121 for cell in &area_cells {
122 let value = self.grid[cell.row][cell.col];
123 if value == 0 {
124 candidates.extend(self.compute_cell_candidates(cell));
125 continue
126 }
127 if values.contains(&value) {
128 return (
129 false,
130 Some(InvalidStateReason {
131 state_type: InvalidStateType::AreaValueConflict,
132 area: area.clone(),
133 values: vec![value],
134 }),
135 )
136 }
137 values.insert(value);
138 }
139
140 candidates.extend(values);
141
142 if candidates.len() < area_cells.len() {
144 let values = if area_cells.len() == self.constraints.grid_size {
145 self.compute_all_candidates().difference(&candidates).copied().sorted().collect()
146 } else {
147 vec![]
148 };
149
150 return (
151 false,
152 Some(InvalidStateReason {
153 state_type: InvalidStateType::AreaCandidates,
154 area: area.clone(),
155 values,
156 }),
157 )
158 }
159
160 if area_cells.len() == self.constraints.grid_size {
161 let mut value_cells: HashMap<u32, Vec<CellPosition>> = HashMap::new();
165 for cell in self.get_empty_area_cells(area) {
166 for value in self.compute_cell_candidates(&cell) {
167 let entry = value_cells.entry(value).or_insert(vec![]);
168 entry.push(cell);
169 }
170 }
171
172 let mut used_cells_set: HashSet<CellPosition> = HashSet::new();
173 let mut values = vec![];
174 for (value_index, (value, value_cells)) in value_cells.into_iter().sorted_by_key(|e| (e.1.len(), e.0)).enumerate() {
175 used_cells_set.extend(value_cells);
176 values.push(value);
177 if value_index + 1 > used_cells_set.len() {
178 return (
179 false,
180 Some(InvalidStateReason {
181 state_type: InvalidStateType::AreaCandidates,
182 area: area.clone(),
183 values,
184 }),
185 )
186 }
187 }
188 }
189
190 (true, None)
191 }
192
193 fn check_thermo_area_valid(&self, area: &Area) -> (bool, Option<InvalidStateReason>) {
194 let mut crt_max_value: u32 = 0;
195
196 for CellPosition { row, col } in self.get_area_cells(area) {
197 let value = self.grid[row][col];
198 if value == 0 {
199 continue
200 }
201 if value <= crt_max_value {
202 return (
203 false,
204 Some(InvalidStateReason {
205 state_type: InvalidStateType::AreaConstraint,
206 area: area.clone(),
207 values: vec![crt_max_value, value],
208 }),
209 )
210 }
211 crt_max_value = value
212 }
213
214 (true, None)
215 }
216
217 fn check_arrow_valid(&self, arrow_index: usize) -> (bool, Option<InvalidStateReason>) {
218 let arrow = &self.constraints.arrows[arrow_index];
219 let (arrow_sum, arrow_full) = self.arrow_arrow_sum(arrow);
220 let (circle_number, circle_full) = self.arrow_circle_number(arrow);
221
222 if arrow_full && circle_full {
223 if arrow_sum == circle_number {
224 return (true, None)
225 }
226 return (
227 false,
228 Some(InvalidStateReason {
229 state_type: InvalidStateType::AreaConstraint,
230 area: Area::Arrow(arrow_index),
231 values: vec![],
232 }),
233 )
234 }
235
236 if circle_full {
237 if arrow_sum <= circle_number {
238 return (true, None)
239 }
240
241 return (
242 false,
243 Some(InvalidStateReason {
244 state_type: InvalidStateType::AreaConstraint,
245 area: Area::Arrow(arrow_index),
246 values: vec![],
247 }),
248 )
249 }
250
251 (true, None)
252 }
253
254 fn check_renban_valid(&self, area: &Area) -> (bool, Option<InvalidStateReason>) {
255 let check = self.check_area_region_valid(area);
256 if !check.0 {
257 return check
258 }
259
260 let mut min_value: u32 = self.constraints.grid_size as u32 + 1;
261 let mut max_value: u32 = 0;
262 let mut any_zero = false;
263 let values = self.get_area_values(area);
264 for &value in &values {
265 if value == 0 {
266 any_zero = true;
267 }
268 if value > 0 && value < min_value {
269 min_value = value;
270 }
271 if value > max_value {
272 max_value = value;
273 }
274 }
275
276 if max_value == 0 {
277 return (true, None)
279 }
280
281 if (!any_zero && max_value - min_value + 1 != values.len() as u32) ||
282 (any_zero && max_value - min_value + 1 > values.len() as u32) {
283 return (
284 false,
285 Some(InvalidStateReason {
286 state_type: InvalidStateType::AreaConstraint,
287 area: area.clone(),
288 values: vec![],
289 }),
290 )
291 }
292
293 (true, None)
294 }
295
296 fn check_palindrome_valid(&self, area: &Area) -> (bool, Option<InvalidStateReason>) {
297 let values = self.get_area_values(area);
298 let mut left = 0;
299 let mut right = values.len() - 1;
300 while left < right {
301 if values[left] != 0 && values[right] != 0 && values[left] != values[right] {
302 return (
303 false,
304 Some(InvalidStateReason {
305 state_type: InvalidStateType::AreaConstraint,
306 area: area.clone(),
307 values: vec![left as u32, right as u32],
308 }),
309 )
310 }
311 left += 1;
312 right -= 1;
313 }
314
315 (true, None)
316 }
317
318 fn check_anti_knight_valid(&self) -> (bool, Option<InvalidStateReason>) {
319 for cell in self.get_area_cells(&Area::Grid) {
320 let value = self.grid[cell.row][cell.col];
321 if value == 0 {
322 continue
323 }
324
325 for peer in self.get_knight_peers(&cell) {
326 let peer_value = self.grid[peer.row][peer.col];
327 if peer_value == 0 {
328 continue
329 }
330 if value == peer_value {
331 return (
332 false,
333 Some(InvalidStateReason {
334 state_type: InvalidStateType::CellInvalidValue,
335 area: Area::Cell(cell.row, cell.col),
336 values: vec![value],
337 }),
338 )
339 }
340 }
341 }
342
343 (true, None)
344 }
345
346 fn check_anti_king_valid(&self) -> (bool, Option<InvalidStateReason>) {
347 for cell in self.get_area_cells(&Area::Grid) {
348 let value = self.grid[cell.row][cell.col];
349 if value == 0 {
350 continue
351 }
352
353 for peer in self.get_king_peers(&cell) {
354 let peer_value = self.grid[peer.row][peer.col];
355 if peer_value == 0 {
356 continue
357 }
358 if value == peer_value {
359 return (
360 false,
361 Some(InvalidStateReason {
362 state_type: InvalidStateType::CellInvalidValue,
363 area: Area::Cell(cell.row, cell.col),
364 values: vec![value],
365 }),
366 )
367 }
368 }
369 }
370
371 (true, None)
372 }
373
374 fn check_killer_area_valid(&self, area: &Area, killer_cage_index: usize) -> (bool, Option<InvalidStateReason>) {
375 let check = self.check_area_region_valid(area);
376 if !check.0 {
377 return check
378 }
379
380 let mut sum: u32 = 0;
381 let mut any_zero = false;
382 for cell in self.get_area_cells(&area) {
383 let value = self.grid[cell.row][cell.col];
384 if value == 0 {
385 any_zero = true;
386 }
387 sum += value;
388 }
389
390 let killer_cage = &self.constraints.killer_cages[killer_cage_index];
391 if let Some(killer_sum) = killer_cage.sum {
392 if sum != killer_sum && !any_zero || sum > killer_sum {
393 return (
394 false,
395 Some(InvalidStateReason {
396 state_type: InvalidStateType::AreaConstraint,
397 area: area.clone(),
398 values: vec![],
399 }),
400 )
401 }
402 }
403
404 (true, None)
405 }
406
407 fn check_kropki_dot_valid(&self, kropki_dot_index: usize) -> (bool, Option<InvalidStateReason>) {
408 let kropki_dot = &self.constraints.kropki_dots[kropki_dot_index];
409 let KropkiDot { dot_type, cell_1, cell_2 } = kropki_dot;
410 let mut value1 = self.grid[cell_1.row][cell_1.col];
411 let mut value2 = self.grid[cell_2.row][cell_2.col];
412 if value1 > value2 {
413 swap(&mut value1, &mut value2);
414 }
415 if value1 == 0 {
416 return (true, None)
417 }
418
419 let valid = match dot_type {
420 KropkiDotType::Consecutive => {
421 value1 + 1 == value2
422 },
423 KropkiDotType::Double => {
424 value1 * 2 == value2
425 },
426 KropkiDotType::Negative => {
427 value1 + 1 != value2 && value1 * 2 != value2
428 },
429 };
430
431 if valid {
432 return (true, None)
433 }
434
435 (
436 false,
437 Some(InvalidStateReason {
438 state_type: InvalidStateType::AreaConstraint,
439 area: Area::KropkiDot(kropki_dot_index),
440 values: vec![],
441 }),
442 )
443 }
444
445 fn check_odd_cells(&self) -> (bool, Option<InvalidStateReason>) {
446 for cell in &self.constraints.odd_cells {
447 let value = self.grid[cell.row][cell.col];
448 if value != 0 && value % 2 == 0 {
449 return (
450 false,
451 Some(InvalidStateReason {
452 state_type: InvalidStateType::CellInvalidValue,
453 area: Area::Cell(cell.row, cell.col),
454 values: vec![value],
455 }),
456 )
457 }
458 }
459 (true, None)
460 }
461
462 fn check_even_cells(&self) -> (bool, Option<InvalidStateReason>) {
463 for cell in &self.constraints.even_cells {
464 let value = self.grid[cell.row][cell.col];
465 if value != 0 && value % 2 == 1 {
466 return (
467 false,
468 Some(InvalidStateReason {
469 state_type: InvalidStateType::CellInvalidValue,
470 area: Area::Cell(cell.row, cell.col),
471 values: vec![value],
472 }),
473 )
474 }
475 }
476 (true, None)
477 }
478
479 fn check_top_bottom_valid(&self) -> (bool, Option<InvalidStateReason>) {
480 let valid = TopBottomCandidates::new(true).run(&self).is_empty();
481
482 if valid {
483 return (true, None)
484 }
485
486 (
487 false,
488 Some(InvalidStateReason {
489 state_type: InvalidStateType::AreaConstraint,
490 area: Area::Grid,
491 values: vec![],
492 }),
493 )
494 }
495}