|
@@ -13,10 +13,11 @@ const MAX_SIZE: u8 = 81;
|
|
|
use bit_field::BitField;
|
|
|
|
|
|
extern crate rand_chacha;
|
|
|
-use rand::prelude::*;
|
|
|
+// use rand::prelude::*;
|
|
|
use rand::seq::SliceRandom;
|
|
|
use rand_chacha::rand_core::SeedableRng;
|
|
|
use rand_chacha::ChaCha20Rng;
|
|
|
+use rand::distributions::{Distribution, Uniform};
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
|
pub struct Possible(u16);
|
|
@@ -139,7 +140,7 @@ mod tests {
|
|
|
pub type SudokuBoard = [u8; MAX_SIZE as usize];
|
|
|
pub type SudokuPossible = [Possible; MAX_SIZE as usize];
|
|
|
|
|
|
-#[derive(Debug)]
|
|
|
+#[derive(Debug, Clone, Copy)]
|
|
|
pub struct Sudoku {
|
|
|
pub board: [u8; MAX_SIZE as usize],
|
|
|
// pub possible: [HashSet<u8>; MAX_SIZE as usize],
|
|
@@ -185,6 +186,12 @@ impl Sudoku {
|
|
|
s
|
|
|
}
|
|
|
|
|
|
+ pub fn clear_possible(&mut self) {
|
|
|
+ let mut initial = Possible(set_bits(10));
|
|
|
+ initial.set(0, false);
|
|
|
+ self.possible = [initial; MAX_SIZE as usize];
|
|
|
+ }
|
|
|
+
|
|
|
pub fn clear(&mut self) {
|
|
|
let mut initial = Possible(set_bits(10));
|
|
|
initial.set(0, false);
|
|
@@ -262,6 +269,10 @@ impl Sudoku {
|
|
|
result
|
|
|
}
|
|
|
|
|
|
+ pub fn get(&self, x:u8, y:u8) -> u8 {
|
|
|
+ self.board[pos(x,y) as usize]
|
|
|
+ }
|
|
|
+
|
|
|
pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
self.board[pos(x, y) as usize] = value;
|
|
|
// Ok, update the possible
|
|
@@ -289,6 +300,31 @@ impl Sudoku {
|
|
|
self.possible[pos(x, y) as usize].clear();
|
|
|
}
|
|
|
|
|
|
+ pub fn reset_possible(&mut self) {
|
|
|
+ // Reset the possible.
|
|
|
+ self.clear_possible();
|
|
|
+ for y in 0..WIDTH {
|
|
|
+ for x in 0..WIDTH {
|
|
|
+ let item: u8 = self.board[pos(x as u8, y as u8) as usize];
|
|
|
+ if item != 0 {
|
|
|
+ let mut g: &Group = for_row(y);
|
|
|
+ for g in g.0 {
|
|
|
+ self.possible[g as usize].set(item, false);
|
|
|
+ }
|
|
|
+ g = for_column(x);
|
|
|
+ for g in g.0 {
|
|
|
+ self.possible[g as usize].set(item, false);
|
|
|
+ }
|
|
|
+ g = for_cell(which_cell(x,y));
|
|
|
+ for g in g.0 {
|
|
|
+ self.possible[g as usize].set(item, false);
|
|
|
+ }
|
|
|
+ self.possible[pos(x,y) as usize].clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
pub fn display(&self) {
|
|
|
println!("╔═══╦═══╦═══╗");
|
|
|
for y in 0..WIDTH {
|
|
@@ -443,6 +479,7 @@ impl Sudoku {
|
|
|
let mut rng = ChaCha20Rng::from_entropy();
|
|
|
|
|
|
self.fill_board(&mut rng);
|
|
|
+ // Ok, this gives us a random (but fully solved) puzzle.
|
|
|
}
|
|
|
|
|
|
fn fill_board(&mut self, rng: &mut ChaCha20Rng) -> bool {
|
|
@@ -493,7 +530,49 @@ impl Sudoku {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- pub fn make_(&mut self) {
|
|
|
+ pub fn remove(&mut self) -> bool {
|
|
|
+ // Find a number, remove it. Save position.
|
|
|
+ let mut rng = ChaCha20Rng::from_entropy();
|
|
|
+ let puzrange = Uniform::new(0, WIDTH);
|
|
|
+ let mut x = 0;
|
|
|
+ let mut y = 0;
|
|
|
+ let mut value: u8 = 0;
|
|
|
+
|
|
|
+ while value == 0 {
|
|
|
+ x = puzrange.sample(&mut rng);
|
|
|
+ y = puzrange.sample(&mut rng);
|
|
|
+ value = self.get(x,y);
|
|
|
+ }
|
|
|
+
|
|
|
+ self.set(x,y,0);
|
|
|
+
|
|
|
+ // Clone, and solve by logic.
|
|
|
+ let mut puzcopy = self.clone();
|
|
|
+
|
|
|
+ puzcopy.reset_possible();
|
|
|
+ /*
|
|
|
+ puzcopy.display();
|
|
|
+ puzcopy.display_possible();
|
|
|
+ */
|
|
|
+ // If solvable, return true.
|
|
|
+ while puzcopy.solve(false) {};
|
|
|
+
|
|
|
+ /*
|
|
|
+ puzcopy.display();
|
|
|
+ puzcopy.display_possible();
|
|
|
+ */
|
|
|
+
|
|
|
+ if puzcopy.puzzle_complete() {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If not solvable, restore number, return false.
|
|
|
+ self.set(x,y,value);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ fn make_(&mut self) {
|
|
|
self.clear();
|
|
|
|
|
|
let mut rng = ChaCha20Rng::from_entropy();
|
|
@@ -530,6 +609,7 @@ impl Sudoku {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ */
|
|
|
|
|
|
pub fn solve(&mut self, debug: bool) -> bool {
|
|
|
// Pass 1: Look for singles in the possible sets.
|