Add a Box-Line Reduction Strategy.
This commit is contained in:
parent
ea1d345a4e
commit
5c6ce777e8
18
src/board.rs
18
src/board.rs
|
@ -8,6 +8,20 @@ pub struct Board {
|
||||||
pub squares: [Square; 81],
|
pub squares: [Square; 81],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn index_to_row(index: u8) -> u8 {
|
||||||
|
index / 9
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index_to_col(index: u8) -> u8 {
|
||||||
|
index % 9
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index_to_box(index: u8) -> u8 {
|
||||||
|
let row = index / 27;
|
||||||
|
let col = (index % 9) / 3;
|
||||||
|
(row * 3) + col
|
||||||
|
}
|
||||||
|
|
||||||
impl Board {
|
impl Board {
|
||||||
pub fn from_string(str: &str) -> Board {
|
pub fn from_string(str: &str) -> Board {
|
||||||
if str.len() != 81 {
|
if str.len() != 81 {
|
||||||
|
@ -58,9 +72,7 @@ impl Board {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn box_from_index(&self, index: u8) -> SquareIter {
|
pub fn box_from_index(&self, index: u8) -> SquareIter {
|
||||||
let row = index / 27;
|
SquareIter::from_box(&self.squares, index_to_box(index))
|
||||||
let col = (index % 9) / 3;
|
|
||||||
SquareIter::from_box(&self.squares, (row * 3) + col)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn boxn(&self, boxn: u8) -> SquareIter {
|
pub fn boxn(&self, boxn: u8) -> SquareIter {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::board::Board;
|
use crate::board::Board;
|
||||||
use crate::strategy::{HiddenSingle, NakedSingle, Strategy, StrategyFn};
|
use crate::strategy::{BoxLine, HiddenSingle, NakedSingle, Strategy, StrategyFn};
|
||||||
use crate::validator;
|
use crate::validator;
|
||||||
|
|
||||||
fn try_strategies(strategies: &[StrategyFn], sudoku_board: &Board) -> Option<Box<dyn Strategy>> {
|
fn try_strategies(strategies: &[StrategyFn], sudoku_board: &Board) -> Option<Box<dyn Strategy>> {
|
||||||
|
@ -21,10 +21,15 @@ pub fn solve_board(mut sudoku_board: Board) -> Result<Board, Board> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let strategies: [StrategyFn; 2] = [NakedSingle::find_instance, HiddenSingle::find_instance];
|
let strategies: [StrategyFn; 3] = [
|
||||||
|
NakedSingle::find_instance,
|
||||||
|
HiddenSingle::find_instance,
|
||||||
|
BoxLine::find_instance,
|
||||||
|
];
|
||||||
|
|
||||||
while let Some(ns) = try_strategies(&strategies, &sudoku_board) {
|
while let Some(strat) = try_strategies(&strategies, &sudoku_board) {
|
||||||
sudoku_board = ns.apply(sudoku_board);
|
println!("{}", strat);
|
||||||
|
sudoku_board = strat.apply(sudoku_board);
|
||||||
|
|
||||||
if let validator::BoardState::Broken(ind) = validator::validate_board(&sudoku_board) {
|
if let validator::BoardState::Broken(ind) = validator::validate_board(&sudoku_board) {
|
||||||
println!("{}", sudoku_board.to_url());
|
println!("{}", sudoku_board.to_url());
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
use crate::board::Board;
|
use crate::board::Board;
|
||||||
|
|
||||||
pub type StrategyFn = fn(&Board) -> Option<Box<dyn Strategy>>;
|
pub type StrategyFn = fn(&Board) -> Option<Box<dyn Strategy>>;
|
||||||
|
|
||||||
pub trait Strategy {
|
pub trait Strategy: fmt::Display {
|
||||||
fn find_instance(board: &Board) -> Option<Box<dyn Strategy>>
|
fn find_instance(board: &Board) -> Option<Box<dyn Strategy>>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
board::{index_to_box, index_to_col, index_to_row, Board},
|
||||||
|
square::Square,
|
||||||
|
strategy::Strategy,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Seen {
|
||||||
|
NotSeen,
|
||||||
|
SeenInBox(u8),
|
||||||
|
SeenInMultiple,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum RowCol {
|
||||||
|
Row(u8),
|
||||||
|
Col(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BoxLine {
|
||||||
|
rowcol: RowCol,
|
||||||
|
boxn: u8,
|
||||||
|
value: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Strategy for BoxLine {
|
||||||
|
fn find_instance(board: &Board) -> Option<Box<dyn Strategy>> {
|
||||||
|
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) {
|
||||||
|
let boxn = index_to_box(i);
|
||||||
|
match curr {
|
||||||
|
Seen::NotSeen => Seen::SeenInBox(boxn),
|
||||||
|
Seen::SeenInBox(b) => {
|
||||||
|
if b == boxn {
|
||||||
|
curr
|
||||||
|
} else {
|
||||||
|
Seen::SeenInMultiple
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Seen::SeenInMultiple,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
curr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => curr,
|
||||||
|
});
|
||||||
|
|
||||||
|
match seen {
|
||||||
|
Seen::SeenInBox(b) => {
|
||||||
|
if board.boxn(b).any(|sq| match sq {
|
||||||
|
Square::Marks(i, marks) => {
|
||||||
|
(index_to_row(i) != r) && marks.get(n as usize)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}) {
|
||||||
|
return Some(Box::new(BoxLine {
|
||||||
|
rowcol: RowCol::Row(r),
|
||||||
|
boxn: b,
|
||||||
|
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) {
|
||||||
|
let boxn = index_to_box(i);
|
||||||
|
match curr {
|
||||||
|
Seen::NotSeen => Seen::SeenInBox(boxn),
|
||||||
|
Seen::SeenInBox(b) => {
|
||||||
|
if b == boxn {
|
||||||
|
curr
|
||||||
|
} else {
|
||||||
|
Seen::SeenInMultiple
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Seen::SeenInMultiple,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
curr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => curr,
|
||||||
|
});
|
||||||
|
|
||||||
|
match seen {
|
||||||
|
Seen::SeenInBox(b) => {
|
||||||
|
if board.boxn(b).any(|sq| match sq {
|
||||||
|
Square::Marks(i, marks) => {
|
||||||
|
(index_to_col(i) != c) && marks.get(n as usize)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}) {
|
||||||
|
return Some(Box::new(BoxLine {
|
||||||
|
rowcol: RowCol::Col(c),
|
||||||
|
boxn: b,
|
||||||
|
value: n,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(&self, mut board: Board) -> Board {
|
||||||
|
for ref mut square in board.squares.iter_mut() {
|
||||||
|
if let Square::Marks(i, ref mut marks) = square {
|
||||||
|
if index_to_box(*i as u8) == self.boxn {
|
||||||
|
match self.rowcol {
|
||||||
|
RowCol::Row(r) => {
|
||||||
|
if index_to_row(*i) != r {
|
||||||
|
marks.set(self.value as usize, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowCol::Col(c) => {
|
||||||
|
if index_to_col(*i) != c {
|
||||||
|
marks.set(self.value as usize, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
board
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BoxLine {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self.rowcol {
|
||||||
|
RowCol::Row(r) => write!(
|
||||||
|
f,
|
||||||
|
"Box Line Reduction on {} in row {} in box {}",
|
||||||
|
self.value,
|
||||||
|
r + 1,
|
||||||
|
self.boxn + 1,
|
||||||
|
),
|
||||||
|
RowCol::Col(c) => write!(
|
||||||
|
f,
|
||||||
|
"Box Line Reduction on {} in col {} in box {}",
|
||||||
|
self.value,
|
||||||
|
c + 1,
|
||||||
|
self.boxn + 1,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::board::Board;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::board::{index_to_col, index_to_row, Board};
|
||||||
use crate::square::Square;
|
use crate::square::Square;
|
||||||
use crate::strategy::Strategy;
|
use crate::strategy::Strategy;
|
||||||
|
|
||||||
|
@ -99,3 +101,15 @@ impl Strategy for HiddenSingle {
|
||||||
board
|
board
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for HiddenSingle {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Hidden Single {} at row {} col {}",
|
||||||
|
self.value,
|
||||||
|
index_to_row(self.index) + 1,
|
||||||
|
index_to_col(self.index) + 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
pub mod base;
|
pub mod base;
|
||||||
|
|
||||||
|
pub mod box_line;
|
||||||
pub mod hidden_single;
|
pub mod hidden_single;
|
||||||
pub mod naked_single;
|
pub mod naked_single;
|
||||||
|
|
||||||
pub use base::Strategy;
|
pub use base::Strategy;
|
||||||
pub use base::StrategyFn;
|
pub use base::StrategyFn;
|
||||||
|
|
||||||
|
pub use box_line::BoxLine;
|
||||||
pub use hidden_single::HiddenSingle;
|
pub use hidden_single::HiddenSingle;
|
||||||
pub use naked_single::NakedSingle;
|
pub use naked_single::NakedSingle;
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
use crate::{board::Board, square::Square, strategy::Strategy};
|
use std::fmt::{Display, Write};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
board::{index_to_col, index_to_row, Board},
|
||||||
|
square::Square,
|
||||||
|
strategy::Strategy,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct NakedSingle {
|
pub struct NakedSingle {
|
||||||
index: u8,
|
index: u8,
|
||||||
|
@ -32,3 +38,15 @@ impl Strategy for NakedSingle {
|
||||||
board
|
board
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for NakedSingle {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Naked Single {} at row {} col {}",
|
||||||
|
self.value,
|
||||||
|
index_to_row(self.index) + 1,
|
||||||
|
index_to_col(self.index) + 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue