|
@@ -110,6 +110,8 @@ impl fmt::Display for GameLoadError {
|
|
|
|
|
|
impl error::Error for GameLoadError {}
|
|
|
|
|
|
+// const DEBUG_OUTPUT: bool = false;
|
|
|
+
|
|
|
// Vec doesn't implement Copy ...
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct AnyBoard {
|
|
@@ -135,31 +137,62 @@ impl AnyBoard {
|
|
|
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);
|
|
|
}
|
|
|
|
|
|
+ pub fn copy(&mut self, copy_from: &Self) {
|
|
|
+ debug_assert!(
|
|
|
+ self.size == copy_from.size,
|
|
|
+ "Can't copy size {} into size {}",
|
|
|
+ copy_from.size,
|
|
|
+ self.size
|
|
|
+ );
|
|
|
+ for i in 0..self.max_index {
|
|
|
+ self.board[i] = copy_from.board[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// Calculate index position of (x,y)
|
|
|
+ #[inline]
|
|
|
pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
|
+ debug_assert!(
|
|
|
+ x < self.width && y < self.width,
|
|
|
+ "Expected ({}, {}) < {}",
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ self.width
|
|
|
+ );
|
|
|
x as usize + y as usize * self.width as usize
|
|
|
}
|
|
|
|
|
|
+ /// Return (x,y) position for given index.
|
|
|
+ #[inline]
|
|
|
+ pub fn xy(&self, idx: usize) -> (u8, u8) {
|
|
|
+ (
|
|
|
+ (idx % self.width as usize) as u8,
|
|
|
+ (idx / self.width as usize) as u8,
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
/// Set a position in the board with a value
|
|
|
pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
- assert!(value <= self.width);
|
|
|
+ debug_assert!(
|
|
|
+ x < self.width && y < self.width,
|
|
|
+ "Expected ({}, {}) < {}",
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ self.width
|
|
|
+ );
|
|
|
+ debug_assert!(
|
|
|
+ value <= self.width,
|
|
|
+ "Expected value for ({},{}) = {} < {}",
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ value,
|
|
|
+ self.width
|
|
|
+ );
|
|
|
let index = self.pos(x, y);
|
|
|
assert!(index <= self.board.capacity());
|
|
|
self.board[index] = value;
|
|
@@ -167,6 +200,13 @@ impl AnyBoard {
|
|
|
|
|
|
/// Get value at position (x,y)
|
|
|
pub fn get(&self, x: u8, y: u8) -> u8 {
|
|
|
+ debug_assert!(
|
|
|
+ x < self.width && y < self.width,
|
|
|
+ "Expected ({}, {}) < {}",
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ self.width
|
|
|
+ );
|
|
|
let index = self.pos(x, y);
|
|
|
assert!(index <= self.board.capacity());
|
|
|
self.board[index]
|
|
@@ -187,7 +227,7 @@ impl AnyBoard {
|
|
|
// self.size * self.size*self.size*self.size {
|
|
|
return Err(Box::new(GameLoadError {
|
|
|
message: format!(
|
|
|
- "String exceeds ({}) expected length {}.",
|
|
|
+ "String ({}) exceeds expected length {}.",
|
|
|
s.len(),
|
|
|
self.width
|
|
|
),
|
|
@@ -372,6 +412,79 @@ impl AnyBoard {
|
|
|
}
|
|
|
true
|
|
|
}
|
|
|
+
|
|
|
+ pub fn brute_force_solver(&self) -> u16 {
|
|
|
+ let mut workset = self.clone();
|
|
|
+ let mut total_solutions: u16 = 0;
|
|
|
+ let mut solutions: Vec<AnyBoard> = Vec::new();
|
|
|
+ let groups = AnyGroup::new(workset.size);
|
|
|
+ solutions.reserve(1);
|
|
|
+ workset.brute_force(&mut total_solutions, &mut solutions, &groups);
|
|
|
+
|
|
|
+ if solutions.len() > 0 {
|
|
|
+ println!("*** A Solution:");
|
|
|
+ solutions[0].display();
|
|
|
+ println!("***");
|
|
|
+ }
|
|
|
+ total_solutions
|
|
|
+ }
|
|
|
+
|
|
|
+ fn brute_force(
|
|
|
+ &mut self,
|
|
|
+ total_solutions: &mut u16,
|
|
|
+ solutions: &mut Vec<AnyBoard>,
|
|
|
+ groups: &AnyGroup,
|
|
|
+ ) -> bool {
|
|
|
+ for idx in 0..self.max_index {
|
|
|
+ if self.board[idx] == 0 {
|
|
|
+ // Blank found
|
|
|
+ let (x, y) = self.xy(idx);
|
|
|
+
|
|
|
+ // println!("Blank ({},{})", x, y);
|
|
|
+
|
|
|
+ 'outer: for value in 1..=self.width {
|
|
|
+ // Check if it fits in the puzzle via group.
|
|
|
+ for clear in groups.row(y) {
|
|
|
+ if self.board[*clear] == value {
|
|
|
+ continue 'outer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for clear in groups.column(x) {
|
|
|
+ if self.board[*clear] == value {
|
|
|
+ continue 'outer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for clear in groups.cell(groups.which_cell(x, y)) {
|
|
|
+ if self.board[*clear] == value {
|
|
|
+ continue 'outer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ok, this is possible move.
|
|
|
+ self.board[idx] = value;
|
|
|
+ // println!("Try ({},{}) = {}", x, y, value);
|
|
|
+ // self.display();
|
|
|
+
|
|
|
+ if self.complete() {
|
|
|
+ if *total_solutions < solutions.capacity() as u16 {
|
|
|
+ solutions.push(self.clone());
|
|
|
+ }
|
|
|
+ *total_solutions += 1;
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ if self.brute_force(total_solutions, solutions, groups) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // We failed to place a value -- return failure.
|
|
|
+ // println!("rewind");
|
|
|
+ self.board[idx] = 0;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Need to use u32, so 5*5=25, 25 bits can be accessed.
|
|
@@ -404,27 +517,103 @@ impl AnyPossible {
|
|
|
// let width = self.size * self.size;
|
|
|
initial.set_bits(0..self.width);
|
|
|
|
|
|
- self.possible = vec![initial; self.max_index];
|
|
|
+ self.possible.fill(initial);
|
|
|
+
|
|
|
+ // self.possible = vec![initial; self.max_index];
|
|
|
// width as usize * width as usize];
|
|
|
}
|
|
|
|
|
|
+ pub fn copy(&mut self, copy_from: &Self) {
|
|
|
+ debug_assert!(
|
|
|
+ self.size == copy_from.size,
|
|
|
+ "Can't copy size {} into size {}",
|
|
|
+ copy_from.size,
|
|
|
+ self.size
|
|
|
+ );
|
|
|
+ for i in 0..self.max_index {
|
|
|
+ self.possible[i] = copy_from.possible[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// NOTE: values range from 1..width. Bits range from 0..width-1
|
|
|
|
|
|
#[inline]
|
|
|
pub fn set(&mut self, index: usize, value: usize, state: bool) {
|
|
|
+ debug_assert!(
|
|
|
+ index < self.max_index,
|
|
|
+ "Index {} >= {}",
|
|
|
+ index,
|
|
|
+ self.max_index
|
|
|
+ );
|
|
|
self.possible[index].set(value - 1, state);
|
|
|
}
|
|
|
|
|
|
#[inline]
|
|
|
pub fn get(&self, index: usize, value: usize) -> bool {
|
|
|
+ debug_assert!(
|
|
|
+ index < self.max_index,
|
|
|
+ "Index {} >= {}",
|
|
|
+ index,
|
|
|
+ self.max_index
|
|
|
+ );
|
|
|
self.possible[index].get(value - 1)
|
|
|
}
|
|
|
|
|
|
+ #[inline]
|
|
|
pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
|
+ debug_assert!(
|
|
|
+ x < self.width && y < self.width,
|
|
|
+ "Expected ({},{}) < {}",
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ self.width
|
|
|
+ );
|
|
|
x as usize + y as usize * self.width as usize
|
|
|
}
|
|
|
+ /// Return (x,y) position for given index.
|
|
|
+ #[inline]
|
|
|
+ pub fn xy(&self, idx: usize) -> (u8, u8) {
|
|
|
+ debug_assert!(idx < self.max_index, "Index {} >= {}", idx, self.max_index);
|
|
|
+ (
|
|
|
+ (idx % self.width as usize) as u8,
|
|
|
+ (idx / self.width as usize) as u8,
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn display(&self) {
|
|
|
+ for y in 0..self.width {
|
|
|
+ for x in 0..self.width {
|
|
|
+ let idx = self.pos(x, y);
|
|
|
+ let possible = self.possible[idx];
|
|
|
+ let values: Vec<u8> = possible.iter().collect();
|
|
|
+ // let stuff: String = values.into_iter().map(|i| i.to_string().push_str(",")).collect::<String>();
|
|
|
+ let stuff: String = values
|
|
|
+ .into_iter()
|
|
|
+ .map(|i| (i+1).to_string())
|
|
|
+ .collect::<String>();
|
|
|
+ /*
|
|
|
+ if stuff.len() > 1 {
|
|
|
+ stuff.pop();
|
|
|
+ }*/
|
|
|
+ print!("({},{}):{:9} ", x + 1, y + 1, stuff);
|
|
|
+ }
|
|
|
+ println!("");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+An idea I have for AnySolver is to store the AnyPossible as
|
|
|
+sections. row/column/cell.
|
|
|
+I think this would better allow me to judge how hard the puzzle
|
|
|
+is. If it can be solved by looking at just one section --
|
|
|
+that would be one level. If it takes 2, another level. All 3,
|
|
|
+yet another. I think that's how I mentally judge the puzzles.
|
|
|
+
|
|
|
+"Not all hard puzzles are hard."
|
|
|
+
|
|
|
+ */
|
|
|
+
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct AnySolver {
|
|
|
pub board: AnyBoard,
|
|
@@ -433,7 +622,17 @@ pub struct AnySolver {
|
|
|
}
|
|
|
|
|
|
impl AnySolver {
|
|
|
- pub fn new(initial_board: &AnyBoard) -> Self {
|
|
|
+ pub fn new(board_size: u8) -> Self {
|
|
|
+ let mut s = Self {
|
|
|
+ board: AnyBoard::new(board_size),
|
|
|
+ possible: AnyPossible::new(board_size),
|
|
|
+ group: AnyGroup::new(board_size),
|
|
|
+ };
|
|
|
+ s.reset_possible();
|
|
|
+ s
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn new_from(initial_board: &AnyBoard) -> Self {
|
|
|
let mut s = AnySolver {
|
|
|
board: initial_board.clone(),
|
|
|
possible: AnyPossible::new(initial_board.size),
|
|
@@ -443,10 +642,31 @@ impl AnySolver {
|
|
|
s
|
|
|
}
|
|
|
|
|
|
+ pub fn clear(&mut self) {
|
|
|
+ let board_size:u8 = self.board.size;
|
|
|
+ self.board = AnyBoard::new(board_size);
|
|
|
+ self.possible = AnyPossible::new(board_size);
|
|
|
+ self.reset_possible();
|
|
|
+ }
|
|
|
+
|
|
|
/// 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) {
|
|
|
+ debug_assert!(
|
|
|
+ x <= self.board.width && y <= self.board.width,
|
|
|
+ "Expected ({}, {}) <= {}",
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ self.board.width
|
|
|
+ );
|
|
|
+ debug_assert!(
|
|
|
+ value <= self.board.width,
|
|
|
+ "Expected value {} < {}",
|
|
|
+ value,
|
|
|
+ self.board.width
|
|
|
+ );
|
|
|
+
|
|
|
let mut g = self.group.row(y);
|
|
|
let value: usize = value as usize;
|
|
|
for g in g {
|
|
@@ -460,7 +680,8 @@ impl AnySolver {
|
|
|
for g in g {
|
|
|
self.possible.set(*g, value, false);
|
|
|
}
|
|
|
- self.possible.possible[self.board.pos(x, y)] = GenBits::<u32>(0); // .clear();
|
|
|
+ let idx = self.possible.pos(x,y);
|
|
|
+ self.possible.possible[idx] = GenBits::<u32>(0); // .clear();
|
|
|
}
|
|
|
|
|
|
/// Validate the board
|
|
@@ -532,8 +753,161 @@ impl AnySolver {
|
|
|
/// - This updates all the possibles (row,column,cell).
|
|
|
/// - Clears the possible for (x,y) [See process_move].
|
|
|
pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
+ debug_assert!(
|
|
|
+ x < self.board.width && y < self.board.width,
|
|
|
+ "Expected ({}, {}) < {}",
|
|
|
+ x,
|
|
|
+ y,
|
|
|
+ self.board.width
|
|
|
+ );
|
|
|
+ debug_assert!(
|
|
|
+ value < self.board.width + 1,
|
|
|
+ "Expected value {} < {}",
|
|
|
+ value,
|
|
|
+ self.board.width + 1
|
|
|
+ );
|
|
|
+
|
|
|
self.board.set(x, y, value);
|
|
|
- self.process_move(x, y, value);
|
|
|
+ if value != 0 {
|
|
|
+ self.process_move(x, y, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn make(&mut self) {
|
|
|
+ let mut rng = ChaCha20Rng::from_entropy();
|
|
|
+ self.reset_possible();
|
|
|
+ self.fill_board(&mut rng);
|
|
|
+ }
|
|
|
+
|
|
|
+ fn fill_board(&mut self, rng: &mut ChaCha20Rng) -> bool {
|
|
|
+ let backup = self.clone();
|
|
|
+ let max_index = self.board.max_index;
|
|
|
+ let width = self.board.width;
|
|
|
+
|
|
|
+ for idx in 0..max_index {
|
|
|
+ if self.board.board[idx] == 0 {
|
|
|
+ let (x,y) = self.board.xy(idx);
|
|
|
+ let mut available :Vec<u8> = Vec::new();
|
|
|
+ available.reserve(width as usize);
|
|
|
+
|
|
|
+ for t in self.possible.possible[idx].iter() {
|
|
|
+ available.push(t+1);
|
|
|
+ }
|
|
|
+ if available.len() == 0 {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // print!("{:?}", available);
|
|
|
+ // Randomize the possible items.
|
|
|
+ available.shuffle(rng);
|
|
|
+ // available.as_mut_slice().shuffle(rng);
|
|
|
+
|
|
|
+ // print!("{:?}", available);
|
|
|
+ for value in available.into_iter() {
|
|
|
+ assert!(value != 0);
|
|
|
+
|
|
|
+ self.set(x,y, value);
|
|
|
+ // self.board.display();
|
|
|
+ // self.possible.display();
|
|
|
+
|
|
|
+ // I can't validate board, it might be invalid!
|
|
|
+ // Really!
|
|
|
+ /*
|
|
|
+ if ! self.validate_board() {
|
|
|
+ panic!("Whaaaat?!");
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+ if self.fill_board(rng) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ // Failure
|
|
|
+ self.board.copy(&backup.board);
|
|
|
+ self.possible.copy(&backup.possible);
|
|
|
+ }
|
|
|
+
|
|
|
+ // We've run out of possible.
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // We've visited everything, and it isn't 0.
|
|
|
+ true
|
|
|
+ }
|
|
|
+
|
|
|
+ // This seems .. broken. Not sure. 4x4 kicked me.
|
|
|
+ pub fn brute_force_solver(&mut self) -> u16 {
|
|
|
+ let mut workset = self.clone();
|
|
|
+ workset.reset_possible();
|
|
|
+
|
|
|
+ let mut total_solutions: u16 = 0;
|
|
|
+ let mut solutions: Vec<AnyBoard> = Vec::new();
|
|
|
+ solutions.reserve(1);
|
|
|
+
|
|
|
+ workset.brute_force(&mut total_solutions, &mut solutions);
|
|
|
+
|
|
|
+ if solutions.len() > 0 {
|
|
|
+ println!("*** A solution:");
|
|
|
+ solutions[0].display();
|
|
|
+ println!("***");
|
|
|
+ }
|
|
|
+ total_solutions
|
|
|
+ }
|
|
|
+
|
|
|
+ fn brute_force(&mut self, total_solutions: &mut u16, solutions: &mut Vec<AnyBoard>) -> bool {
|
|
|
+ // Verify that possible is setup correctly.
|
|
|
+ // self.validate_board(); // The better choice here.
|
|
|
+ // self.reset_possible();
|
|
|
+
|
|
|
+ for idx in 0..self.board.max_index {
|
|
|
+ if self.board.board[idx] == 0 {
|
|
|
+ // Blank found
|
|
|
+ let (x, y) = self.board.xy(idx);
|
|
|
+ if DEBUG_OUTPUT {
|
|
|
+ println!("idx={} ({},{})", idx, x + 1, y + 1);
|
|
|
+ self.board.display();
|
|
|
+ self.possible.display();
|
|
|
+ }
|
|
|
+
|
|
|
+ // save possible
|
|
|
+ let old_possible = self.possible.clone();
|
|
|
+
|
|
|
+ for value in 1..=self.board.width {
|
|
|
+ if self.possible.get(idx, value as usize) {
|
|
|
+ // Ok, it could go here.
|
|
|
+ if DEBUG_OUTPUT {
|
|
|
+ println!("({},{})={}", x + 1, y + 1, value);
|
|
|
+ }
|
|
|
+ self.set(x, y, value);
|
|
|
+ self.board.display();
|
|
|
+
|
|
|
+ if self.board.complete() {
|
|
|
+ if *total_solutions < solutions.capacity() as u16 {
|
|
|
+ solutions.push(self.board.clone());
|
|
|
+ }
|
|
|
+ *total_solutions += 1;
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ if self.brute_force(total_solutions, solutions) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Restore
|
|
|
+ self.possible.copy(&old_possible);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if DEBUG_OUTPUT {
|
|
|
+ println!("Rewind!");
|
|
|
+ }
|
|
|
+ self.set(x, y, 0);
|
|
|
+ // self.possible.copy(&old_possible);
|
|
|
+ if DEBUG_OUTPUT {
|
|
|
+ self.board.display();
|
|
|
+ self.possible.display();
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ false
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -544,12 +918,15 @@ mod tests {
|
|
|
|
|
|
#[test]
|
|
|
fn display_board() {
|
|
|
- let mut board: AnyBoard = AnyBoard::new(5);
|
|
|
- let result = 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_");
|
|
|
+ let mut board = AnyBoard::new(3);
|
|
|
+ let result = board.load_from_tld(
|
|
|
+ 'b',
|
|
|
+ '_',
|
|
|
+ "__c_____e_h__cb___bd___ei_ch_jb__d___________i_eh__b__dg___ij_f_i__jg_____b_____g",
|
|
|
+ );
|
|
|
assert!(result.is_ok());
|
|
|
board.display();
|
|
|
-
|
|
|
- let mut solver = AnySolver::new(&board);
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
assert!(solver.validate_board());
|
|
|
|
|
|
board = AnyBoard::new(4);
|
|
@@ -557,10 +934,21 @@ mod tests {
|
|
|
assert!(result.is_ok());
|
|
|
board.display();
|
|
|
|
|
|
- let mut solver = AnySolver::new(&board);
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
+ assert!(solver.validate_board());
|
|
|
+
|
|
|
+ let mut board: AnyBoard = AnyBoard::new(5);
|
|
|
+ let result = 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_");
|
|
|
+ assert!(result.is_ok());
|
|
|
+ board.display();
|
|
|
+
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
assert!(solver.validate_board());
|
|
|
+ }
|
|
|
|
|
|
- board = AnyBoard::new(3);
|
|
|
+ #[test]
|
|
|
+ fn solve_board() {
|
|
|
+ let mut board = AnyBoard::new(3);
|
|
|
let result = board.load_from_tld(
|
|
|
'b',
|
|
|
'_',
|
|
@@ -568,8 +956,42 @@ mod tests {
|
|
|
);
|
|
|
assert!(result.is_ok());
|
|
|
board.display();
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
+ assert!(solver.validate_board());
|
|
|
+ let solutions = solver.board.brute_force_solver();
|
|
|
+ assert!(solutions == 1, "Expected 1 solution, got {}", solutions);
|
|
|
+
|
|
|
+ // 4x4 board takes 40 minutes
|
|
|
+
|
|
|
+ if false {
|
|
|
+ board = AnyBoard::new(4);
|
|
|
+ let result = 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_");
|
|
|
+ assert!(result.is_ok());
|
|
|
+ board.display();
|
|
|
+
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
+ assert!(solver.validate_board());
|
|
|
+ let solutions = solver.board.brute_force_solver();
|
|
|
+ assert!(solutions == 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ let mut board: AnyBoard = AnyBoard::new(5);
|
|
|
+ let result = 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_");
|
|
|
+ assert!(result.is_ok());
|
|
|
+ board.display();
|
|
|
+
|
|
|
let mut solver = AnySolver::new(&board);
|
|
|
assert!(solver.validate_board());
|
|
|
+ assert!(solver.brute_force_solver() == 1);
|
|
|
+ */
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn make_board() {
|
|
|
+ let mut solver = AnySolver::new(4);
|
|
|
+ solver.make();
|
|
|
+ solver.board.display();
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -588,7 +1010,7 @@ mod tests {
|
|
|
board.set(2, 8, 9);
|
|
|
// board.display();
|
|
|
|
|
|
- let mut solver = AnySolver::new(&board);
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
assert!(!solver.validate_board());
|
|
|
|
|
|
// Invalid board: Has two 1's in same column & cell.
|
|
@@ -597,7 +1019,7 @@ mod tests {
|
|
|
board.set(4, 1, 1);
|
|
|
// board.display();
|
|
|
|
|
|
- let mut solver = AnySolver::new(&board);
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
assert!(!solver.validate_board());
|
|
|
|
|
|
// Invalid board: Has two 1's in same row & cell.
|
|
@@ -612,7 +1034,7 @@ mod tests {
|
|
|
board.set(4, 4, 1);
|
|
|
// board.display();
|
|
|
|
|
|
- let mut solver = AnySolver::new(&board);
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
assert!(!solver.validate_board());
|
|
|
|
|
|
// Invalid board: Has two 1's in same row.
|
|
@@ -620,7 +1042,7 @@ mod tests {
|
|
|
board.set(4, 0, 1);
|
|
|
board.set(7, 0, 1);
|
|
|
// board.display();
|
|
|
- let mut solver = AnySolver::new(&board);
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
assert!(!solver.validate_board());
|
|
|
|
|
|
// Invalid board: Has two 1's in same cell.
|
|
@@ -629,7 +1051,7 @@ mod tests {
|
|
|
board.set(5, 1, 1);
|
|
|
// board.display();
|
|
|
|
|
|
- let mut solver = AnySolver::new(&board);
|
|
|
+ let mut solver = AnySolver::new_from(&board);
|
|
|
assert!(!solver.validate_board());
|
|
|
}
|
|
|
}
|
|
@@ -670,6 +1092,7 @@ pub struct SudokuPossible {
|
|
|
|
|
|
*/
|
|
|
|
|
|
+/*
|
|
|
#[derive(Debug)]
|
|
|
pub struct BoardPossible<'a> {
|
|
|
board: &'a mut Board,
|
|
@@ -755,6 +1178,7 @@ impl<'a> BoardPossible<'a> {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+*/
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
pub struct Sudoku {
|