Apply naked single strategy to solve a NYT easy sudoku.

This commit is contained in:
Drew Galbraith 2024-06-20 22:46:55 -07:00
parent 9901c6832f
commit 322011306a
7 changed files with 163 additions and 3 deletions

View File

@ -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()

View File

@ -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];

View File

@ -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());
}

9
src/strategy/base.rs Normal file
View File

@ -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;
}

5
src/strategy/mod.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod base;
pub mod naked_single;
pub use base::Strategy;

View File

@ -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
}
}

63
src/validator.rs Normal file
View File

@ -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
}
}