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)]
|
#[derive(Clone)]
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
squares: [Square; 81],
|
pub squares: [Square; 81],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Board {
|
impl Board {
|
||||||
|
@ -63,16 +63,28 @@ impl Board {
|
||||||
SquareIter::from_row(&self.squares, index / 9)
|
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 {
|
pub fn col_from_index(&self, index: usize) -> SquareIter {
|
||||||
SquareIter::from_col(&self.squares, index % 9)
|
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 {
|
pub fn box_from_index(&self, index: usize) -> SquareIter {
|
||||||
let row = index / 27;
|
let row = index / 27;
|
||||||
let col = (index % 9) / 3;
|
let col = (index % 9) / 3;
|
||||||
SquareIter::from_box(&self.squares, (row * 3) + col)
|
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 {
|
fn url_puzzle_str(&self) -> String {
|
||||||
self.squares
|
self.squares
|
||||||
.iter()
|
.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] {
|
fn to_box(squares: &[Square; 81], box_num: usize) -> [Square; 9] {
|
||||||
let root = BOX_ROOTS[box_num];
|
let root = BOX_ROOTS[box_num];
|
||||||
|
|
40
src/main.rs
40
src/main.rs
|
@ -1,8 +1,13 @@
|
||||||
mod board;
|
mod board;
|
||||||
mod iterators;
|
mod iterators;
|
||||||
mod square;
|
mod square;
|
||||||
|
mod strategy;
|
||||||
|
mod validator;
|
||||||
|
|
||||||
|
use strategy::Strategy;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
/*
|
||||||
let nyt_hard = "3...184.9\
|
let nyt_hard = "3...184.9\
|
||||||
8.47.....\
|
8.47.....\
|
||||||
.....61..\
|
.....61..\
|
||||||
|
@ -12,7 +17,40 @@ fn main() {
|
||||||
...6.1...\
|
...6.1...\
|
||||||
17.2.....\
|
17.2.....\
|
||||||
5........";
|
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);
|
||||||
println!("{}", sudoku_board.to_url());
|
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