use super::board::Board;
use super::masks::Masks;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Candidates {
cache: [[u16; 9]; 9],
}
impl Candidates {
pub(super) fn new() -> Self {
Candidates { cache: [[0; 9]; 9] }
}
pub(super) fn get(&self, r: usize, c: usize) -> u16 {
self.cache[r][c]
}
pub fn get_candidates(&self, r: usize, c: usize) -> Vec<u8> {
let mask = self.get(r, c);
let mut candidates = Vec::new();
for i in 0..9 {
if (mask >> i) & 1 == 1 {
candidates.push((i + 1) as u8);
}
}
candidates
}
pub(super) fn set(&mut self, r: usize, c: usize, mask: u16) {
self.cache[r][c] = mask;
}
pub(super) fn update_affected_cells(
&mut self,
r: usize,
c: usize,
masks: &Masks,
board: &Board,
) {
self.update_affected_cells_for(r, c, masks, board, None);
}
pub(super) fn update_affected_cells_for(
&mut self,
r: usize,
c: usize,
masks: &Masks,
board: &Board,
placed_num: Option<u8>,
) {
if board.is_empty(r, c) {
self.cache[r][c] = masks.compute_candidates_mask_for_cell(r, c);
} else {
self.cache[r][c] = 0;
}
let num_bit = placed_num.map(|n| 1u16 << (n - 1));
for i in 0..9 {
if board.is_empty(r, i) && i != c {
if let Some(bit) = num_bit {
if self.cache[r][i] & bit == 0 {
continue;
}
}
self.cache[r][i] = masks.compute_candidates_mask_for_cell(r, i);
}
if board.is_empty(i, c) && i != r {
if let Some(bit) = num_bit {
if self.cache[i][c] & bit == 0 {
continue;
}
}
self.cache[i][c] = masks.compute_candidates_mask_for_cell(i, c);
}
}
let box_idx = Masks::get_box_idx(r, c);
let start_row = (box_idx / 3) * 3;
let start_col = (box_idx % 3) * 3;
for r_offset in 0..3 {
for c_offset in 0..3 {
let cur_r = start_row + r_offset;
let cur_c = start_col + c_offset;
if (cur_r == r) || (cur_c == c) {
continue;
}
if board.is_empty(cur_r, cur_c) {
if let Some(bit) = num_bit {
if self.cache[cur_r][cur_c] & bit == 0 {
continue;
}
}
self.cache[cur_r][cur_c] = masks.compute_candidates_mask_for_cell(cur_r, cur_c);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_candidates_empty_mask() {
let candidates = Candidates::new(); let r = 0;
let c = 0;
let cands: Vec<u8> = candidates.get_candidates(r, c);
assert_eq!(cands, Vec::<u8>::new());
}
#[test]
fn test_get_candidates_full_mask() {
let mut candidates = Candidates::new();
let r = 0;
let c = 0;
let full_mask = (1 << 9) - 1; candidates.set(r, c, full_mask);
let cands = candidates.get_candidates(r, c);
assert_eq!(cands, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
}
#[test]
fn test_get_candidates_single_candidate() {
let mut candidates = Candidates::new();
let r = 1;
let c = 2;
candidates.set(r, c, 1 << 0); let cands_1 = candidates.get_candidates(r, c);
assert_eq!(cands_1, vec![1]);
candidates.set(r, c, 1 << 4); let cands_5 = candidates.get_candidates(r, c);
assert_eq!(cands_5, vec![5]);
candidates.set(r, c, 1 << 8); let cands_9 = candidates.get_candidates(r, c);
assert_eq!(cands_9, vec![9]);
}
#[test]
fn test_get_candidates_multiple_candidates() {
let mut candidates = Candidates::new();
let r = 3;
let c = 4;
let mask = (1 << 1) | (1 << 3) | (1 << 6); candidates.set(r, c, mask);
let cands = candidates.get_candidates(r, c);
assert_eq!(cands, vec![2, 4, 7]);
let mask_1_9 = (1 << 0) | (1 << 8); candidates.set(r, c, mask_1_9);
let cands_1_9 = candidates.get_candidates(r, c);
assert_eq!(cands_1_9, vec![1, 9]);
}
#[test]
fn test_get_candidates_different_cells() {
let mut candidates = Candidates::new();
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]);
assert_eq!(candidates.get_candidates(8, 8), vec![6, 8]);
assert_eq!(candidates.get_candidates(0, 1), Vec::<u8>::new()); }
}