|
@@ -1,22 +1,26 @@
|
|
// pub mod group;
|
|
// pub mod group;
|
|
-use crate::group::*;
|
|
|
|
use crate::bits::*;
|
|
use crate::bits::*;
|
|
|
|
+use crate::group::*;
|
|
|
|
|
|
// use std::collections::HashSet;
|
|
// use std::collections::HashSet;
|
|
use std::string::String;
|
|
use std::string::String;
|
|
|
|
|
|
const SIZE: u8 = 3;
|
|
const SIZE: u8 = 3;
|
|
/// Width of the sudoku board.
|
|
/// Width of the sudoku board.
|
|
-const WIDTH: u8 = SIZE*SIZE;
|
|
|
|
|
|
+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;
|
|
|
|
|
|
extern crate rand_chacha;
|
|
extern crate rand_chacha;
|
|
// use rand::prelude::*;
|
|
// use rand::prelude::*;
|
|
|
|
+use rand::distributions::{Distribution, Uniform};
|
|
use rand::seq::SliceRandom;
|
|
use rand::seq::SliceRandom;
|
|
use rand_chacha::rand_core::SeedableRng;
|
|
use rand_chacha::rand_core::SeedableRng;
|
|
use rand_chacha::ChaCha20Rng;
|
|
use rand_chacha::ChaCha20Rng;
|
|
-use rand::distributions::{Distribution, Uniform};
|
|
|
|
|
|
+
|
|
|
|
+// For custom error
|
|
|
|
+use std::error;
|
|
|
|
+use std::fmt;
|
|
|
|
|
|
// Used bo calculate_possible to return the solutions.
|
|
// Used bo calculate_possible to return the solutions.
|
|
pub type SudokuBoard = [u8; MAX_SIZE as usize];
|
|
pub type SudokuBoard = [u8; MAX_SIZE as usize];
|
|
@@ -29,9 +33,6 @@ pub type SudokuPossible = [Bits; MAX_SIZE as usize];
|
|
#[derive(Debug, Clone, Copy)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Board([u8; MAX_SIZE as usize]);
|
|
pub struct Board([u8; MAX_SIZE as usize]);
|
|
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
impl Board {
|
|
impl Board {
|
|
pub fn new() -> Self {
|
|
pub fn new() -> Self {
|
|
let s = Self {
|
|
let s = Self {
|
|
@@ -44,12 +45,12 @@ impl Board {
|
|
self.0 = [0; MAX_SIZE as usize];
|
|
self.0 = [0; MAX_SIZE as usize];
|
|
}
|
|
}
|
|
|
|
|
|
- pub fn set(&mut self, x:u8, y:u8, value:u8) {
|
|
|
|
- self.0[pos(x,y) as usize] = value;
|
|
|
|
|
|
+ pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
|
+ self.0[pos(x, y) as usize] = value;
|
|
}
|
|
}
|
|
|
|
|
|
- pub fn get(&mut self, x:u8, y:u8) -> u8 {
|
|
|
|
- self.0[pos(x,y) as usize]
|
|
|
|
|
|
+ pub fn get(&mut self, x: u8, y: u8) -> u8 {
|
|
|
|
+ self.0[pos(x, y) as usize]
|
|
}
|
|
}
|
|
|
|
|
|
/// Load puzzle from a string.
|
|
/// Load puzzle from a string.
|
|
@@ -61,7 +62,7 @@ impl Board {
|
|
|
|
|
|
for ch in s.chars() {
|
|
for ch in s.chars() {
|
|
if ch != blank {
|
|
if ch != blank {
|
|
- self.set(x,y, (ch as u8 - start_ch as u8)+1);
|
|
|
|
|
|
+ self.set(x, y, (ch as u8 - start_ch as u8) + 1);
|
|
}
|
|
}
|
|
y += 1;
|
|
y += 1;
|
|
if y >= WIDTH {
|
|
if y >= WIDTH {
|
|
@@ -77,31 +78,43 @@ impl Board {
|
|
pub fn save_to_tld(&mut self, start_ch: char, blank: char) -> String {
|
|
pub fn save_to_tld(&mut self, start_ch: char, blank: char) -> String {
|
|
let mut result = String::new();
|
|
let mut result = String::new();
|
|
result.reserve(MAX_SIZE as usize);
|
|
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;
|
|
|
|
|
|
+ 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 {
|
|
for _i in 0..MAX_SIZE {
|
|
- if self.0[pos(x,y) as usize] == 0 {
|
|
|
|
|
|
+ if self.0[pos(x, y) as usize] == 0 {
|
|
result.push(blank);
|
|
result.push(blank);
|
|
} else {
|
|
} else {
|
|
- result.push((start_ch as u8 + self.0[pos(x,y) as usize]) as char);
|
|
|
|
|
|
+ result.push((start_ch as u8 + self.0[pos(x, y) as usize]) as char);
|
|
}
|
|
}
|
|
y += 1;
|
|
y += 1;
|
|
if y >= WIDTH {
|
|
if y >= WIDTH {
|
|
- y = 0;
|
|
|
|
|
|
+ y = 0;
|
|
x += 1;
|
|
x += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
result
|
|
result
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
|
|
+#[derive(Debug, Clone)]
|
|
|
|
+struct GameLoadError {
|
|
|
|
+ message: String,
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+impl fmt::Display for GameLoadError {
|
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
+ write!(f, "Game load error: {}", self.message)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl error::Error for GameLoadError {}
|
|
|
|
+
|
|
// Vec doesn't implement Copy ...
|
|
// Vec doesn't implement Copy ...
|
|
#[derive(Debug, Clone)]
|
|
#[derive(Debug, Clone)]
|
|
pub struct AnyBoard {
|
|
pub struct AnyBoard {
|
|
- pub size : u8,
|
|
|
|
- pub board : Vec<u8>,
|
|
|
|
|
|
+ pub size: u8,
|
|
|
|
+ pub board: Vec<u8>,
|
|
}
|
|
}
|
|
|
|
|
|
impl AnyBoard {
|
|
impl AnyBoard {
|
|
@@ -110,13 +123,286 @@ impl AnyBoard {
|
|
panic!("Board size must be 3-5.");
|
|
panic!("Board size must be 3-5.");
|
|
}
|
|
}
|
|
|
|
|
|
- let s = AnyBoard{
|
|
|
|
|
|
+ let n = board_size as usize;
|
|
|
|
+ let s = AnyBoard {
|
|
size: board_size,
|
|
size: board_size,
|
|
- board: vec![0, board_size*board_size*2],
|
|
|
|
|
|
+ board: vec![0; n * n * n * n],
|
|
};
|
|
};
|
|
s
|
|
s
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /// Max board width (size*size)
|
|
|
|
+ pub fn width(&self) -> u8 {
|
|
|
|
+ self.size * self.size
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Max board index (width*width)
|
|
|
|
+ pub fn max_index(&self) -> usize {
|
|
|
|
+ self.width() as usize * self.width() as usize
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Clear out the board
|
|
|
|
+ pub fn clear(&mut self) {
|
|
|
|
+ self.board.fill(0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Calculate index position of (x,y)
|
|
|
|
+ pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
|
|
+ x as usize + y as usize * self.width() as usize
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Set a position in the board with a value
|
|
|
|
+ pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
|
+ assert!(value <= self.size * self.size);
|
|
|
|
+ let index = self.pos(x, y);
|
|
|
|
+ assert!(index <= self.board.capacity());
|
|
|
|
+ self.board[index] = value;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Get value at position (x,y)
|
|
|
|
+ pub fn get(&self, x: u8, y: u8) -> u8 {
|
|
|
|
+ let index = self.pos(x, y);
|
|
|
|
+ assert!(index <= self.board.capacity());
|
|
|
|
+ self.board[index]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Load puzzle from string (top,left) going down.
|
|
|
|
+ pub fn load_from_tld(
|
|
|
|
+ &mut self,
|
|
|
|
+ start_ch: char,
|
|
|
|
+ blank: char,
|
|
|
|
+ s: &str,
|
|
|
|
+ ) -> Result<(), Box<dyn error::Error>> {
|
|
|
|
+ self.clear();
|
|
|
|
+ let mut x: u8 = 0;
|
|
|
|
+ let mut y: u8 = 0;
|
|
|
|
+
|
|
|
|
+ if s.len() != self.max_index() {
|
|
|
|
+ // self.size * self.size*self.size*self.size {
|
|
|
|
+ return Err(Box::new(GameLoadError {
|
|
|
|
+ message: format!(
|
|
|
|
+ "String exceeds ({}) expected length {}.",
|
|
|
|
+ s.len(),
|
|
|
|
+ self.size * self.size
|
|
|
|
+ ),
|
|
|
|
+ }));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for ch in s.chars() {
|
|
|
|
+ if ch != blank {
|
|
|
|
+ let value = (ch as u8 - start_ch as u8) + 1;
|
|
|
|
+ if value == 0 || value > self.width() {
|
|
|
|
+ return Err(Box::new(GameLoadError {
|
|
|
|
+ message: format!(
|
|
|
|
+ "String symbol ({}) represents value {}, expecting 1 to {}.",
|
|
|
|
+ ch,
|
|
|
|
+ value,
|
|
|
|
+ self.size * self.size
|
|
|
|
+ ),
|
|
|
|
+ }));
|
|
|
|
+ }
|
|
|
|
+ self.set(x, y, value);
|
|
|
|
+ }
|
|
|
|
+ y += 1;
|
|
|
|
+ if y >= self.size * self.size {
|
|
|
|
+ y = 0;
|
|
|
|
+ x += 1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ Ok(())
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Save puzzle to a string (top,left) going down.
|
|
|
|
+ pub fn save_to_tld(&self, start_ch: char, blank: char) -> String {
|
|
|
|
+ let mut result = String::new();
|
|
|
|
+ result.reserve(self.max_index());
|
|
|
|
+ let start_ch = (start_ch as u8 - 1) as char;
|
|
|
|
+ let mut x: u8 = 0;
|
|
|
|
+ let mut y: u8 = 0;
|
|
|
|
+
|
|
|
|
+ for _i in 0..self.max_index() {
|
|
|
|
+ let value = self.get(x, y);
|
|
|
|
+ if value == 0 {
|
|
|
|
+ result.push(blank);
|
|
|
|
+ } else {
|
|
|
|
+ result.push((start_ch as u8 + value) as char);
|
|
|
|
+ }
|
|
|
|
+ y += 1;
|
|
|
|
+ if y >= self.width() {
|
|
|
|
+ y = 0;
|
|
|
|
+ x += 1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ result
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn display(&self) {
|
|
|
|
+ let line = "═".repeat(self.size as usize);
|
|
|
|
+ let alpha_display = self.width() >= 10;
|
|
|
|
+ // println!("╔{}╦{}╦{}╗", line, line, line);
|
|
|
|
+
|
|
|
|
+ println!("alpha = {}", alpha_display);
|
|
|
|
+
|
|
|
|
+ // Top
|
|
|
|
+ for i in 0..self.size {
|
|
|
|
+ if i == 0 {
|
|
|
|
+ print!("╔{}", line);
|
|
|
|
+ } else {
|
|
|
|
+ print!("╦{}", line);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ println!("╗");
|
|
|
|
+
|
|
|
|
+ for y in 0..self.width() {
|
|
|
|
+ print!("║");
|
|
|
|
+ for x in 0..self.width() {
|
|
|
|
+ let value = self.get(x, y);
|
|
|
|
+ if value == 0 {
|
|
|
|
+ print!(" ");
|
|
|
|
+ } else {
|
|
|
|
+ if alpha_display {
|
|
|
|
+ print!("{}", (value - 1 + 'A' as u8) as char);
|
|
|
|
+ } else {
|
|
|
|
+ print!("{}", value);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if x % self.size == self.size - 1 {
|
|
|
|
+ print!("║");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ println!("");
|
|
|
|
+ if y % self.size == self.size - 1 {
|
|
|
|
+ if y + 1 == self.width() {
|
|
|
|
+ // Bottom
|
|
|
|
+ for i in 0..self.size {
|
|
|
|
+ if i == 0 {
|
|
|
|
+ print!("╚{}", line);
|
|
|
|
+ } else {
|
|
|
|
+ print!("╩{}", line);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ println!("╝");
|
|
|
|
+
|
|
|
|
+ // println("╚═══╩═══╩═══╝");
|
|
|
|
+ } else {
|
|
|
|
+ // Middle
|
|
|
|
+ for i in 0..self.size {
|
|
|
|
+ if i == 0 {
|
|
|
|
+ print!("╠{}", line);
|
|
|
|
+ } else {
|
|
|
|
+ print!("╬{}", line);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ println!("╣");
|
|
|
|
+ // println("╠═══╬═══╬═══╣");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Need to use u32, so 5*5=25, 25 bits can be accessed.
|
|
|
|
+// u16 is fine for 3*3=9.
|
|
|
|
+
|
|
|
|
+#[derive(Debug, Clone)]
|
|
|
|
+pub struct AnyPossible {
|
|
|
|
+ pub size: u8,
|
|
|
|
+ pub possible: Vec<GenBits<u32>>,
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+impl AnyPossible {
|
|
|
|
+ pub fn new(board_size: u8) -> Self {
|
|
|
|
+ let mut initial = GenBits::<u32>(0);
|
|
|
|
+ initial.set_bits(1..board_size);
|
|
|
|
+ Self {
|
|
|
|
+ size: board_size,
|
|
|
|
+ possible: vec![initial; board_size as usize * board_size as usize],
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn clear(&mut self) {
|
|
|
|
+ let mut initial = GenBits::<u32>(0);
|
|
|
|
+ initial.set_bits(1..self.size);
|
|
|
|
+ self.possible = vec![initial; self.size as usize * self.size as usize];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn set(&mut self, index: usize, value: usize, state: bool) {
|
|
|
|
+ self.possible[index].set(value, state);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#[derive(Debug, Clone)]
|
|
|
|
+pub struct AnySolver {
|
|
|
|
+ pub board: AnyBoard,
|
|
|
|
+ pub possible: AnyPossible,
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl AnySolver {
|
|
|
|
+ pub fn new(initial_board: &AnyBoard) -> Self {
|
|
|
|
+ let mut s = AnySolver {
|
|
|
|
+ board: initial_board.clone(),
|
|
|
|
+ possible: AnyPossible::new(initial_board.max_index() as u8),
|
|
|
|
+ };
|
|
|
|
+ s.reset_possible();
|
|
|
|
+ s
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Process a move
|
|
|
|
+ /// - Remove value from rows, columns, and cells.
|
|
|
|
+ /// - Clear possibles from (x,y) position, it's filled.
|
|
|
|
+ pub fn process_move(&mut self, x: u8, y: u8, value: u8) {
|
|
|
|
+ let mut g = for_row(y);
|
|
|
|
+ let value: usize = value as usize;
|
|
|
|
+ for g in g.0 {
|
|
|
|
+ self.possible.set(g, value, false);
|
|
|
|
+ }
|
|
|
|
+ g = for_column(x);
|
|
|
|
+ for g in g.0 {
|
|
|
|
+ self.possible.set(g, value, false);
|
|
|
|
+ }
|
|
|
|
+ g = for_cell(which_cell(x, y));
|
|
|
|
+ for g in g.0 {
|
|
|
|
+ self.possible.set(g, value, false);
|
|
|
|
+ }
|
|
|
|
+ self.possible.possible[self.board.pos(x, y)] = GenBits::<u32>(0); // .clear();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Reset the possible
|
|
|
|
+ /// - For when a new puzzle has been loaded.
|
|
|
|
+ /// - When something has changed, and the possibles are out of sync.
|
|
|
|
+ pub fn reset_possible(&mut self) {
|
|
|
|
+ self.possible.clear();
|
|
|
|
+ for y in 0..self.board.width() {
|
|
|
|
+ for x in 0..self.board.width() {
|
|
|
|
+ let value = self.board.get(x, y);
|
|
|
|
+ if value != 0 {
|
|
|
|
+ self.process_move(x, y, value);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#[cfg(test)]
|
|
|
|
+
|
|
|
|
+mod tests {
|
|
|
|
+ use crate::sudoku::*;
|
|
|
|
+
|
|
|
|
+ #[test]
|
|
|
|
+ fn display_board() {
|
|
|
|
+ let mut board: AnyBoard = AnyBoard::new(5);
|
|
|
|
+ println!("{:?}", board);
|
|
|
|
+ board.load_from_tld('b', '_', "_m_kzg____h_s_n____ftd_u_u_dh_____f_b_t_w_____yg_c_rl_o_tdhy__m__uvig_w_sk_eg___p_q_jikouys_r_d___mlq_t_sb_emcwg_dlzyo_kp_i_ng__ir_b_fp_vhz_ce_y_jm__w__m__o_k_xul_qbt_d_s__e____otv_dhegn___mfkpz_blr____s_dv_n_mjx_ckg_w_bo_p___kqyelwjcz_____nxumoisdh_z__fp_vbi_______dkx_eg__r_y_mlwf_u__q_i__o_chdv_j_i_he_r_____________p_zl_k_d_vbjh_y__e_p__s_tguc_q_s__qj_kpn_______ufw_hx__i_hvntirfxw_____lbckympjg___u_kz_m_bfn_yvx_h_ir_o____rgm_otlnx___ipfes_kwc____p__c_v_ugh_krj_m_w__x__x__ci_j_qk_mpo_dr_u_zb__ht_i_qe_wjvcy_bhkzx_ng_u_syv___u_c_hsfrlqo_t_e___pj_cn_h_slzr__j__mqgp_y_vd_m_bs_____t_o_n_h_____ez_f_e_ufd____p_g_z____cqr_x_");
|
|
|
|
+ println!("{:?}", board);
|
|
|
|
+ board.display();
|
|
|
|
+
|
|
|
|
+ board = AnyBoard::new(4);
|
|
|
|
+ board.load_from_tld('b', '_', "_bo_j_m_f__dp__ge_h_pcfdo___q__n___qio______df___f__l___hpnm_i___obig_p_qhl__k_m_dq_cn______o_g_p_____bi_kc__jn______fo____gi______eb____jd______jk__ml_bn_____i_m_b______oq_nj_d_n__jck_m_fgbq___i_medp___n__b___dg______qjk___j__p___fgohl_d_qo__mq__g_d_p_le_");
|
|
|
|
+ board.display();
|
|
|
|
+ assert!(false);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
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
|
|
@@ -135,7 +421,7 @@ funcs on just that, rather then having them in BoardPossible (wrong
|
|
place for Possible's implementation).
|
|
place for Possible's implementation).
|
|
*/
|
|
*/
|
|
|
|
|
|
- /*
|
|
|
|
|
|
+/*
|
|
#[derive(Debug, Clone, Copy)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct SudokuPossible {
|
|
pub struct SudokuPossible {
|
|
pub possible: [Possible; MAX_SIZE as usize];
|
|
pub possible: [Possible; MAX_SIZE as usize];
|
|
@@ -155,12 +441,12 @@ pub struct SudokuPossible {
|
|
|
|
|
|
#[derive(Debug)]
|
|
#[derive(Debug)]
|
|
pub struct BoardPossible<'a> {
|
|
pub struct BoardPossible<'a> {
|
|
- board: &'a mut Board,
|
|
|
|
|
|
+ board: &'a mut Board,
|
|
possible: [Bits; MAX_SIZE as usize],
|
|
possible: [Bits; MAX_SIZE as usize],
|
|
}
|
|
}
|
|
|
|
|
|
-impl <'a>BoardPossible<'a> {
|
|
|
|
- pub fn new(board : &'a mut Board) -> Self {
|
|
|
|
|
|
+impl<'a> BoardPossible<'a> {
|
|
|
|
+ pub fn new(board: &'a mut Board) -> Self {
|
|
// 10 = WIDTH + 1
|
|
// 10 = WIDTH + 1
|
|
// This would need to be changed in order to handle 4x4 or 5x5.
|
|
// This would need to be changed in order to handle 4x4 or 5x5.
|
|
// NOTE: We ignore bit 0, we use bits 1-9 (for 3x3).
|
|
// NOTE: We ignore bit 0, we use bits 1-9 (for 3x3).
|
|
@@ -185,8 +471,8 @@ impl <'a>BoardPossible<'a> {
|
|
/// - This updates the board.
|
|
/// - This updates the board.
|
|
/// - this updates all the possible (row,column,cell).
|
|
/// - this updates all the possible (row,column,cell).
|
|
/// - Clears the possible for the (x,y).
|
|
/// - Clears the possible for the (x,y).
|
|
- pub fn set(&mut self, x:u8, y:u8, value:u8) {
|
|
|
|
- self.board.0[pos(x,y)] = value;
|
|
|
|
|
|
+ pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
|
+ self.board.0[pos(x, y)] = value;
|
|
|
|
|
|
// update the possible
|
|
// update the possible
|
|
let mut g: &Group = for_row(y);
|
|
let mut g: &Group = for_row(y);
|
|
@@ -197,16 +483,14 @@ impl <'a>BoardPossible<'a> {
|
|
for g in g.0 {
|
|
for g in g.0 {
|
|
self.possible[g].set(value, false);
|
|
self.possible[g].set(value, false);
|
|
}
|
|
}
|
|
- g = for_cell(which_cell(x,y));
|
|
|
|
|
|
+ g = for_cell(which_cell(x, y));
|
|
for g in g.0 {
|
|
for g in g.0 {
|
|
self.possible[g].set(value, false);
|
|
self.possible[g].set(value, false);
|
|
}
|
|
}
|
|
- self.possible[pos(x,y)].clear();
|
|
|
|
|
|
+ self.possible[pos(x, y)].clear();
|
|
}
|
|
}
|
|
|
|
|
|
- pub fn reset_possible(&mut self) {
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
|
|
+ pub fn reset_possible(&mut self) {}
|
|
|
|
|
|
/// Display the possibles.
|
|
/// Display the possibles.
|
|
/// This should be in the Possible struct, not here.
|
|
/// This should be in the Possible struct, not here.
|
|
@@ -241,17 +525,16 @@ impl <'a>BoardPossible<'a> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Sudoku {
|
|
pub struct Sudoku {
|
|
- pub board: [u8; MAX_SIZE as usize], // Board
|
|
|
|
|
|
+ pub board: [u8; MAX_SIZE as usize], // Board
|
|
pub possible: [Bits; MAX_SIZE as usize], // BoardPossible
|
|
pub possible: [Bits; MAX_SIZE as usize], // BoardPossible
|
|
}
|
|
}
|
|
|
|
|
|
/// Translate x,y to position in board.
|
|
/// Translate x,y to position in board.
|
|
/// This is used as usize, so return that instead of u8.
|
|
/// This is used as usize, so return that instead of u8.
|
|
pub const fn pos(x: u8, y: u8) -> usize {
|
|
pub const fn pos(x: u8, y: u8) -> usize {
|
|
- x as usize + (y as usize * WIDTH as 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.
|
|
@@ -348,7 +631,7 @@ impl Sudoku {
|
|
if self.board[pos(x, y) as usize] == 0 {
|
|
if self.board[pos(x, y) as usize] == 0 {
|
|
result.push(blank);
|
|
result.push(blank);
|
|
} else {
|
|
} else {
|
|
- result.push((start_ch as u8 + self.board[pos(x,y) as usize]) as char);
|
|
|
|
|
|
+ result.push((start_ch as u8 + self.board[pos(x, y) as usize]) as char);
|
|
}
|
|
}
|
|
y += 1;
|
|
y += 1;
|
|
if y >= WIDTH {
|
|
if y >= WIDTH {
|
|
@@ -373,8 +656,8 @@ impl Sudoku {
|
|
result
|
|
result
|
|
}
|
|
}
|
|
|
|
|
|
- pub fn get(&self, x:u8, y:u8) -> u8 {
|
|
|
|
- self.board[pos(x,y) as usize]
|
|
|
|
|
|
+ pub fn get(&self, x: u8, y: u8) -> u8 {
|
|
|
|
+ self.board[pos(x, y) as usize]
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -422,17 +705,17 @@ impl Sudoku {
|
|
if item != 0 {
|
|
if item != 0 {
|
|
let mut g: &Group = for_row(y);
|
|
let mut g: &Group = for_row(y);
|
|
for g in g.0 {
|
|
for g in g.0 {
|
|
- self.possible[g as usize].set(item, false);
|
|
|
|
|
|
+ self.possible[g as usize].set(item, false);
|
|
}
|
|
}
|
|
g = for_column(x);
|
|
g = for_column(x);
|
|
for g in g.0 {
|
|
for g in g.0 {
|
|
- self.possible[g as usize].set(item, false);
|
|
|
|
|
|
+ self.possible[g as usize].set(item, false);
|
|
}
|
|
}
|
|
- g = for_cell(which_cell(x,y));
|
|
|
|
|
|
+ g = for_cell(which_cell(x, y));
|
|
for g in g.0 {
|
|
for g in g.0 {
|
|
- self.possible[g as usize].set(item, false);
|
|
|
|
|
|
+ self.possible[g as usize].set(item, false);
|
|
}
|
|
}
|
|
- self.possible[pos(x,y) as usize].clear();
|
|
|
|
|
|
+ self.possible[pos(x, y) as usize].clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -513,7 +796,11 @@ impl Sudoku {
|
|
/// Recursive brute-force solver
|
|
/// Recursive brute-force solver
|
|
/// - 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 as usize {
|
|
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
|
|
@@ -611,7 +898,7 @@ impl Sudoku {
|
|
}
|
|
}
|
|
|
|
|
|
/// Fill puzzle with random
|
|
/// Fill puzzle with random
|
|
- /// - This is like the brute-force solver, it calls itself recursively
|
|
|
|
|
|
+ /// - This is like the brute-force solver, it calls itself recursively
|
|
/// and backtraces if a solution can't be found.
|
|
/// and backtraces if a solution can't be found.
|
|
fn fill_board(&mut self, rng: &mut ChaCha20Rng) -> bool {
|
|
fn fill_board(&mut self, rng: &mut ChaCha20Rng) -> bool {
|
|
let backup = Sudoku {
|
|
let backup = Sudoku {
|
|
@@ -676,10 +963,10 @@ impl Sudoku {
|
|
while value == 0 {
|
|
while value == 0 {
|
|
x = puzrange.sample(&mut rng);
|
|
x = puzrange.sample(&mut rng);
|
|
y = puzrange.sample(&mut rng);
|
|
y = puzrange.sample(&mut rng);
|
|
- value = self.get(x,y);
|
|
|
|
|
|
+ value = self.get(x, y);
|
|
}
|
|
}
|
|
|
|
|
|
- self.set(x,y,0);
|
|
|
|
|
|
+ self.set(x, y, 0);
|
|
|
|
|
|
// Clone, and solve by logic.
|
|
// Clone, and solve by logic.
|
|
let mut puzcopy = self.clone();
|
|
let mut puzcopy = self.clone();
|
|
@@ -690,7 +977,7 @@ impl Sudoku {
|
|
puzcopy.display_possible();
|
|
puzcopy.display_possible();
|
|
*/
|
|
*/
|
|
// If solvable, return true.
|
|
// If solvable, return true.
|
|
- while puzcopy.solve(false) {};
|
|
|
|
|
|
+ while puzcopy.solve(false) {}
|
|
|
|
|
|
/*
|
|
/*
|
|
puzcopy.display();
|
|
puzcopy.display();
|
|
@@ -700,9 +987,9 @@ impl Sudoku {
|
|
if puzcopy.puzzle_complete() {
|
|
if puzcopy.puzzle_complete() {
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// If not solvable, restore number, return false.
|
|
// If not solvable, restore number, return false.
|
|
- self.set(x,y,value);
|
|
|
|
|
|
+ self.set(x, y, value);
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|