|
@@ -1,7 +1,7 @@
|
|
// pub mod group;
|
|
// pub mod group;
|
|
use crate::group::*;
|
|
use crate::group::*;
|
|
|
|
|
|
-use std::collections::HashSet;
|
|
|
|
|
|
+// use std::collections::HashSet;
|
|
use std::string::String;
|
|
use std::string::String;
|
|
|
|
|
|
/// Width of the sudoku board.
|
|
/// Width of the sudoku board.
|
|
@@ -12,11 +12,17 @@ const MAX_SIZE: u8 = 81;
|
|
// Use bitfields instead of HashSets.
|
|
// Use bitfields instead of HashSets.
|
|
use bit_field::BitField;
|
|
use bit_field::BitField;
|
|
|
|
|
|
|
|
+extern crate rand_chacha;
|
|
|
|
+use rand::prelude::*;
|
|
|
|
+use rand_chacha::rand_core::SeedableRng;
|
|
|
|
+use rand_chacha::ChaCha20Rng;
|
|
|
|
+use rand::seq::SliceRandom;
|
|
|
|
+
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
pub struct Possible(u16);
|
|
pub struct Possible(u16);
|
|
|
|
|
|
pub const fn set_bits(bits: u8) -> u16 {
|
|
pub const fn set_bits(bits: u8) -> u16 {
|
|
- (1 << (bits )) - 1
|
|
|
|
|
|
+ (1 << (bits)) - 1
|
|
}
|
|
}
|
|
|
|
|
|
impl Possible {
|
|
impl Possible {
|
|
@@ -26,12 +32,12 @@ impl Possible {
|
|
|
|
|
|
pub fn set(&mut self, bit: u8, value: bool) {
|
|
pub fn set(&mut self, bit: u8, value: bool) {
|
|
// print!("{} set {}={}", self.0, bit, value);
|
|
// print!("{} set {}={}", self.0, bit, value);
|
|
- self.0.set_bit((bit-1) as usize, value);
|
|
|
|
|
|
+ self.0.set_bit((bit - 1) as usize, value);
|
|
// println!("{}", self.0);
|
|
// println!("{}", self.0);
|
|
}
|
|
}
|
|
|
|
|
|
pub fn get(&self, bit: u8) -> bool {
|
|
pub fn get(&self, bit: u8) -> bool {
|
|
- self.0.get_bit((bit-1) as usize)
|
|
|
|
|
|
+ self.0.get_bit((bit - 1) as usize)
|
|
}
|
|
}
|
|
|
|
|
|
pub fn set_bits(&mut self, bits: u8) {
|
|
pub fn set_bits(&mut self, bits: u8) {
|
|
@@ -162,31 +168,15 @@ impl Sudoku {
|
|
|
|
|
|
let s = Sudoku {
|
|
let s = Sudoku {
|
|
board: [0; MAX_SIZE as usize],
|
|
board: [0; MAX_SIZE as usize],
|
|
- // possible: [(); MAX_SIZE as usize].map(|_| HashSet::from_iter(1..=9)),
|
|
|
|
possible: [initial; MAX_SIZE as usize],
|
|
possible: [initial; MAX_SIZE as usize],
|
|
- // possible: [HashSet::from_iter(1..=9); MAX_SIZE as usize],
|
|
|
|
- // possible: [[0; SIZE as usize]; MAX_SIZE as usize],
|
|
|
|
- // possible: [(0..MAX_SIZE).map( |_| (1..=9).collect())],
|
|
|
|
- // possible: [(1..=9).map(|_| HashSet::new()).collect(); MAX_SIZE as usize],
|
|
|
|
};
|
|
};
|
|
s
|
|
s
|
|
}
|
|
}
|
|
|
|
|
|
pub fn clear(&mut self) {
|
|
pub fn clear(&mut self) {
|
|
let initial = Possible(set_bits(9));
|
|
let initial = Possible(set_bits(9));
|
|
-
|
|
|
|
- for x in 0..MAX_SIZE {
|
|
|
|
- self.board[x as usize] = 0;
|
|
|
|
- self.possible[x as usize] = initial;
|
|
|
|
- // self.possible = [(); MAX_SIZE as usize].map(|_| HashSet::from_iter(1..=9));
|
|
|
|
- /*
|
|
|
|
- self.possible[x as usize].clear();
|
|
|
|
- for i in 1..=9 {
|
|
|
|
- self.possible[x as usize].insert(i);
|
|
|
|
- }
|
|
|
|
- */
|
|
|
|
- // self.possible[x as usize] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
|
|
- }
|
|
|
|
|
|
+ self.board = [0; MAX_SIZE as usize];
|
|
|
|
+ self.possible = [initial; MAX_SIZE as usize];
|
|
}
|
|
}
|
|
|
|
|
|
/// Load puzzle from a string.
|
|
/// Load puzzle from a string.
|
|
@@ -257,6 +247,7 @@ impl Sudoku {
|
|
}
|
|
}
|
|
result
|
|
result
|
|
}
|
|
}
|
|
|
|
+
|
|
pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
self.board[pos(x, y) as usize] = value;
|
|
self.board[pos(x, y) as usize] = value;
|
|
// Ok, update the possible
|
|
// Ok, update the possible
|
|
@@ -284,30 +275,6 @@ impl Sudoku {
|
|
self.possible[pos(x, y) as usize].clear();
|
|
self.possible[pos(x, y) as usize].clear();
|
|
}
|
|
}
|
|
|
|
|
|
- pub fn set2(&mut self, x: u8, y: u8, value: u8) {
|
|
|
|
- self.board[pos(x, y) as usize] = value;
|
|
|
|
- // Ok, update the possible
|
|
|
|
- let mut g = Group::new();
|
|
|
|
- g.for_row(x, y);
|
|
|
|
- for g in g.0 {
|
|
|
|
- // remove value from these sets.
|
|
|
|
- self.possible[g as usize].set(value, false);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- g.for_column(x, y);
|
|
|
|
- for g in g.0 {
|
|
|
|
- // remove value from these sets.
|
|
|
|
- self.possible[g as usize].set(value, false);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- g.for_block(x, y);
|
|
|
|
- for g in g.0 {
|
|
|
|
- // remove value from these sets.
|
|
|
|
- self.possible[g as usize].set(value, false);
|
|
|
|
- }
|
|
|
|
- self.possible[pos(x, y) as usize].clear();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
pub fn display(&self) {
|
|
pub fn display(&self) {
|
|
println!("╔═══╦═══╦═══╗");
|
|
println!("╔═══╦═══╦═══╗");
|
|
for y in 0..WIDTH {
|
|
for y in 0..WIDTH {
|
|
@@ -364,6 +331,104 @@ impl Sudoku {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ pub fn puzzle_complete(&self) -> bool {
|
|
|
|
+ for i in 0..MAX_SIZE {
|
|
|
|
+ if self.board[i as usize] == 0 {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ true
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn make(&mut self) {
|
|
|
|
+ let mut rng = ChaCha20Rng::from_entropy();
|
|
|
|
+
|
|
|
|
+ self.fill_board(&mut rng);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn fill_board(&mut self, rng : &mut ChaCha20Rng ) -> bool {
|
|
|
|
+ let backup = Sudoku{ board: self.board,
|
|
|
|
+ possible: self.possible
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ for idx in 0..MAX_SIZE {
|
|
|
|
+ if self.board[idx as usize] == 0 {
|
|
|
|
+ let (x,y) = xy(idx);
|
|
|
|
+ let mut available: [u8; WIDTH as usize] = [0; WIDTH as usize];
|
|
|
|
+ let mut total_available: u8 = 0;
|
|
|
|
+
|
|
|
|
+ for t in self.possible[idx as usize].iter() {
|
|
|
|
+ available[total_available as usize] = t;
|
|
|
|
+ total_available += 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if total_available == 0 {
|
|
|
|
+ // No possible moves remain.
|
|
|
|
+ /*
|
|
|
|
+ self.board = backup.board;
|
|
|
|
+ self.possible = backup.possible;
|
|
|
|
+ */
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Randomize the possible items.
|
|
|
|
+ available[0..total_available as usize].shuffle(rng);
|
|
|
|
+ for v_idx in 0..total_available {
|
|
|
|
+ let value = available[v_idx as usize];
|
|
|
|
+ self.set(x,y, value);
|
|
|
|
+ if self.fill_board(rng) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ // failure
|
|
|
|
+ self.board = backup.board;
|
|
|
|
+ self.possible = backup.possible;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // We've run out of possible.
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // We've visited everything, and it isn't 0.
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn make_(&mut self) {
|
|
|
|
+ self.clear();
|
|
|
|
+
|
|
|
|
+ let mut rng = ChaCha20Rng::from_entropy();
|
|
|
|
+
|
|
|
|
+ let pick_one = |this: &Self, rng: &mut ChaCha20Rng, idx: u8| -> Option<u8> {
|
|
|
|
+ let mut available: [u8; WIDTH as usize] = [0; WIDTH as usize];
|
|
|
|
+ let mut total_available: u8 = 0;
|
|
|
|
+
|
|
|
|
+ for t in this.possible[idx as usize].iter() {
|
|
|
|
+ available[total_available as usize] = t;
|
|
|
|
+ total_available += 1;
|
|
|
|
+ }
|
|
|
|
+ if total_available == 1 {
|
|
|
|
+ return Some(available[0]);
|
|
|
|
+ }
|
|
|
|
+ if total_available == 0 {
|
|
|
|
+ return None;
|
|
|
|
+ }
|
|
|
|
+ Some(available[rng.gen_range(0..total_available as usize)])
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ for i in 0..MAX_SIZE {
|
|
|
|
+ let (x, y) = xy(i);
|
|
|
|
+ if self.board[i as usize] == 0 {
|
|
|
|
+ // Ok, we found a blank space.
|
|
|
|
+ let value = pick_one(self, &mut rng, i);
|
|
|
|
+ if value.is_some() {
|
|
|
|
+ let value = value.unwrap();
|
|
|
|
+ println!("Set({},{})={}", x, y, value);
|
|
|
|
+ self.set(x, y, value);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
pub fn solve(&mut self) -> bool {
|
|
pub fn solve(&mut self) -> bool {
|
|
// Pass 1: Look for singles in the possible sets.
|
|
// Pass 1: Look for singles in the possible sets.
|
|
let mut found_something = false;
|
|
let mut found_something = false;
|