|
@@ -1,5 +1,6 @@
|
|
// pub mod group;
|
|
// pub mod group;
|
|
use crate::group::*;
|
|
use crate::group::*;
|
|
|
|
+use crate::bits::*;
|
|
|
|
|
|
// use std::collections::HashSet;
|
|
// use std::collections::HashSet;
|
|
use std::string::String;
|
|
use std::string::String;
|
|
@@ -10,9 +11,6 @@ const WIDTH: u8 = SIZE*SIZE;
|
|
/// Size (width * height) of the board.
|
|
/// Size (width * height) of the board.
|
|
const MAX_SIZE: u8 = WIDTH * WIDTH; // 81;
|
|
const MAX_SIZE: u8 = WIDTH * WIDTH; // 81;
|
|
|
|
|
|
-// Use bitfields instead of HashSets.
|
|
|
|
-use bit_field::BitField;
|
|
|
|
-
|
|
|
|
extern crate rand_chacha;
|
|
extern crate rand_chacha;
|
|
// use rand::prelude::*;
|
|
// use rand::prelude::*;
|
|
use rand::seq::SliceRandom;
|
|
use rand::seq::SliceRandom;
|
|
@@ -20,135 +18,83 @@ use rand_chacha::rand_core::SeedableRng;
|
|
use rand_chacha::ChaCha20Rng;
|
|
use rand_chacha::ChaCha20Rng;
|
|
use rand::distributions::{Distribution, Uniform};
|
|
use rand::distributions::{Distribution, Uniform};
|
|
|
|
|
|
-// If I wanted to do 4x4 or 5x5, I would need more bits. (u32).
|
|
|
|
-
|
|
|
|
-#[derive(Debug, Copy, Clone, PartialEq)]
|
|
|
|
-pub struct Bits(u16);
|
|
|
|
-
|
|
|
|
-/// Set bits number of bits to 1 (true)
|
|
|
|
-pub const fn set_bits(bits: u8) -> u16 {
|
|
|
|
- (1 << (bits)) - 1
|
|
|
|
-}
|
|
|
|
|
|
+// Used bo calculate_possible to return the solutions.
|
|
|
|
+pub type SudokuBoard = [u8; MAX_SIZE as usize];
|
|
|
|
|
|
-impl Bits {
|
|
|
|
- /// clear all bits
|
|
|
|
- pub fn clear(&mut self) {
|
|
|
|
- self.0 = 0;
|
|
|
|
- }
|
|
|
|
|
|
+/*
|
|
|
|
+pub type Possible = [Bits; MAX_SIZE as usize];
|
|
|
|
+pub type SudokuPossible = [Bits; MAX_SIZE as usize];
|
|
|
|
+*/
|
|
|
|
|
|
- /// set bit to state of value.
|
|
|
|
- pub fn set(&mut self, bit: u8, value: bool) {
|
|
|
|
- self.0.set_bit(bit as usize, value);
|
|
|
|
- }
|
|
|
|
|
|
+#[derive(Debug, Clone, Copy)]
|
|
|
|
+pub struct Board([u8; MAX_SIZE as usize]);
|
|
|
|
+#[derive(Debug, Clone, Copy)]
|
|
|
|
+pub struct BoardPossible([Bits; MAX_SIZE as usize]);
|
|
|
|
|
|
- /// get state of bit.
|
|
|
|
- pub fn get(&self, bit: u8) -> bool {
|
|
|
|
- self.0.get_bit(bit as usize)
|
|
|
|
|
|
+impl Board {
|
|
|
|
+ pub fn new() -> Self {
|
|
|
|
+ let s = Self {
|
|
|
|
+ 0: [0; MAX_SIZE as usize],
|
|
|
|
+ };
|
|
|
|
+ s
|
|
}
|
|
}
|
|
|
|
|
|
- /// set bits on, given number of bits initially to set.
|
|
|
|
- pub fn set_bits(&mut self, bits: u8) {
|
|
|
|
- self.0 = set_bits(bits);
|
|
|
|
|
|
+ pub fn clear(&mut self) {
|
|
|
|
+ self.0 = [0; MAX_SIZE as usize];
|
|
}
|
|
}
|
|
|
|
|
|
- /// count number of bits set.
|
|
|
|
- pub fn count_set(&self) -> u8 {
|
|
|
|
- let mut count = 0;
|
|
|
|
- for i in 0..u16::BIT_LENGTH {
|
|
|
|
- if self.get(i as u8) {
|
|
|
|
- count += 1;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- count
|
|
|
|
|
|
+ pub fn set(&mut self, x:u8, y:u8, value:u8) {
|
|
|
|
+ self.0[pos(x,y) as usize] = value;
|
|
}
|
|
}
|
|
-}
|
|
|
|
|
|
|
|
-struct BitsIterator<'a> {
|
|
|
|
- possible: &'a Bits,
|
|
|
|
- index: u8,
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-impl Bits {
|
|
|
|
- fn iter(&self) -> BitsIterator {
|
|
|
|
- BitsIterator {
|
|
|
|
- possible: self,
|
|
|
|
- index: 1,
|
|
|
|
- }
|
|
|
|
|
|
+ pub fn get(&mut self, x:u8, y:u8) -> u8 {
|
|
|
|
+ self.0[pos(x,y) as usize]
|
|
}
|
|
}
|
|
-}
|
|
|
|
|
|
|
|
-impl<'a> Iterator for BitsIterator<'a> {
|
|
|
|
- type Item = u8;
|
|
|
|
|
|
+ /// Load puzzle from a string.
|
|
|
|
+ /// Note, we load from (top,left) going down, to (bottom,left) by columns.
|
|
|
|
+ pub fn load_from_tld(&mut self, start_ch: char, blank: char, s: &str) {
|
|
|
|
+ self.clear();
|
|
|
|
+ let mut x: u8 = 0;
|
|
|
|
+ let mut y: u8 = 0;
|
|
|
|
|
|
- fn next(&mut self) -> Option<u8> {
|
|
|
|
- while (self.index < u16::BIT_LENGTH as u8) && (!self.possible.get(self.index)) {
|
|
|
|
- self.index += 1;
|
|
|
|
- // println!("index = {}", self.index);
|
|
|
|
- }
|
|
|
|
- if self.index == u16::BIT_LENGTH as u8 {
|
|
|
|
- None
|
|
|
|
- } else {
|
|
|
|
- self.index += 1;
|
|
|
|
- Some(self.index - 1)
|
|
|
|
|
|
+ for ch in s.chars() {
|
|
|
|
+ if ch != blank {
|
|
|
|
+ self.set(x,y, (ch as u8 - start_ch as u8)+1);
|
|
|
|
+ }
|
|
|
|
+ y += 1;
|
|
|
|
+ if y >= WIDTH {
|
|
|
|
+ y = 0;
|
|
|
|
+ x += 1;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-}
|
|
|
|
-
|
|
|
|
-#[cfg(test)]
|
|
|
|
-mod tests {
|
|
|
|
- use crate::sudoku::*;
|
|
|
|
|
|
|
|
- #[test]
|
|
|
|
- fn check_possible_bitset() {
|
|
|
|
- let mut p = Bits(0);
|
|
|
|
-
|
|
|
|
- p.clear();
|
|
|
|
|
|
+ /// Save puzzle to a string.
|
|
|
|
+ /// Note, we load from (top,left) going down, to (bottom,left) by columns.
|
|
|
|
|
|
- for i in 0..9 {
|
|
|
|
- let mut result = p.get(i);
|
|
|
|
- assert_eq!(result, false);
|
|
|
|
- p.set(i, true);
|
|
|
|
- result = p.get(i);
|
|
|
|
- assert_eq!(result, true);
|
|
|
|
|
|
+ pub fn save_to_tld(&mut self, start_ch: char, blank: char) -> String {
|
|
|
|
+ let mut result = String::new();
|
|
|
|
+ result.reserve(MAX_SIZE as usize);
|
|
|
|
+ let start_ch = (start_ch as u8 -1) as char;
|
|
|
|
+ let mut x:u8=0;
|
|
|
|
+ let mut y:u8=0;
|
|
|
|
+ for _i in 0..MAX_SIZE {
|
|
|
|
+ if self.0[pos(x,y) as usize] == 0 {
|
|
|
|
+ result.push(blank);
|
|
|
|
+ } else {
|
|
|
|
+ result.push((start_ch as u8 + self.0[pos(x,y) as usize]) as char);
|
|
|
|
+ }
|
|
|
|
+ y += 1;
|
|
|
|
+ if y >= WIDTH {
|
|
|
|
+ y = 0;
|
|
|
|
+ x += 1;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ result
|
|
}
|
|
}
|
|
|
|
|
|
- #[test]
|
|
|
|
- fn check_possible_iter() {
|
|
|
|
- let mut p = Bits(0);
|
|
|
|
- p.set(3, true);
|
|
|
|
- p.set(5, true);
|
|
|
|
- p.set(6, true);
|
|
|
|
- assert_eq!(3, p.count_set());
|
|
|
|
- let values: Vec<u8> = p.iter().collect();
|
|
|
|
- assert_eq!(values, vec!(3, 5, 6));
|
|
|
|
- assert_eq!(3, p.count_set());
|
|
|
|
- p.set(0, true);
|
|
|
|
- assert_eq!(4, p.count_set());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- #[test]
|
|
|
|
- fn check_bits() {
|
|
|
|
- // Set bits 0-5 (6 bits total)
|
|
|
|
- let p = Bits(set_bits(6));
|
|
|
|
-
|
|
|
|
- for i in 0..6 {
|
|
|
|
- let result = p.get(i);
|
|
|
|
- assert_eq!(result, true);
|
|
|
|
- }
|
|
|
|
- assert_eq!(p.get(6), false);
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
-pub type SudokuBoard = [u8; MAX_SIZE as usize];
|
|
|
|
-pub type Possible = [Bits; MAX_SIZE as usize];
|
|
|
|
-pub type SudokuPossible = [Bits; MAX_SIZE as usize];
|
|
|
|
-
|
|
|
|
-#[derive(Debug, Clone, Copy)]
|
|
|
|
-pub struct Board([u8; MAX_SIZE as usize]);
|
|
|
|
-#[derive(Debug, Clone, Copy)]
|
|
|
|
-pub struct BoardPossible([Bits; MAX_SIZE as usize]);
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
I probably should keep board and possible separate from one another.
|
|
I probably should keep board and possible separate from one another.
|
|
Possible is only used when solving the puzzles, and only used by the
|
|
Possible is only used when solving the puzzles, and only used by the
|
|
@@ -207,18 +153,19 @@ pub struct Sudoku {
|
|
}
|
|
}
|
|
|
|
|
|
/// Translate x,y to position in board.
|
|
/// Translate x,y to position in board.
|
|
-pub const fn pos(x: u8, y: u8) -> u8 {
|
|
|
|
- x + (y * WIDTH as u8)
|
|
|
|
|
|
+/// This is used as usize, so return that instead of u8.
|
|
|
|
+pub const fn pos(x: u8, y: u8) -> usize {
|
|
|
|
+ x as usize + (y as usize * WIDTH as usize)
|
|
}
|
|
}
|
|
|
|
|
|
/// Translate x,y (with starting index of 1) to position in board.
|
|
/// Translate x,y (with starting index of 1) to position in board.
|
|
-pub const fn pos1(x: u8, y: u8) -> u8 {
|
|
|
|
- (x - 1) + ((y - 1) * WIDTH as u8)
|
|
|
|
|
|
+pub const fn pos1(x: u8, y: u8) -> usize {
|
|
|
|
+ (x as usize - 1) + ((y as usize - 1) * WIDTH as usize)
|
|
}
|
|
}
|
|
|
|
|
|
/// Translate post to x,y in board.
|
|
/// Translate post to x,y in board.
|
|
-pub const fn xy(pos: u8) -> (u8, u8) {
|
|
|
|
- ((pos % WIDTH), (pos / WIDTH))
|
|
|
|
|
|
+pub const fn xy(pos: usize) -> (u8, u8) {
|
|
|
|
+ ((pos % WIDTH as usize) as u8, (pos / WIDTH as usize) as u8)
|
|
}
|
|
}
|
|
|
|
|
|
const DEBUG_OUTPUT: bool = false;
|
|
const DEBUG_OUTPUT: bool = false;
|
|
@@ -284,7 +231,7 @@ impl Sudoku {
|
|
/// This loads from (top,left) going right, to (top,right), by rows.
|
|
/// This loads from (top,left) going right, to (top,right), by rows.
|
|
pub fn load_from_tlr(&mut self, start_ch: char, blank: char, s: &str) {
|
|
pub fn load_from_tlr(&mut self, start_ch: char, blank: char, s: &str) {
|
|
self.clear();
|
|
self.clear();
|
|
- let mut i: u8 = 0;
|
|
|
|
|
|
+ let mut i: usize = 0;
|
|
|
|
|
|
for ch in s.chars() {
|
|
for ch in s.chars() {
|
|
if ch != blank {
|
|
if ch != blank {
|
|
@@ -471,7 +418,7 @@ impl Sudoku {
|
|
/// - As possibilities are tried, it recursively calls itself to see
|
|
/// - As possibilities are tried, it recursively calls itself to see
|
|
/// if there are any solutions with the given possibility.
|
|
/// if there are any solutions with the given possibility.
|
|
fn calculate_possible(&mut self, total_solutions: &mut u16, solutions: &mut Vec<SudokuBoard>) -> bool {
|
|
fn calculate_possible(&mut self, total_solutions: &mut u16, solutions: &mut Vec<SudokuBoard>) -> bool {
|
|
- for idx in 0..MAX_SIZE {
|
|
|
|
|
|
+ for idx in 0..MAX_SIZE as usize {
|
|
if self.board[idx as usize] == 0 {
|
|
if self.board[idx as usize] == 0 {
|
|
// Ok, there's a blank here
|
|
// Ok, there's a blank here
|
|
|
|
|
|
@@ -576,7 +523,7 @@ impl Sudoku {
|
|
possible: self.possible,
|
|
possible: self.possible,
|
|
};
|
|
};
|
|
|
|
|
|
- for idx in 0..MAX_SIZE {
|
|
|
|
|
|
+ for idx in 0..MAX_SIZE as usize {
|
|
if self.board[idx as usize] == 0 {
|
|
if self.board[idx as usize] == 0 {
|
|
let (x, y) = xy(idx);
|
|
let (x, y) = xy(idx);
|
|
let mut available: [u8; WIDTH as usize] = [0; WIDTH as usize];
|
|
let mut available: [u8; WIDTH as usize] = [0; WIDTH as usize];
|
|
@@ -710,7 +657,7 @@ impl Sudoku {
|
|
// 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;
|
|
|
|
|
|
- for i in 0..MAX_SIZE {
|
|
|
|
|
|
+ for i in 0..MAX_SIZE as usize {
|
|
if self.possible[i as usize].count_set() == 1 {
|
|
if self.possible[i as usize].count_set() == 1 {
|
|
// Get the value
|
|
// Get the value
|
|
let value = self.possible[i as usize].iter().next().unwrap();
|
|
let value = self.possible[i as usize].iter().next().unwrap();
|