rustoku_lib/core/
candidates.rs1use super::board::Board;
2use super::masks::Masks;
3
4#[derive(Debug, Copy, Clone, PartialEq, Eq)]
12pub struct Candidates {
13 cache: [[u16; 9]; 9],
14}
15
16impl Candidates {
17 pub(super) fn new() -> Self {
18 Candidates { cache: [[0; 9]; 9] }
19 }
20
21 pub(super) fn get(&self, r: usize, c: usize) -> u16 {
23 self.cache[r][c]
24 }
25
26 pub fn get_candidates(&self, r: usize, c: usize) -> Vec<u8> {
28 let mask = self.get(r, c);
29 let mut candidates = Vec::new();
30 for i in 0..9 {
31 if (mask >> i) & 1 == 1 {
33 candidates.push((i + 1) as u8);
34 }
35 }
36 candidates
37 }
38
39 pub(super) fn set(&mut self, r: usize, c: usize, mask: u16) {
41 self.cache[r][c] = mask;
42 }
43
44 pub(super) fn update_affected_cells(
46 &mut self,
47 r: usize,
48 c: usize,
49 masks: &Masks,
50 board: &Board,
51 ) {
52 self.cache[r][c] = 0; for i in 0..9 {
57 if board.is_empty(r, i) {
58 self.cache[r][i] = masks.compute_candidates_mask_for_cell(r, i);
59 }
60 if board.is_empty(i, c) {
61 self.cache[i][c] = masks.compute_candidates_mask_for_cell(i, c);
62 }
63 }
64
65 let box_idx = Masks::get_box_idx(r, c);
67 let start_row = (box_idx / 3) * 3;
68 let start_col = (box_idx % 3) * 3;
69 for r_offset in 0..3 {
70 for c_offset in 0..3 {
71 let cur_r = start_row + r_offset;
72 let cur_c = start_col + c_offset;
73 if board.is_empty(cur_r, cur_c) {
74 self.cache[cur_r][cur_c] = masks.compute_candidates_mask_for_cell(cur_r, cur_c);
75 }
76 }
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test_get_candidates_empty_mask() {
87 let candidates = Candidates::new(); let r = 0;
89 let c = 0;
90 let cands = candidates.get_candidates(r, c);
91 assert_eq!(cands, vec![]);
92 }
93
94 #[test]
95 fn test_get_candidates_full_mask() {
96 let mut candidates = Candidates::new();
97 let r = 0;
98 let c = 0;
99 let full_mask = (1 << 9) - 1; candidates.set(r, c, full_mask);
103 let cands = candidates.get_candidates(r, c);
104 assert_eq!(cands, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
105 }
106
107 #[test]
108 fn test_get_candidates_single_candidate() {
109 let mut candidates = Candidates::new();
110 let r = 1;
111 let c = 2;
112
113 candidates.set(r, c, 1 << 0); let cands_1 = candidates.get_candidates(r, c);
116 assert_eq!(cands_1, vec![1]);
117
118 candidates.set(r, c, 1 << 4); let cands_5 = candidates.get_candidates(r, c);
121 assert_eq!(cands_5, vec![5]);
122
123 candidates.set(r, c, 1 << 8); let cands_9 = candidates.get_candidates(r, c);
126 assert_eq!(cands_9, vec![9]);
127 }
128
129 #[test]
130 fn test_get_candidates_multiple_candidates() {
131 let mut candidates = Candidates::new();
132 let r = 3;
133 let c = 4;
134
135 let mask = (1 << 1) | (1 << 3) | (1 << 6); candidates.set(r, c, mask);
141 let cands = candidates.get_candidates(r, c);
142 assert_eq!(cands, vec![2, 4, 7]);
143
144 let mask_1_9 = (1 << 0) | (1 << 8); candidates.set(r, c, mask_1_9);
148 let cands_1_9 = candidates.get_candidates(r, c);
149 assert_eq!(cands_1_9, vec![1, 9]);
150 }
151
152 #[test]
153 fn test_get_candidates_different_cells() {
154 let mut candidates = Candidates::new();
155
156 candidates.set(0, 0, (1 << 0) | (1 << 2)); candidates.set(8, 8, (1 << 5) | (1 << 7)); assert_eq!(candidates.get_candidates(0, 0), vec![1, 3]);
162 assert_eq!(candidates.get_candidates(8, 8), vec![6, 8]);
163 assert_eq!(candidates.get_candidates(0, 1), vec![]); }
165}