sudoku/strategy/strategies/
avoidable_rectangles.rs

1// WIP
2use super::prelude::*;
3
4#[allow(unused)]
5fn find_avoidable_rectangles(
6    filled_cells: Sudoku,
7    clues: Sudoku,
8    cell_poss_digits: &CellArray<Set<Digit>>,
9    stop_after_first: bool,
10    mut on_avoidable_rectangle: impl FnMut(
11        // 2 lines and 2 cols of the rectangle
12        Set<Line>,
13        // impossible candidate
14        Candidate,
15    ),
16) -> Result<(), Unsolvable> {
17    let cell = |row: u8, col: u8| row * 9 + col;
18    for row1 in 0..8 {
19        for row2 in row1 + 1..9 {
20            let rows_in_same_chute = row1 / 3 == row2 / 3;
21            for col1 in 0..8 {
22                for col2 in col1 + 1..9 {
23                    let cols_in_same_chute = col1 / 3 == col2 / 3;
24                    if !(rows_in_same_chute ^ cols_in_same_chute) {
25                        continue;
26                    }
27
28                    // find the digits that are already set, their count
29                    // and whether there are any clues
30                    let cells = [
31                        cell(row1, col1),
32                        cell(row1, col2),
33                        cell(row2, col1),
34                        cell(row2, col2),
35                    ];
36                    let mut n_cells_set = 0;
37                    let mut n_cells_clues = 0;
38                    let mut set_digits = Set::NONE;
39                    let mut free_cell = 0;
40                    for &cell in &cells {
41                        match filled_cells.0[cell as usize] {
42                            0 => free_cell = cell,
43                            digit => {
44                                set_digits |= Digit::new(digit);
45                                n_cells_set += 1;
46                                if clues.0[cell as usize] != 0 {
47                                    n_cells_clues += 1;
48                                }
49                            }
50                        }
51                    }
52
53                    if n_cells_set == 3 && n_cells_clues == 0 && set_digits.len() == 2 {
54                        let candidates_remaining_cell = cell_poss_digits[Cell::new(free_cell)];
55                        if candidates_remaining_cell.overlaps(set_digits) {
56                            // found an avoidable rectangle
57                            let row1 = Row::new(row1);
58                            let row2 = Row::new(row2);
59                            let col1 = Col::new(col1);
60                            let col2 = Col::new(col2);
61                            if let Some(digit) = (candidates_remaining_cell & set_digits).unique()? {
62                                on_avoidable_rectangle(
63                                    Line::from(row1).as_set()
64                                        | Line::from(row2)
65                                        | Line::from(col1)
66                                        | Line::from(col2),
67                                    Candidate {
68                                        cell: Cell::new(free_cell),
69                                        digit,
70                                    },
71                                )
72                            }
73                        }
74                    }
75                }
76            }
77        }
78    }
79    Ok(())
80}