Apply naked single strategy to solve a NYT easy sudoku.
This commit is contained in:
parent
9901c6832f
commit
322011306a
14
src/board.rs
14
src/board.rs
|
@ -5,7 +5,7 @@ use crate::square::Square;
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct Board {
|
||||
squares: [Square; 81],
|
||||
pub squares: [Square; 81],
|
||||
}
|
||||
|
||||
impl Board {
|
||||
|
@ -63,16 +63,28 @@ impl Board {
|
|||
SquareIter::from_row(&self.squares, index / 9)
|
||||
}
|
||||
|
||||
pub fn row(&self, row: usize) -> SquareIter {
|
||||
SquareIter::from_row(&self.squares, row)
|
||||
}
|
||||
|
||||
pub fn col_from_index(&self, index: usize) -> SquareIter {
|
||||
SquareIter::from_col(&self.squares, index % 9)
|
||||
}
|
||||
|
||||
pub fn col(&self, col: usize) -> SquareIter {
|
||||
SquareIter::from_col(&self.squares, col)
|
||||
}
|
||||
|
||||
pub fn box_from_index(&self, index: usize) -> SquareIter {
|
||||
let row = index / 27;
|
||||
let col = (index % 9) / 3;
|
||||
SquareIter::from_box(&self.squares, (row * 3) + col)
|
||||
}
|
||||
|
||||
pub fn boxn(&self, boxn: usize) -> SquareIter {
|
||||
SquareIter::from_box(&self.squares, boxn)
|
||||
}
|
||||
|
||||
fn url_puzzle_str(&self) -> String {
|
||||
self.squares
|
||||
.iter()
|
||||
|
|
|
@ -28,7 +28,7 @@ fn to_col(squares: &[Square; 81], col: usize) -> [Square; 9] {
|
|||
]
|
||||
}
|
||||
|
||||
static BOX_ROOTS: [usize; 9] = [0, 3, 6, 27, 30, 36, 54, 57, 60];
|
||||
static BOX_ROOTS: [usize; 9] = [0, 3, 6, 27, 30, 33, 54, 57, 60];
|
||||
|
||||
fn to_box(squares: &[Square; 81], box_num: usize) -> [Square; 9] {
|
||||
let root = BOX_ROOTS[box_num];
|
||||
|
|
40
src/main.rs
40
src/main.rs
|
@ -1,8 +1,13 @@
|
|||
mod board;
|
||||
mod iterators;
|
||||
mod square;
|
||||
mod strategy;
|
||||
mod validator;
|
||||
|
||||
use strategy::Strategy;
|
||||
|
||||
fn main() {
|
||||
/*
|
||||
let nyt_hard = "3...184.9\
|
||||
8.47.....\
|
||||
.....61..\
|
||||
|
@ -12,7 +17,40 @@ fn main() {
|
|||
...6.1...\
|
||||
17.2.....\
|
||||
5........";
|
||||
let sudoku_board = board::Board::from_string(nyt_hard);
|
||||
*/
|
||||
let nyt_easy = "154..627.\
|
||||
..817..45\
|
||||
..649..1.\
|
||||
..9683...\
|
||||
321...86.\
|
||||
6....193.\
|
||||
5..9....3\
|
||||
.97...1.2\
|
||||
8...54...";
|
||||
let mut sudoku_board = board::Board::from_string(nyt_easy);
|
||||
|
||||
if let validator::BoardState::Broken(ind) = validator::validate_board(&sudoku_board) {
|
||||
println!("{}", sudoku_board.to_url());
|
||||
panic!(
|
||||
"Error invalid number at row {} col {}",
|
||||
(ind / 9) + 1,
|
||||
(ind % 9) + 1
|
||||
);
|
||||
}
|
||||
|
||||
while let Some(ns) = strategy::naked_single::NakedSingle::find_instance(&sudoku_board) {
|
||||
println!("Applying naked single.");
|
||||
sudoku_board = ns.apply(sudoku_board);
|
||||
|
||||
if let validator::BoardState::Broken(ind) = validator::validate_board(&sudoku_board) {
|
||||
println!("{}", sudoku_board.to_url());
|
||||
panic!(
|
||||
"Error invalid number at row {} col {}",
|
||||
(ind / 9) + 1,
|
||||
(ind % 9) + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
println!("{:#?}", sudoku_board);
|
||||
println!("{}", sudoku_board.to_url());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
use crate::board::Board;
|
||||
|
||||
pub trait Strategy {
|
||||
fn find_instance(board: &Board) -> Option<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn apply(&self, board: Board) -> Board;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pub mod base;
|
||||
|
||||
pub mod naked_single;
|
||||
|
||||
pub use base::Strategy;
|
|
@ -0,0 +1,33 @@
|
|||
use crate::{board::Board, square::Square, strategy::Strategy};
|
||||
|
||||
pub struct NakedSingle {
|
||||
index: usize,
|
||||
value: u8,
|
||||
}
|
||||
|
||||
impl Strategy for NakedSingle {
|
||||
fn find_instance(board: &crate::board::Board) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
board
|
||||
.squares
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, square)| match square {
|
||||
Square::Options(marks) => Some((index, marks)),
|
||||
_ => None,
|
||||
})
|
||||
.find(|(_index, marks)| marks.len() == 1)
|
||||
.map(|(index, marks)| NakedSingle {
|
||||
index,
|
||||
value: marks.first_index().unwrap() as u8 + 1,
|
||||
})
|
||||
}
|
||||
|
||||
fn apply(&self, mut board: Board) -> Board {
|
||||
board.squares[self.index] = Square::Value(self.value);
|
||||
board.restrict_marks();
|
||||
board
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
use crate::{board::Board, square::Square};
|
||||
|
||||
pub enum BoardState {
|
||||
Finished,
|
||||
Broken(u8),
|
||||
InProgress,
|
||||
}
|
||||
|
||||
fn are_unique<T>(iter: T) -> bool
|
||||
where
|
||||
T: Iterator<Item = u8>,
|
||||
{
|
||||
let mut bitmap: bitmaps::Bitmap<10> = bitmaps::Bitmap::new();
|
||||
|
||||
for item in iter {
|
||||
if bitmap.get(item as usize) {
|
||||
return false;
|
||||
}
|
||||
bitmap.set(item as usize, true);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn validate_board(board: &Board) -> BoardState {
|
||||
for r in 0..9 {
|
||||
if !are_unique(board.row(r).filter_map(|sq| match sq {
|
||||
Square::Value(x) => Some(x),
|
||||
_ => None,
|
||||
})) {
|
||||
// TODO: Add the index.
|
||||
return BoardState::Broken(0);
|
||||
}
|
||||
}
|
||||
|
||||
for c in 0..9 {
|
||||
if !are_unique(board.col(c).filter_map(|sq| match sq {
|
||||
Square::Value(x) => Some(x),
|
||||
_ => None,
|
||||
})) {
|
||||
// TODO: Add the index.
|
||||
return BoardState::Broken(0);
|
||||
}
|
||||
}
|
||||
|
||||
for b in 0..9 {
|
||||
if !are_unique(board.boxn(b).filter_map(|sq| match sq {
|
||||
Square::Value(x) => Some(x),
|
||||
_ => None,
|
||||
})) {
|
||||
// TODO: Add the index.
|
||||
return BoardState::Broken(0);
|
||||
}
|
||||
}
|
||||
|
||||
if board.squares.iter().any(|sq| match sq {
|
||||
Square::Options(_) => true,
|
||||
_ => false,
|
||||
}) {
|
||||
BoardState::InProgress
|
||||
} else {
|
||||
BoardState::Finished
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue