1use crate::solver::logical_solver::arrow_advanced_candidates::ArrowAdvancedCandidates;
2use crate::solver::logical_solver::common_peer_elimination_arrow::CommonPeerEliminationArrow;
3use crate::solver::logical_solver::kropki_advanced_candidates::KropkiAdvancedCandidates;
4use crate::solver::logical_solver::nishio_forcing_chains::NishioForcingChains;
5use crate::solver::logical_solver::renban_candidates::RenbanCandidates;
6use crate::types::{Area, Arrow, CellDirection, CellPosition, Grid, KillerCage, KropkiDot, KropkiDotType, Rule, SudokuConstraints, SudokuGrid};
7use std::cell::RefCell;
8use std::collections::{HashSet, HashMap};
9use std::cmp::{min, max};
10use std::ops::BitAnd;
11use std::rc::Rc;
12use itertools::Itertools;
13use logical_solver::adhoc_naked_set::AdhocNakedSet;
14use logical_solver::palindrome_candidates::PalindromeCandidates;
15use logical_solver::palindrome_values::PalindromeValues;
16use self::logical_solver::advanced_candidates::CellEliminationsResult;
17use self::logical_solver::arrow_candidates::ArrowCombinationLogicFactory;
18use self::logical_solver::candidates::Candidates;
19use self::logical_solver::combinations::cell_combination_logic::CellsCacheKey;
20use self::logical_solver::common_peer_elimination::CommonPeerElimination;
21use self::logical_solver::common_peer_elimination_kropki::CommonPeerEliminationKropki;
22use self::logical_solver::hidden_set::HiddenSet;
23use self::logical_solver::hidden_singles::HiddenSingles;
24use self::logical_solver::killer45::Killer45;
25use self::logical_solver::killer_candidates::KillerCandidates;
26use self::logical_solver::kropki_chain_candidates::KropkiChainCandidates;
27use self::logical_solver::locked_candidates::LockedCandidates;
28use self::logical_solver::naked_set::NakedSet;
29use self::logical_solver::naked_singles::NakedSingle;
30use self::logical_solver::technique::Technique;
31use self::logical_solver::thermo_candidates::ThermoCandidates;
32use self::logical_solver::thermo_steps::Thermo;
33use self::logical_solver::top_bottom_candidates::TopBottomCandidates;
34use self::logical_solver::x_wing::XWing;
35use self::logical_solver::xy_wing::XYWing;
36use self::logical_solver::turbot_fish::TurbotFish;
37use self::logical_solver::empty_reclanges::EmptyRectangles;
38use crate::solver::logical_solver::arrow_candidates::ArrowCandidates;
39
40mod checker;
41pub mod logical_solver;
42mod brute_solver;
43
44const KNIGHT_MOVES: [CellDirection; 8] = [
45 CellDirection { row: 1, col: 2 },
46 CellDirection { row: 1, col: -2 },
47 CellDirection { row: -1, col: 2 },
48 CellDirection { row: -1, col: -2 },
49 CellDirection { row: 2, col: 1 },
50 CellDirection { row: 2, col: -1 },
51 CellDirection { row: -2, col: 1 },
52 CellDirection { row: -2, col: -1 },
53];
54
55const KING_MOVES: [CellDirection; 8] = [
56 CellDirection { row: -1, col: -1 },
57 CellDirection { row: -1, col: 0 },
58 CellDirection { row: -1, col: 1 },
59 CellDirection { row: 0, col: -1 },
60 CellDirection { row: 0, col: 1 },
61 CellDirection { row: 1, col: -1 },
62 CellDirection { row: 1, col: 0 },
63 CellDirection { row: 1, col: 1 },
64];
65
66const ADJACENT_MOVES: [CellDirection; 4] = [
67 CellDirection { row: 0, col: 1 },
68 CellDirection { row: 0, col: -1 },
69 CellDirection { row: 1, col: 0 },
70 CellDirection { row: -1, col: 0 },
71];
72
73pub struct Solver {
74 pub constraints: SudokuConstraints,
75 pub techniques: Vec<Rc<dyn Technique>>,
76 pub grid: Grid,
77 pub solution: Option<Grid>,
78 grid_to_regions: Vec<Vec<Vec<usize>>>,
79 grid_to_thermos: Vec<Vec<Vec<usize>>>,
80 grid_to_killer_cage: Vec<Vec<usize>>,
81 grid_to_kropki_dots: Vec<Vec<Vec<usize>>>,
82 grid_to_odd_cells: Vec<Vec<bool>>,
83 grid_to_even_cells: Vec<Vec<bool>>,
84 grid_to_renbans: Vec<Vec<Vec<usize>>>,
85 candidates_active: bool,
86 candidates: Vec<Vec<HashSet<u32>>>,
87 hint_mode: bool,
88 step_count_limit: Option<usize>,
89 arrow_combinatons_logic_factory: RefCell<ArrowCombinationLogicFactory>,
90 cell_eliminations_cache: RefCell<HashMap<CellsCacheKey, CellEliminationsResult>>,
91}
92
93impl Clone for Solver {
94 fn clone(&self) -> Self {
95 Self {
96 constraints: self.constraints.clone(),
97 techniques: self.techniques.clone(),
98 grid: self.grid.clone(),
99 solution: self.solution.clone(),
100 grid_to_regions: self.grid_to_regions.clone(),
101 grid_to_thermos: self.grid_to_thermos.clone(),
102 grid_to_killer_cage: self.grid_to_killer_cage.clone(),
103 grid_to_kropki_dots: self.grid_to_kropki_dots.clone(),
104 grid_to_odd_cells: self.grid_to_odd_cells.clone(),
105 grid_to_even_cells: self.grid_to_even_cells.clone(),
106 grid_to_renbans: self.grid_to_renbans.clone(),
107 candidates_active: self.candidates_active.clone(),
108 candidates: self.candidates.clone(),
109 hint_mode: self.hint_mode.clone(),
110 step_count_limit: self.step_count_limit.clone(),
111 arrow_combinatons_logic_factory: RefCell::new(ArrowCombinationLogicFactory::new()),
112 cell_eliminations_cache: self.cell_eliminations_cache.clone(),
113 }
114 }
115}
116
117impl Solver {
118 pub fn new(mut constraints: SudokuConstraints, input_grid: Option<SudokuGrid>) -> Solver {
119 constraints.regions.extend(constraints.extra_regions.to_vec());
121
122 let mut grid_to_regions = vec![ vec![ vec![]; constraints.grid_size ]; constraints.grid_size ];
123 for (index, region) in constraints.regions.iter().enumerate() {
124 for cell in region {
125 grid_to_regions[cell.row][cell.col].push(index);
126 }
127 }
128
129 let mut grid_to_thermos = vec![ vec![ vec![]; constraints.grid_size ]; constraints.grid_size ];
130 for (index, thermo) in constraints.thermos.iter().enumerate() {
131 for cell in thermo {
132 grid_to_thermos[cell.row][cell.col].push(index);
133 }
134 }
135
136 let mut grid_to_killer_cage = vec![ vec![ usize::MAX; constraints.grid_size ]; constraints.grid_size ];
137 for (index, killer_cage) in constraints.killer_cages.iter().enumerate() {
138 for cell in &killer_cage.region {
139 grid_to_killer_cage[cell.row][cell.col] = index;
140 }
141 }
142
143 let mut grid_to_kropki_dots = vec![ vec![ vec![]; constraints.grid_size ]; constraints.grid_size ];
144 for (index, kropki_dot) in constraints.kropki_dots.iter().enumerate() {
145 let KropkiDot { dot_type: _, cell_1, cell_2 } = kropki_dot;
146 grid_to_kropki_dots[cell_1.row][cell_1.col].push(index);
147 grid_to_kropki_dots[cell_2.row][cell_2.col].push(index);
148 }
149 if constraints.kropki_negative {
150 for row in 0..constraints.grid_size {
151 for col in 0..constraints.grid_size {
152 let cell = CellPosition::new(row, col);
153 let adjacent_cells: HashSet<CellPosition> = Self::get_adjacent_cells(cell, constraints.grid_size).into_iter().collect();
154 let dot_cells: HashSet<CellPosition> = grid_to_kropki_dots[row][col].iter()
155 .map(|&kropki_dot_index| {
156 let kropki_dot = &constraints.kropki_dots[kropki_dot_index];
157 let other_cell = kropki_dot.other_cell(&cell);
158 other_cell
159 })
160 .collect();
161 let negative_cells: HashSet<CellPosition> = adjacent_cells.difference(&dot_cells).copied().collect();
162 for negative_cell in negative_cells.into_iter().sorted() {
163 let kropki_dot_index = constraints.kropki_dots.len();
164 grid_to_kropki_dots[cell.row][cell.col].push(kropki_dot_index);
165 grid_to_kropki_dots[negative_cell.row][negative_cell.col].push(kropki_dot_index);
166 constraints.kropki_dots.push(KropkiDot {
167 dot_type: KropkiDotType::Negative,
168 cell_1: cell,
169 cell_2: negative_cell,
170 })
171 }
172 }
173 }
174 }
175
176 let mut grid_to_odd_cells = vec![ vec![ false; constraints.grid_size ]; constraints.grid_size ];
177 for cell in &constraints.odd_cells {
178 grid_to_odd_cells[cell.row][cell.col] = true;
179 }
180
181 let mut grid_to_even_cells = vec![ vec![ false; constraints.grid_size ]; constraints.grid_size ];
182 for cell in &constraints.even_cells {
183 grid_to_even_cells[cell.row][cell.col] = true;
184 }
185
186 let mut grid_to_renbans = vec![ vec![ vec![]; constraints.grid_size ]; constraints.grid_size ];
187 for (index, renban) in constraints.renbans.iter().enumerate() {
188 for cell in renban {
189 grid_to_renbans[cell.row][cell.col].push(index);
190 }
191 }
192
193 let grid = if input_grid.is_some() {
194 input_grid.unwrap().values
195 } else {
196 let mut initial_grid = vec![ vec![ 0; constraints.grid_size ]; constraints.grid_size ];
197 for fixed_number in &constraints.fixed_numbers {
198 initial_grid[fixed_number.position.row][fixed_number.position.col] = fixed_number.value;
199 }
200 initial_grid
201 };
202
203 let candidates = vec![ vec![ HashSet::new(); constraints.grid_size ]; constraints.grid_size ];
204
205 Solver {
206 constraints,
207 grid,
208 solution: None,
209 grid_to_regions,
210 grid_to_thermos,
211 grid_to_killer_cage,
212 grid_to_kropki_dots,
213 grid_to_odd_cells,
214 grid_to_even_cells,
215 grid_to_renbans,
216 candidates_active: false,
217 candidates,
218 hint_mode: false,
219 step_count_limit: None,
220 techniques: Self::default_techniques(),
221 arrow_combinatons_logic_factory: RefCell::new(ArrowCombinationLogicFactory::new()),
222 cell_eliminations_cache: RefCell::new(HashMap::new()),
223 }
224 }
225
226 pub fn with_hint_mode(mut self, flag: bool) -> Self {
227 self.hint_mode = flag;
228 self
229 }
230
231 pub fn with_step_count_limit(mut self, step_count_limit: usize) -> Self {
232 self.step_count_limit = Some(step_count_limit);
233 self
234 }
235
236 pub fn default_techniques() -> Vec<Rc<dyn Technique>> {
237 vec![
238 Rc::new(ThermoCandidates),
239 Rc::new(KillerCandidates),
240 Rc::new(KropkiChainCandidates::new(false)),
241 Rc::new(KropkiChainCandidates::new(true)),
242 Rc::new(TopBottomCandidates::new(false)),
243 Rc::new(ArrowCandidates),
244 Rc::new(RenbanCandidates),
245 Rc::new(PalindromeValues),
246 Rc::new(PalindromeCandidates),
247 Rc::new(NakedSingle),
248 Rc::new(HiddenSingles),
249 Rc::new(Thermo),
250 Rc::new(Candidates),
251 Rc::new(Killer45),
252 Rc::new(LockedCandidates::new(2)),
253 Rc::new(NakedSet::new(2)),
254 Rc::new(HiddenSet::new(2)),
255 Rc::new(LockedCandidates::new(3)),
256 Rc::new(NakedSet::new(3)),
257 Rc::new(HiddenSet::new(3)),
258 Rc::new(XWing),
259 Rc::new(XYWing),
260 Rc::new(CommonPeerElimination),
261 Rc::new(CommonPeerEliminationKropki),
262 Rc::new(KropkiAdvancedCandidates),
263 Rc::new(ArrowAdvancedCandidates),
264 Rc::new(CommonPeerEliminationArrow),
265 Rc::new(AdhocNakedSet),
266 Rc::new(TurbotFish),
267 Rc::new(EmptyRectangles),
268 Rc::new(NishioForcingChains),
270 ]
271 }
272
273 pub fn with_techniques(mut self, techniques: Vec<Rc<dyn Technique>>) -> Self {
274 self.techniques = techniques;
275 self
276 }
277
278 pub fn without_techniques(mut self, techniques: Vec<Rc<dyn Technique>>) -> Self {
279 let rules: Vec<Rule> = techniques.into_iter().map(|t| t.get_rule()).collect();
280 self.techniques = self.techniques.into_iter().filter(|t| !rules.contains(&t.get_rule())).collect();
281 self
282 }
283
284 fn get_adjacent_cells(cell: CellPosition, grid_size: usize) -> Vec<CellPosition> {
285 ADJACENT_MOVES.iter().filter_map(|direction| {
286 let prow = cell.row as isize + direction.row;
287 let pcol = cell.col as isize + direction.col;
288 if prow < 0 || prow >= grid_size as isize ||
289 pcol < 0 || pcol >= grid_size as isize {
290 return None
291 }
292 let peer = CellPosition {
293 row: prow as usize,
294 col: pcol as usize,
295 };
296 Some(peer)
297 }).collect()
298 }
299
300 fn compute_area_cell_candidates(&self, area: &Area, cell: &CellPosition) -> HashSet<u32> {
301 match area {
302 #[allow(unused_parens)]
303 (
304 &Area::Adhoc(_) | &Area::Row(_) | &Area::Column(_) | &Area::Region(_) | &Area::Renban(_) |
305 &Area::PrimaryDiagonal | &Area::SecondaryDiagonal
306 ) => self.compute_generic_area_cell_candidates(area),
307 &Area::Thermo(thermo_index) => self.compute_thermo_cell_candidates(thermo_index, cell),
308 &Area::KillerCage(killer_cage_index) => self.compute_killer_cell_candidates(killer_cage_index),
309 &Area::KropkiDot(_) => {
310 self.compute_all_candidates()
312 },
313 &Area::Grid | &Area::Cell(_, _) | &Area::Arrow(_) | &Area::Palindrome(_) => unimplemented!(),
314 }
315 }
316
317 fn compute_generic_area_cell_candidates(&self, area: &Area) -> HashSet<u32> {
318 let mut set = self.compute_all_candidates();
319 for CellPosition { row, col } in self.get_area_cells(area) {
320 if self.grid[row][col] != 0 {
321 set.remove(&self.grid[row][col]);
322 }
323 }
324 set
325 }
326
327 fn compute_killer_cell_candidates(&self, killer_cage_index: usize) -> HashSet<u32> {
328 let area = Area::KillerCage(killer_cage_index);
329 let mut set = self.compute_generic_area_cell_candidates(&area);
330
331 let killer_cage = &self.constraints.killer_cages[killer_cage_index];
332 if let Some(sum) = killer_cage.sum {
333 for value in (sum+1)..=(self.constraints.grid_size as u32) {
334 set.remove(&value);
335 }
336 }
337
338 set
339 }
340
341 fn compute_thermo_cell_candidates(&self, thermo_index: usize, area_cell: &CellPosition) -> HashSet<u32> {
343 let thermo = &self.constraints.thermos[thermo_index];
344
345 let mut after = false;
346 let mut max_before = 0;
347 let mut min_after = self.constraints.grid_size as u32 + 1;
348
349 for cell in thermo {
350 if area_cell.row == cell.row && area_cell.col == cell.col {
351 after = true;
352 continue
353 }
354 let value = self.grid[cell.row][cell.col];
355 if value == 0 {
356 continue
357 }
358
359 if after {
360 min_after = min(min_after, value);
361 } else {
362 max_before = max(max_before, value);
363 }
364 }
365
366 let set: HashSet<u32> = (max_before+1..=min_after-1).collect();
367
368 set
369 }
370
371 fn compute_cell_candidates(&self, cell: &CellPosition) -> HashSet<u32> {
372 if self.grid[cell.row][cell.col] != 0 {
373 return HashSet::new()
374 }
375
376 if self.candidates_active {
377 return self.candidates[cell.row][cell.col].clone();
378 }
379
380 self.recompute_cell_candidates(cell)
381 }
382
383 fn recompute_cell_candidates(&self, cell: &CellPosition) -> HashSet<u32> {
386 let mut candidates = self.compute_all_candidates();
387 for peer in self.get_cell_peers(cell, false) {
388 let value = self.grid[peer.row][peer.col];
389 if value == 0 {
390 continue
391 }
392 candidates.remove(&value);
393 }
394
395 for area in &self.get_cell_areas(cell, false) {
396 let area_set = self.compute_area_cell_candidates(area, cell);
397 candidates = candidates.intersection(&area_set).cloned().collect();
398 }
399
400 if self.grid_to_odd_cells[cell.row][cell.col] {
401 candidates = candidates.into_iter().filter(|value| value % 2 == 1).collect();
402 }
403 if self.grid_to_even_cells[cell.row][cell.col] {
404 candidates = candidates.into_iter().filter(|value| value % 2 == 0).collect();
405 }
406
407 candidates
408 }
409
410 fn compute_all_candidates(&self) -> HashSet<u32> {
411 (1..=self.constraints.grid_size as u32).collect::<HashSet<u32>>()
412 }
413
414 fn get_cell_areas(&self, cell: &CellPosition, include_thermo: bool) -> Vec<Area> {
417 let mut areas: Vec<Area> = vec![];
418
419 areas.extend(self.get_cell_classic_areas(cell));
420 areas.extend(self.get_cell_special_areas(cell, include_thermo));
421
422 areas
423 }
424
425 fn get_cell_classic_areas(&self, cell: &CellPosition) -> Vec<Area> {
426 let &CellPosition { row, col } = cell;
427 let mut areas = vec![ Area::Row(row), Area::Column(col) ];
428
429 for ®ion_index in &self.grid_to_regions[row][col] {
430 if region_index < self.constraints.grid_size {
431 areas.push(Area::Region(region_index));
432 }
433 }
434
435 areas
436 }
437
438 fn get_cell_special_areas(&self, cell: &CellPosition, include_thermo: bool) -> Vec<Area> {
439 let &CellPosition { row, col } = cell;
440 let mut areas: Vec<Area> = vec![];
441
442 for ®ion_index in &self.grid_to_regions[row][col] {
443 if region_index >= self.constraints.grid_size {
444 areas.push(Area::Region(region_index));
445 }
446 }
447
448 if self.constraints.primary_diagonal && row == col {
449 areas.push(Area::PrimaryDiagonal);
450 }
451 if self.constraints.secondary_diagonal && row == self.constraints.grid_size - 1 - col {
452 areas.push(Area::SecondaryDiagonal);
453 }
454 let killer_cage_index = self.grid_to_killer_cage[row][col];
455 if killer_cage_index != usize::MAX {
456 areas.push(Area::KillerCage(killer_cage_index));
457 }
458 if include_thermo {
459 for &thermo_index in &self.grid_to_thermos[row][col] {
460 areas.push(Area::Thermo(thermo_index));
461 }
462 }
463 for &renban_index in &self.grid_to_renbans[row][col] {
464 areas.push(Area::Renban(renban_index));
465 }
466
467 areas
468 }
469
470 fn get_all_areas(
473 &self, include_thermo: bool, include_killer: bool, include_kropki: bool, include_renban: bool,
474 include_palindrome: bool,
475 ) -> Vec<Area> {
476 let mut areas = vec![];
477 areas.extend(self.get_row_areas());
478 areas.extend(self.get_col_areas());
479 areas.extend(self.get_region_areas());
480 if self.constraints.primary_diagonal {
481 areas.push(Area::PrimaryDiagonal);
482 }
483 if self.constraints.secondary_diagonal {
484 areas.push(Area::SecondaryDiagonal);
485 }
486 if include_thermo {
487 for thermo_index in 0..self.constraints.thermos.len() {
488 areas.push(Area::Thermo(thermo_index));
489 }
490 }
491 if include_killer {
492 for killer_cage_index in 0..self.constraints.killer_cages.len() {
493 areas.push(Area::KillerCage(killer_cage_index));
494 }
495 }
496 if include_kropki {
497 for kropki_dot_index in 0..self.constraints.kropki_dots.len() {
498 areas.push(Area::KropkiDot(kropki_dot_index));
499 }
500 }
501 if include_renban {
502 for renban_index in 0..self.constraints.renbans.len() {
503 areas.push(Area::Renban(renban_index));
504 }
505 }
506 if include_palindrome {
507 for palindrome_index in 0..self.constraints.palindromes.len() {
508 areas.push(Area::Palindrome(palindrome_index));
509 }
510 }
511
512 areas
513 }
514
515 fn get_all_proper_areas(&self) -> Vec<Area> {
516 self.get_all_areas(false, false, false, false, false)
517 }
518
519 fn get_row_areas(&self) -> Vec<Area> {
520 (0..self.constraints.grid_size).map(|row| Area::Row(row)).collect()
521 }
522
523 fn get_col_areas(&self) -> Vec<Area> {
524 (0..self.constraints.grid_size).map(|col| Area::Column(col)).collect()
525 }
526
527 fn get_region_areas(&self) -> Vec<Area> {
528 (0..self.constraints.regions.len()).map(|region_index| {
529 Area::Region(region_index)
530 }).collect()
531 }
532
533 fn get_area_cells(&self, area: &Area) -> Vec<CellPosition> {
534 match area {
535 &Area::Grid => self.get_grid_cells(),
536 Area::Adhoc(cells) => cells.to_vec(),
537 &Area::Cell(row, col) => vec![CellPosition::new(row, col)],
538 &Area::Row(row) => self.get_row_cells(row),
539 &Area::Column(col) => self.get_col_cells(col),
540 &Area::Region(region_index) => self.constraints.regions[region_index].to_vec(),
541 &Area::Thermo(thermo_index) => self.constraints.thermos[thermo_index].to_vec(),
542 &Area::KillerCage(killer_cage_index) => {
543 self.constraints.killer_cages[killer_cage_index].region.to_vec()
544 },
545 &Area::KropkiDot(kropki_dot_index) => {
546 let kropki_dot = &self.constraints.kropki_dots[kropki_dot_index];
547 vec![ kropki_dot.cell_1, kropki_dot.cell_2 ]
548 },
549 &Area::PrimaryDiagonal => self.get_primary_diagonal_cells(),
550 &Area::SecondaryDiagonal => self.get_secondary_diagonal_cells(),
551 &Area::Renban(renban_index) => self.constraints.renbans[renban_index].to_vec(),
552 &Area::Palindrome(palindrome_index) => self.constraints.palindromes[palindrome_index].to_vec(),
553 &Area::Arrow(_) => unimplemented!(),
554 }
555 }
556
557 fn get_area_values(&self, area: &Area) -> Vec<u32> {
558 self.get_area_cells(&area).into_iter().map(|cell| {
559 let value = self.grid[cell.row][cell.col];
560 value
561 }).collect()
562 }
563
564 fn get_empty_area_cells(&self, area: &Area) -> Vec<CellPosition> {
565 self.get_area_cells(area).into_iter().filter(|cell| self.grid[cell.row][cell.col] == 0).collect()
566 }
567
568 fn get_all_empty_cells(&self) -> Vec<CellPosition> {
569 self.get_empty_area_cells(&Area::Grid)
570 }
571
572 #[allow(dead_code)]
573 fn get_all_cells_with_candidate(&self, value: u32) -> Vec<CellPosition> {
574 self.get_all_empty_cells()
575 .into_iter()
576 .filter(|&CellPosition { row, col }| self.candidates[row][col].contains(&value))
577 .collect()
578 }
579
580 fn get_grid_cells(&self) -> Vec<CellPosition> {
581 (0..self.constraints.grid_size).flat_map(|row| {
582 (0..self.constraints.grid_size).map(|col| {
583 CellPosition::new(row, col)
584 }).collect::<Vec<CellPosition>>()
585 }).collect()
586 }
587
588 fn get_row_cells(&self, row: usize) -> Vec<CellPosition> {
589 (0..self.constraints.grid_size).map(|col| CellPosition::new(row, col)).collect()
590 }
591
592 fn get_col_cells(&self, col: usize) -> Vec<CellPosition> {
593 (0..self.constraints.grid_size).map(|row| CellPosition::new(row, col)).collect()
594 }
595
596 fn get_primary_diagonal_cells(&self) -> Vec<CellPosition> {
597 (0..self.constraints.grid_size).map(|index| CellPosition::new(index, index)).collect()
598 }
599
600 fn get_secondary_diagonal_cells(&self) -> Vec<CellPosition> {
601 (0..self.constraints.grid_size).map(|index| {
602 CellPosition::new(index, self.constraints.grid_size - 1 - index)
603 }).collect()
604 }
605
606 #[allow(dead_code)]
607 fn compute_area_candidates_union(&self, area: &Area) -> HashSet<u32> {
608 let mut area_candidates: HashSet<u32> = HashSet::new();
609 for cell in self.get_area_cells(area) {
610 let cell_candidates = self.compute_cell_candidates(&cell);
611 area_candidates = area_candidates.union(&cell_candidates).cloned().collect();
612 }
613 area_candidates
614 }
615
616 fn get_area_cells_with_candidate(&self, area: &Area, value: u32) -> Vec<CellPosition> {
617 self.get_area_cells_with_candidates(area, &HashSet::from([ value ]))
618 }
619
620 fn filter_cells_with_any_candidates(&self, cells: &Vec<CellPosition>, values: &HashSet<u32>) -> Vec<CellPosition> {
621 cells.iter()
622 .filter(|&cell| !self.compute_cell_candidates(cell).is_disjoint(values))
623 .copied()
624 .collect()
625 }
626
627 fn filter_cells_with_subset_candidates(&self, cells: &Vec<CellPosition>, values: &HashSet<u32>) -> Vec<CellPosition> {
628 cells.iter()
629 .filter(|&cell| self.compute_cell_candidates(cell).is_subset(values))
630 .copied()
631 .collect()
632 }
633
634 fn get_area_cells_with_candidates(&self, area: &Area, values: &HashSet<u32>) -> Vec<CellPosition> {
635 let area_cells = self.get_empty_area_cells(area);
636 self.filter_cells_with_any_candidates(&area_cells, values)
637 }
638
639 fn compute_cells_by_value_in_area(&self, area: &Area, candidates: &Vec<Vec<HashSet<u32>>>) -> HashMap<u32, Vec<CellPosition>> {
640 let mut value_cells: HashMap<u32, Vec<CellPosition>> = HashMap::new();
641 for cell in self.get_empty_area_cells(area) {
642 for value in candidates[cell.row][cell.col].iter().sorted() {
643 let entry = value_cells.entry(*value).or_insert(vec![]);
644 entry.push(cell);
645 }
646 }
647 value_cells
648 }
649
650 fn get_cell_peers_with_candidate(&self, cell: &CellPosition, value: u32) -> Vec<CellPosition> {
651 let candidates: HashSet<u32> = HashSet::from([ value ]);
652 self.get_cell_peers_with_candidates(cell, &candidates)
653 }
654
655 fn get_cell_peers_with_candidates(&self, cell: &CellPosition, values: &HashSet<u32>) -> Vec<CellPosition> {
656 let peers = self.get_cell_peers(cell, true);
657 self.filter_cells_with_any_candidates(&peers, values)
658 }
659
660 fn get_cell_peers(&self, cell: &CellPosition, include_thermo: bool) -> Vec<CellPosition> {
664 let mut peers: Vec<CellPosition> = vec![];
665
666 peers.extend(self.get_cell_classic_peers(cell));
667 peers.extend(self.get_cell_special_peers(cell, include_thermo));
668
669 peers.into_iter()
670 .filter(|other_cell| other_cell != cell)
671 .unique()
672 .collect()
673 }
674
675 fn get_cell_classic_peers(&self, cell: &CellPosition) -> Vec<CellPosition> {
676 self.get_cell_classic_areas(cell)
677 .iter()
678 .flat_map(|area| self.get_area_cells(area))
679 .collect()
680 }
681
682 fn get_cell_special_peers(&self, cell: &CellPosition, include_thermo: bool) -> Vec<CellPosition> {
683 let mut peers: Vec<CellPosition> = self.get_cell_special_areas(cell, include_thermo)
684 .iter()
685 .flat_map(|area| self.get_area_cells(area))
686 .collect();
687
688 if self.constraints.anti_knight {
689 peers.extend(self.get_knight_peers(cell));
690 }
691
692 if self.constraints.anti_king {
693 peers.extend(self.get_king_peers(cell));
694 }
695
696 peers
697 }
698
699 fn get_cell_only_special_peers(&self, cell: &CellPosition, include_thermo: bool) -> Vec<CellPosition> {
701 let special_peers = self.get_cell_special_peers(cell, include_thermo);
702 let classic_peers = self.get_cell_classic_peers(cell);
703
704 special_peers.into_iter().filter(|special_peer|
705 !classic_peers.contains(special_peer)
706 ).collect()
707 }
708
709 fn get_knight_peers(&self, cell: &CellPosition) -> Vec<CellPosition> {
710 KNIGHT_MOVES.iter().filter_map(|direction| {
711 let prow = cell.row as isize + direction.row;
712 let pcol = cell.col as isize + direction.col;
713 if prow < 0 || prow >= self.constraints.grid_size as isize ||
714 pcol < 0 || pcol >= self.constraints.grid_size as isize {
715 return None
716 }
717 let peer = CellPosition {
718 row: prow as usize,
719 col: pcol as usize,
720 };
721 Some(peer)
722 }).collect()
723 }
724
725 fn get_king_peers(&self, cell: &CellPosition) -> Vec<CellPosition> {
726 KING_MOVES.iter().filter_map(|direction| {
727 let prow = cell.row as isize + direction.row;
728 let pcol = cell.col as isize + direction.col;
729 if prow < 0 || prow >= self.constraints.grid_size as isize ||
730 pcol < 0 || pcol >= self.constraints.grid_size as isize {
731 return None
732 }
733 let peer = CellPosition {
734 row: prow as usize,
735 col: pcol as usize,
736 };
737 Some(peer)
738 }).collect()
739 }
740
741 fn is_empty_area_subset(&self, small_area: &Area, big_area: &Area) -> bool {
742 let small_set: HashSet<CellPosition> = self.get_empty_area_cells(small_area).into_iter().collect();
743 if small_set.is_empty() {
744 return false
745 }
746
747 let big_set: HashSet<CellPosition> = self.get_area_cells(big_area).into_iter().collect();
748 big_set.is_superset(&small_set)
749 }
750
751 fn get_subset_area_sum(&self, killer_cage: &KillerCage, big_area: &Area) -> u32 {
752 let big_set: HashSet<CellPosition> = self.get_area_cells(big_area).into_iter().collect();
753 let killer_cells_sum: u32 = killer_cage.region.iter().map(|&cell| {
754 if !big_set.contains(&cell) {
755 self.grid[cell.row][cell.col]
756 } else {
757 0
758 }
759 }).sum();
760 killer_cage.sum.unwrap() - killer_cells_sum
761 }
762
763 #[allow(dead_code)]
764 fn bit_mask_to_hash_set(&self, combination_mask: u32) -> HashSet<u32> {
765 (1..=self.constraints.grid_size).filter_map(|value| {
766 if combination_mask.bitand(1 << value) > 0 {
767 Some(value as u32)
768 } else {
769 None
770 }
771 }).collect()
772 }
773
774 fn arrow_circle_number(&self, arrow: &Arrow) -> (u32, bool) {
775 let mut value: u32 = 0;
776 let sorted_circle_cells: Vec<CellPosition> = arrow.circle_cells
777 .iter()
778 .cloned()
779 .sorted_by_key(|cell| *cell)
780 .collect();
781 let mut full = true;
782 for &CellPosition { row, col } in &sorted_circle_cells {
783 value = 10 * value + self.grid[row][col];
784 if self.grid[row][col] == 0 {
785 full = false;
786 }
787 }
788 (value, full)
789 }
790
791 fn arrow_arrow_sum(&self, arrow: &Arrow) -> (u32, bool) {
792 let mut sum: u32 = 0;
793 let mut full = true;
794 for &CellPosition { row, col } in &arrow.arrow_cells {
795 sum += self.grid[row][col];
796 if self.grid[row][col] == 0 {
797 full = false;
798 }
799 }
800 (sum, full)
801 }
802
803 fn count_empty_cells_in_list(&self, cells: &Vec<CellPosition>) -> usize {
804 cells.into_iter().filter(|cell| self.grid[cell.row][cell.col] == 0).count()
805 }
806}
807
808#[cfg(test)]
809mod tests;