From 9901c6832ffb9158d13ce6fe329dafcc40e0e796 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Thu, 20 Jun 2024 21:53:03 -0700 Subject: [PATCH] Initial cell restrictions and url print. --- Cargo.lock | 9 ++++ Cargo.toml | 1 + src/board.rs | 131 +++++++++++++++++++++++++++++++++++------------ src/iterators.rs | 87 +++++++++++++++++++++++++++++++ src/main.rs | 3 ++ src/square.rs | 32 ++++++++++++ 6 files changed, 231 insertions(+), 32 deletions(-) create mode 100644 src/iterators.rs create mode 100644 src/square.rs diff --git a/Cargo.lock b/Cargo.lock index d3dd20b..5645432 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "bitmaps" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" + [[package]] name = "sudoku-solver" version = "0.1.0" +dependencies = [ + "bitmaps", +] diff --git a/Cargo.toml b/Cargo.toml index 17834c5..027a8ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bitmaps = "3.*" diff --git a/src/board.rs b/src/board.rs index 665e5c1..35ab234 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,37 +1,11 @@ use std::fmt::{self, Write}; -pub enum Square { - Value(u8), - Options(Vec), -} - -impl Square { - pub fn from_char(c: char) -> Square { - match c { - '1' => Square::Value(1), - '2' => Square::Value(2), - '3' => Square::Value(3), - '4' => Square::Value(4), - '5' => Square::Value(5), - '6' => Square::Value(6), - '7' => Square::Value(7), - '8' => Square::Value(8), - '9' => Square::Value(9), - '.' => Square::Options(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]), - _ => panic!("Unexpected character in input: {}", c), - } - } - - pub fn to_str(&self) -> String { - match self { - Square::Options(_) => " ".to_string(), - Square::Value(v) => v.to_string(), - } - } -} +use crate::iterators::SquareIter; +use crate::square::Square; +#[derive(Clone)] pub struct Board { - squares: Vec, + squares: [Square; 81], } impl Board { @@ -41,9 +15,102 @@ impl Board { } let mut squares = Vec::new(); for c in str.chars() { - squares.push(Square::from_char(c)) + squares.push(Square::from_char(c)); } - Board { squares } + let mut board = Board { + squares: squares.try_into().unwrap(), + }; + board.restrict_marks(); + board + } + + pub fn restrict_marks(&mut self) { + let self_copy = self.clone(); + for (index, ref mut square) in self.squares.iter_mut().enumerate() { + if let Square::Options(ref mut opt) = square { + self_copy + .row_from_index(index) + .filter_map(|sq| match sq { + Square::Value(x) => Some(x), + _ => None, + }) + .for_each(|value| { + opt.set((value - 1) as usize, false); + }); + self_copy + .col_from_index(index) + .filter_map(|sq| match sq { + Square::Value(x) => Some(x), + _ => None, + }) + .for_each(|value| { + opt.set((value - 1) as usize, false); + }); + self_copy + .box_from_index(index) + .filter_map(|sq| match sq { + Square::Value(x) => Some(x), + _ => None, + }) + .for_each(|value| { + opt.set((value - 1) as usize, false); + }); + } + } + } + + pub fn row_from_index(&self, index: usize) -> SquareIter { + SquareIter::from_row(&self.squares, index / 9) + } + + pub fn col_from_index(&self, index: usize) -> SquareIter { + SquareIter::from_col(&self.squares, index % 9) + } + + 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) + } + + fn url_puzzle_str(&self) -> String { + self.squares + .iter() + .map(|sq| match sq { + Square::Value(x) => x.to_string(), + _ => ".".to_string(), + }) + .fold(String::new(), |a, b| a + &b) + } + + fn url_marks_str(&self) -> String { + self.squares + .iter() + .map(|sq| match sq { + Square::Options(opts) => { + opts.into_iter() + .map(|a| a + 1) + .fold(String::new(), |mut a, b| { + a.push_str(&b.to_string()); + a + }) + } + _ => String::new(), + }) + .fold(String::new(), |mut a, b| { + a.push(','); + a.push_str(&b); + a + })[1..] + .to_string() + } + + pub fn to_url(&self) -> String { + format!( + "https://tiramisu.one/sudoku/?p={}&m={}", + self.url_puzzle_str(), + self.url_marks_str() + ) } } diff --git a/src/iterators.rs b/src/iterators.rs new file mode 100644 index 0000000..df6e06a --- /dev/null +++ b/src/iterators.rs @@ -0,0 +1,87 @@ +use crate::square::Square; + +fn to_row(squares: &[Square; 81], row: usize) -> [Square; 9] { + [ + squares[(9 * row) + 0].clone(), + squares[(9 * row) + 1].clone(), + squares[(9 * row) + 2].clone(), + squares[(9 * row) + 3].clone(), + squares[(9 * row) + 4].clone(), + squares[(9 * row) + 5].clone(), + squares[(9 * row) + 6].clone(), + squares[(9 * row) + 7].clone(), + squares[(9 * row) + 8].clone(), + ] +} + +fn to_col(squares: &[Square; 81], col: usize) -> [Square; 9] { + [ + squares[(9 * 0) + col].clone(), + squares[(9 * 1) + col].clone(), + squares[(9 * 2) + col].clone(), + squares[(9 * 3) + col].clone(), + squares[(9 * 4) + col].clone(), + squares[(9 * 5) + col].clone(), + squares[(9 * 6) + col].clone(), + squares[(9 * 7) + col].clone(), + squares[(9 * 8) + col].clone(), + ] +} + +static BOX_ROOTS: [usize; 9] = [0, 3, 6, 27, 30, 36, 54, 57, 60]; + +fn to_box(squares: &[Square; 81], box_num: usize) -> [Square; 9] { + let root = BOX_ROOTS[box_num]; + [ + squares[root].clone(), + squares[root + 1].clone(), + squares[root + 2].clone(), + squares[root + 9].clone(), + squares[root + 10].clone(), + squares[root + 11].clone(), + squares[root + 18].clone(), + squares[root + 19].clone(), + squares[root + 20].clone(), + ] +} + +pub struct SquareIter { + squares: [Square; 9], + curr: usize, +} + +impl SquareIter { + pub fn from_row(squares: &[Square; 81], row: usize) -> SquareIter { + SquareIter { + squares: to_row(squares, row), + curr: 0, + } + } + + pub fn from_col(squares: &[Square; 81], col: usize) -> SquareIter { + SquareIter { + squares: to_col(squares, col), + curr: 0, + } + } + + pub fn from_box(squares: &[Square; 81], boxn: usize) -> SquareIter { + SquareIter { + squares: to_box(squares, boxn), + curr: 0, + } + } +} + +impl Iterator for SquareIter { + type Item = Square; + + fn next(&mut self) -> Option { + if self.curr >= 9 { + None + } else { + self.curr += 1; + Some(self.squares[self.curr - 1].clone()) + } + } +} diff --git a/src/main.rs b/src/main.rs index 0403a25..9669041 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ mod board; +mod iterators; +mod square; fn main() { let nyt_hard = "3...184.9\ @@ -12,4 +14,5 @@ fn main() { 5........"; let sudoku_board = board::Board::from_string(nyt_hard); println!("{:#?}", sudoku_board); + println!("{}", sudoku_board.to_url()); } diff --git a/src/square.rs b/src/square.rs new file mode 100644 index 0000000..7a741a0 --- /dev/null +++ b/src/square.rs @@ -0,0 +1,32 @@ +use bitmaps::Bitmap; + +#[derive(Clone, Debug)] +pub enum Square { + Value(u8), + Options(Bitmap<9>), +} + +impl Square { + pub fn from_char(c: char) -> Square { + match c { + '1' => Square::Value(1), + '2' => Square::Value(2), + '3' => Square::Value(3), + '4' => Square::Value(4), + '5' => Square::Value(5), + '6' => Square::Value(6), + '7' => Square::Value(7), + '8' => Square::Value(8), + '9' => Square::Value(9), + '.' => Square::Options(Bitmap::mask(9)), + _ => panic!("Unexpected character in input: {}", c), + } + } + + pub fn to_str(&self) -> String { + match self { + Square::Options(_) => " ".to_string(), + Square::Value(v) => v.to_string(), + } + } +}