diff --git a/src/solver/solver.rs b/src/solver/solver.rs index 57b8eab..99631a9 100644 --- a/src/solver/solver.rs +++ b/src/solver/solver.rs @@ -1,5 +1,5 @@ use crate::board::Board; -use crate::strategy::{NakedSingle, Strategy, StrategyFn}; +use crate::strategy::{HiddenSingle, NakedSingle, Strategy, StrategyFn}; use crate::validator; fn try_strategies(strategies: &[StrategyFn], sudoku_board: &Board) -> Option> { @@ -21,7 +21,7 @@ pub fn solve_board(mut sudoku_board: Board) -> Result { ); } - let strategies: [StrategyFn; 1] = [NakedSingle::find_instance]; + let strategies: [StrategyFn; 2] = [NakedSingle::find_instance, HiddenSingle::find_instance]; while let Some(ns) = try_strategies(&strategies, &sudoku_board) { sudoku_board = ns.apply(sudoku_board); diff --git a/src/strategy/hidden_single.rs b/src/strategy/hidden_single.rs new file mode 100644 index 0000000..79c3a9b --- /dev/null +++ b/src/strategy/hidden_single.rs @@ -0,0 +1,101 @@ +use crate::board::Board; +use crate::square::Square; +use crate::strategy::Strategy; + +pub struct HiddenSingle { + index: u8, + value: u8, +} + +enum Seen { + NotSeen, + SeenOnce(u8), + SeenTwice, +} + +impl Strategy for HiddenSingle { + fn find_instance(board: &Board) -> Option> { + for r in 0..9 { + for n in 1..=9 { + let seen = board + .row(r) + .fold(Seen::NotSeen, |curr, square| match square { + Square::Marks(i, opts) => { + if opts.get(n as usize) { + match curr { + Seen::NotSeen => Seen::SeenOnce(i), + _ => Seen::SeenTwice, + } + } else { + curr + } + } + _ => curr, + }); + match seen { + Seen::SeenOnce(i) => { + return Some(Box::new(HiddenSingle { index: i, value: n })) + } + _ => {} + } + } + } + for c in 0..9 { + for n in 1..=9 { + let seen = board + .col(c) + .fold(Seen::NotSeen, |curr, square| match square { + Square::Marks(i, opts) => { + if opts.get(n as usize) { + match curr { + Seen::NotSeen => Seen::SeenOnce(i), + _ => Seen::SeenTwice, + } + } else { + curr + } + } + _ => curr, + }); + match seen { + Seen::SeenOnce(i) => { + return Some(Box::new(HiddenSingle { index: i, value: n })) + } + _ => {} + } + } + } + for b in 0..9 { + for n in 1..=9 { + let seen = board + .boxn(b) + .fold(Seen::NotSeen, |curr, square| match square { + Square::Marks(i, opts) => { + if opts.get(n as usize) { + match curr { + Seen::NotSeen => Seen::SeenOnce(i), + _ => Seen::SeenTwice, + } + } else { + curr + } + } + _ => curr, + }); + match seen { + Seen::SeenOnce(i) => { + return Some(Box::new(HiddenSingle { index: i, value: n })) + } + _ => {} + } + } + } + None + } + + fn apply(&self, mut board: Board) -> Board { + board.squares[self.index as usize] = Square::Value(self.index, self.value); + board.restrict_marks(); + board + } +} diff --git a/src/strategy/mod.rs b/src/strategy/mod.rs index 7d3c11d..a22f2f8 100644 --- a/src/strategy/mod.rs +++ b/src/strategy/mod.rs @@ -1,8 +1,10 @@ pub mod base; +pub mod hidden_single; pub mod naked_single; pub use base::Strategy; pub use base::StrategyFn; +pub use hidden_single::HiddenSingle; pub use naked_single::NakedSingle;