1use std::collections::{HashSet, HashMap};
2use std::ops::BitOr;
3use std::rc::Rc;
4use crate::types::{Area, CellPosition, InvalidStateReason, SolutionStep, SolutionType, SudokulogicalSolveResult};
5use crate::solver::Solver;
6use self::combinations::cell_combination_logic::CellsCacheKey;
7use self::technique::Technique;
8use itertools::Itertools;
9
10pub mod technique;
11pub mod naked_singles;
12pub mod hidden_singles;
13pub mod thermo_steps;
14pub mod candidates;
15pub mod locked_candidates;
16pub mod naked_set;
17pub mod thermo_candidates;
18pub mod hidden_set;
19pub mod x_wing;
20pub mod xy_wing;
21pub mod common_peer_elimination;
22pub mod sum_candidates;
23pub mod killer_candidates;
24pub mod killer45;
25pub mod kropki_chain_candidates;
26pub mod kropki_advanced_candidates;
27pub mod common_peer_elimination_kropki;
28pub mod turbot_fish;
29pub mod top_bottom_candidates;
30pub mod empty_reclanges;
31pub mod combinations;
32pub mod arrow_candidates;
33pub mod advanced_candidates;
34pub mod arrow_advanced_candidates;
35pub mod common_peer_elimination_arrow;
36pub mod phistomefel_ring;
37pub mod nishio_forcing_chains;
38pub mod renban_candidates;
39pub mod palindrome_values;
40pub mod palindrome_candidates;
41pub mod adhoc_naked_set;
42
43const DEBUG: bool = false;
44const DISPLAY_STEPS: bool = false;
45
46impl Solver {
47 pub fn logical_solve(&mut self) -> SudokulogicalSolveResult {
48 let mut solution_type = SolutionType::Full;
49 let mut solution_steps = vec![];
50 let mut solution_steps_group_count = 0;
51
52 let check = self.check_partially_solved();
53 if !check.0 {
54 if DEBUG {
55 println!("Invalid initial grid");
56 }
57 return SudokulogicalSolveResult::no_solution(check.1.unwrap())
58 }
59
60 let mut empty_cell_count = self.compute_empty_cell_count();
61
62 while empty_cell_count > 0 {
63 let check = self.check_partially_solved();
65 if !check.0 {
66 if DEBUG {
67 println!("Reached invalid state");
68 }
69 return SudokulogicalSolveResult::no_solution(check.1.unwrap())
70 }
71
72 let mut steps = self.find_next_steps();
74 if steps.is_empty() {
75 break
76 }
77
78 solution_steps_group_count += 1;
79
80 if self.hint_mode {
81 steps.drain(1..);
83 }
84 if let Some(limit) = self.step_count_limit {
85 if solution_steps_group_count >= limit {
86 solution_steps.extend(steps);
87 break
89 }
90 }
91
92 let mut grid_step = false;
93 for mut step in steps.into_iter() {
94 let rule_check = self.apply_rule(&mut step);
95 if !rule_check.0 {
96 return SudokulogicalSolveResult::no_solution(rule_check.1.unwrap())
97 }
98
99 if step.is_grid_step() {
100 grid_step = true;
101 }
102
103 solution_steps.push(step);
104 }
105
106 if self.hint_mode && grid_step {
107 break
109 }
110
111 empty_cell_count = self.compute_empty_cell_count();
112 }
113
114 if empty_cell_count > 0 {
115 solution_type = SolutionType::Partial;
116 }
117
118 let res = SudokulogicalSolveResult {
119 solution_type,
120 solution: Some(self.grid.to_vec()),
121 steps: solution_steps.clone(),
122 invalid_state_reason: None,
123 };
124
125 res
126 }
127
128 fn find_next_steps(&self) -> Vec<SolutionStep> {
129 if self.hint_mode {
130 let steps = self.find_grid_steps();
132 if !steps.is_empty() {
133 return steps
134 }
135 }
136
137 let validity_update_steps = self.find_candidate_validity_update_steps();
140 if !validity_update_steps.is_empty() && self.step_count_limit.is_none() {
142 return validity_update_steps
143 }
144
145 let grid_steps = self.find_grid_steps();
146 if !grid_steps.is_empty() && self.step_count_limit.is_none() {
147 return grid_steps
148 }
149
150 let nongrid_steps = self.find_nongrid_steps();
151 if !nongrid_steps.is_empty() && self.step_count_limit.is_none() {
152 return nongrid_steps
153 }
154
155 vec![ validity_update_steps, grid_steps, nongrid_steps ].concat()
156 }
157
158 pub fn find_candidate_validity_update_steps(&self) -> Vec<SolutionStep> {
159 let candidate_validity_techniques: Vec<&Rc<dyn Technique>> = self.techniques
160 .iter()
161 .filter(|technique| technique.is_candidate_validity_update_step())
162 .collect();
163
164 let steps = self.run_techniques(candidate_validity_techniques);
165
166 steps
167 }
168
169 pub fn find_grid_steps(&self) -> Vec<SolutionStep> {
170 let grid_techniques: Vec<&Rc<dyn Technique>> = self.techniques
171 .iter()
172 .filter(|technique| technique.is_grid_step())
173 .collect();
174
175 self.run_techniques(grid_techniques)
176 }
177
178 fn find_nongrid_steps(&self) -> Vec<SolutionStep> {
179 let nongrid_techniques: Vec<&Rc<dyn Technique>> = self.techniques
180 .iter()
181 .filter(|technique| !technique.is_grid_step() &&
182 !technique.is_candidate_validity_update_step())
183 .collect();
184
185 let steps = self.run_techniques(nongrid_techniques);
186
187 steps
188 }
189
190 fn run_techniques(&self, techniques: Vec<&Rc<dyn Technique>>) -> Vec<SolutionStep> {
191 techniques.into_iter().fold(vec![], |mut all_steps, technique| {
192 if !all_steps.is_empty() && self.step_count_limit.is_none() {
193 return all_steps
194 }
195 let steps = technique.run(&self);
196 all_steps.extend(steps);
197
198 all_steps
199 })
200 }
201
202 pub fn apply_rule(&mut self, step: &SolutionStep) -> (bool, Option<InvalidStateReason>) {
203 if DISPLAY_STEPS {
204 println!(
205 "{:?} ({}) ({}) ({}): {}",
206 step.rule,
207 step.areas.iter().map(|x| format!("{:?}", x)).join(", "),
208 step.cells.iter().map(|x| format!("({},{})", x.row, x.col)).join(" "),
209 step.values.iter().map(|x| format!("{}", x)).join(", "),
210 step.affected_cells.iter().map(|x| format!("({},{})", x.row, x.col)).join(" ")
211 );
212 if let Some(reason) = &step.invalid_state_reason {
213 println!("{:?}", reason);
214 }
215 }
216
217 let technique = self.techniques
218 .iter()
219 .find(|technique| technique.get_rule() == step.rule)
220 .cloned()
221 .unwrap_or_else(|| panic!("Can't find technique for rule {}", step.rule));
222
223 let rule_check = technique.apply(&step, self);
224
225 if DEBUG {
226 self.validate_candidates();
228 }
229
230 rule_check
231 }
232
233 pub fn apply_rules(&mut self, steps: &Vec<SolutionStep>) {
234 for mut step in steps {
235 self.apply_rule(&mut step);
236 }
237 }
238
239 fn get_affected_by_cell(&self, cell: &CellPosition, values: &HashSet<u32>) -> Vec<CellPosition> {
242 self.get_cell_peers_with_candidates(cell, values)
243 }
244
245 fn get_affected_by_cells(&self, cells: &Vec<CellPosition>, values: &HashSet<u32>) -> Vec<CellPosition> {
247 self.get_affected_by_cell(&cells[0], values)
248 .into_iter()
249 .filter(|cell| {
250 cells[1..].iter().all(|other_cell| {
251 self.cells_affect_eachother(cell, other_cell)
252 })
253 })
254 .collect()
255 }
256
257 fn cells_affect_eachother(&self, cell1: &CellPosition, cell2: &CellPosition) -> bool {
258 cell1 != cell2 &&
259 !self.get_cell_areas(cell1, false)
260 .into_iter()
261 .collect::<HashSet<Area>>()
262 .is_disjoint(
263 &self.get_cell_areas(cell2, false)
264 .into_iter()
265 .collect()
266 )
267 }
268
269 fn get_affected_by_area_cells_cells(&self, area_cells: &Vec<CellPosition>, cells: &Vec<CellPosition>, values: &HashSet<u32>) -> Vec<CellPosition> {
271 self.filter_cells_with_any_candidates(area_cells, values)
272 .into_iter()
273 .filter(|cell| !cells.contains(cell))
274 .collect()
275 }
276
277 fn update_candidates(&mut self, cells: &Vec<CellPosition>, value: u32) {
278 for cell in cells {
279 self.candidates[cell.row][cell.col].remove(&value);
280 }
281 }
282
283 fn compute_empty_cell_count(&self) -> usize {
284 self.grid
285 .iter()
286 .map(|row| row.iter()
287 .map(|cell| if *cell == 0 { 1 } else { 0 })
288 .sum::<usize>())
289 .sum()
290 }
291
292 fn find_common_areas_except(&self, cells: &Vec<CellPosition>, area_exception: Area) -> Vec<Area> {
293 let areas = self.find_common_areas(cells);
294 let other_areas: Vec<Area> = areas.into_iter().filter(|area| *area != area_exception).collect();
295 other_areas
296 }
297
298 fn find_common_areas(&self, cells: &Vec<CellPosition>) -> Vec<Area> {
303 assert!(cells.len() >= 2);
304
305 let cell1 = cells[0];
306
307 let mut areas = vec![];
308 if cells.iter().map(|cell| cell.row).all_equal() {
309 areas.push(Area::Row(cell1.row));
310 }
311 if cells.iter().map(|cell| cell.col).all_equal() {
312 areas.push(Area::Column(cell1.col));
313 }
314
315 let mut common_regions: HashSet<&usize> = self.grid_to_regions[cells[0].row][cells[0].col].iter().collect();
316 for cell in cells[1..].iter() {
317 let cell_regions: HashSet<&usize> = self.grid_to_regions[cell.row][cell.col].iter().collect();
318 common_regions = common_regions.intersection(&cell_regions).copied().collect();
319 }
320 for ®ion_index in common_regions.into_iter().sorted() {
321 areas.push(Area::Region(region_index));
322 }
323
324 if cells.iter().map(|cell| self.grid_to_killer_cage[cell.row][cell.col]).all_equal() {
325 let killer_cage_index = self.grid_to_killer_cage[cell1.row][cell1.col];
326 if killer_cage_index != usize::MAX {
327 areas.push(Area::KillerCage(killer_cage_index));
328 }
329 }
330 if self.constraints.primary_diagonal && cells.iter().all(|cell| cell.row == cell.col) {
331 areas.push(Area::PrimaryDiagonal);
332 }
333 if self.constraints.secondary_diagonal && cells.iter().all(|cell| cell.row + cell.col == self.constraints.grid_size - 1) {
334 areas.push(Area::SecondaryDiagonal);
335 }
336
337 let mut common_renbans: HashSet<&usize> = self.grid_to_renbans[cells[0].row][cells[0].col].iter().collect();
338 for cell in cells[1..].iter() {
339 let cell_renbans: HashSet<&usize> = self.grid_to_renbans[cell.row][cell.col].iter().collect();
340 common_renbans = common_renbans.intersection(&cell_renbans).copied().collect();
341 }
342 for &renban_index in common_renbans {
343 areas.push(Area::Renban(renban_index));
344 }
345
346 areas
347 }
348
349 fn any_cells_with_candidates(&self, cells: &Vec<CellPosition>, values: &HashSet<u32>) -> bool {
350 cells.iter().any(|cell| !self.candidates[cell.row][cell.col].is_disjoint(&values))
351 }
352
353 fn any_cells_with_other_candidates(&self, cells: &Vec<CellPosition>, values: &HashSet<u32>) -> bool {
354 cells.iter().any(|cell| self.candidates[cell.row][cell.col].difference(&values).count() > 0)
355 }
356
357 fn cell_to_area(&self, cell: &CellPosition, area: &Area) -> CellPosition {
359 match area {
360 &Area::Row(row) => CellPosition { row, col: cell.col },
361 &Area::Column(col) => CellPosition { row: cell.row, col },
362 _ => unimplemented!(),
363 }
364 }
365
366 fn validate_candidates(&self) {
367 if !self.candidates_active {
368 return
369 }
370 for cell in &self.get_all_empty_cells() {
371 let &CellPosition { row, col } = cell;
372 let recomputed_cell_candidates = self.recompute_cell_candidates(cell);
373 if !self.candidates[row][col].is_subset(&recomputed_cell_candidates) {
374 println!("==> Invalid candidates for ({},{})!", row, col);
375 println!("Saved candidates: {:?}", self.candidates[row][col]);
376 println!("Real candidates: {:?}", recomputed_cell_candidates);
377 panic!();
378 }
379 }
380 }
381
382 pub fn cell_candidates_diff(&self, cells: &Vec<CellPosition>, valid_candidates: Vec<HashSet<u32>>) -> Vec<(CellPosition, Vec<u32>)> {
383 cells.into_iter().enumerate().filter_map(|(cell_index, &cell)| {
384 let cell_candidates = &self.candidates[cell.row][cell.col];
385 let valid_cell_candidates = &valid_candidates[cell_index];
386 if cell_candidates.len() == valid_cell_candidates.len() {
387 return None
388 }
389
390 let invalid_values: Vec<u32> = cell_candidates.difference(valid_cell_candidates)
391 .into_iter()
392 .copied()
393 .sorted()
394 .collect();
395
396 if invalid_values.is_empty() {
397 return None
398 }
399
400 Some((cell, invalid_values))
401 }).collect()
402 }
403
404 fn get_all_strong_links(&self) -> Vec<(Area, u32, CellPosition, CellPosition)> {
405 self.get_all_proper_areas().iter().flat_map(|area| {
406 let value_cells = self.compute_cells_by_value_in_area(area, &self.candidates);
407
408 value_cells.into_iter().sorted().filter_map(|(value, cells)| {
409 if cells.len() != 2 {
410 return None
411 }
412 return Some(
413 (area.clone(), value, cells[0], cells[1])
414 )
415 })
416 }).collect()
417 }
418
419 fn get_all_strong_links_by_value(&self) -> HashMap<u32, Vec<(Area, u32, CellPosition, CellPosition)>> {
420 self.get_all_strong_links()
421 .iter()
422 .cloned()
423 .sorted_by_key(|link| (link.1, link.0.clone(), link.2, link.3))
424 .group_by(|link| link.1)
425 .into_iter()
426 .map(|(value, group)| (value, group.collect()))
427 .collect()
428 }
429
430 fn candidates_to_set(&self, cell: CellPosition) -> u32 {
431 self.candidates[cell.row][cell.col].iter().fold(0, |acc, e| {
432 acc.bitor(1 << e)
433 })
434 }
435
436 fn cells_to_cache_key(&self, cells: &Vec<CellPosition>) -> CellsCacheKey {
437 cells.into_iter().map(|cell| {
438 (
439 cell.row as u32 * (self.constraints.grid_size as u32 + 1) + cell.col as u32,
440 self.grid[cell.row][cell.col],
441 self.candidates_to_set(*cell),
442 )
443 }).collect()
444 }
445}