Jelajahi Sumber

More updates to Any*.

Brute force has speed issue with brute-force. It can take
up to 40 minutes to finish.
Make sometimes works fast (<1 sec), but I've had some that
took 65 seconds [4x4 board].
Steve Thielemann 4 bulan lalu
induk
melakukan
f3fa2cba29
3 mengubah file dengan 574 tambahan dan 51 penghapusan
  1. 1 1
      sudoku/src/bits.rs
  2. 120 21
      sudoku/src/group.rs
  3. 453 29
      sudoku/src/sudoku.rs

+ 1 - 1
sudoku/src/bits.rs

@@ -48,7 +48,7 @@ impl<T: Integer + BitField> GenBits<T> {
     pub fn iter(&self) -> GenBitsIterator<T> {
         GenBitsIterator {
             possible: self,
-            index: 1,
+            index: 0,
         }
     }
 }

+ 120 - 21
sudoku/src/group.rs

@@ -15,14 +15,15 @@ The old code below only handles 3x3 puzzles only!
 pub struct AnyGroup {
     pub size: u8,
     pub width: u8,
+    pub max_index: usize,
     pub row: Vec<usize>,
     pub column: Vec<usize>,
     pub cell: Vec<usize>,
 }
 
 /// Find the number of digits needed to display given number.
-fn find_number_width(mut number:usize) -> usize {
-    let mut size:usize = 1;
+fn find_number_width(mut number: usize) -> usize {
+    let mut size: usize = 1;
     number /= 10;
     while number > 0 {
         size += 1;
@@ -37,6 +38,7 @@ impl AnyGroup {
         let mut g = AnyGroup {
             size: board_size,
             width: board_size * board_size,
+            max_index: width * width,
             row: vec![0; width * width],
             column: vec![0; width * width],
             cell: vec![0; width * width],
@@ -96,10 +98,9 @@ impl AnyGroup {
                 // y is correct:
                 //    x_off + y_off * self.size
                 self.cell[index] = self.pos(
-                     x_mod +  y_mod * self.size,
-                     x_off + y_off * self.size
-                    // x_mod + x_off * self.size,
-                    // y_off
+                    x_mod + y_mod * self.size,
+                    x_off + y_off * self.size, // x_mod + x_off * self.size,
+                                               // y_off
                 );
             }
         }
@@ -112,11 +113,18 @@ impl AnyGroup {
         let max_index_size = find_number_width(self.width as usize * self.width as usize);
         let max_pos_size = find_number_width(self.width as usize);
 
-        let printer = |row:u8, data:&[usize]| {
+        let printer = |row: u8, data: &[usize]| {
             print!("[{:2}: ", row);
             for j in data {
                 let xy = self.xy(*j);
-                print!("{0:1$}({3:2$},{4:2$}) ", j, max_index_size, max_pos_size, xy.0+1, xy.1+1);
+                print!(
+                    "{0:1$}({3:2$},{4:2$}) ",
+                    j,
+                    max_index_size,
+                    max_pos_size,
+                    xy.0 + 1,
+                    xy.1 + 1
+                );
             }
             println!("");
         };
@@ -168,12 +176,12 @@ impl AnyGroup {
 }
 
 /*
-  ___  _     _    ____          _      
- / _ \| | __| |  / ___|___   __| | ___ 
+  ___  _     _    ____          _
+ / _ \| | __| |  / ___|___   __| | ___
 | | | | |/ _` | | |   / _ \ / _` |/ _ \
 | |_| | | (_| | | |__| (_) | (_| |  __/
  \___/|_|\__,_|  \____\___/ \__,_|\___|
-                                       
+
 Static tables for 3x3 boards only...
  */
 
@@ -542,13 +550,7 @@ pub fn which_cell(x: u8, y: u8) -> u8 {
 
 #[cfg(test)]
 mod tests {
-    // use crate::sudoku::*;
     use crate::group::*;
-    // use crate::sudoku::group::*;
-    // use crate::Group;
-    // use group::*;
-
-    // use crate::{for_cell, for_column, for_row};
 
     #[test]
     fn check_columns() {
@@ -587,10 +589,107 @@ mod tests {
 
     #[test]
     fn check_dynamic() {
-        let g = AnyGroup::new(3);
-        g.display();
-        let g = AnyGroup::new(4);
-        g.display();
+        // Verify that each index (0..max_index) is defined in each group.
+        // - Verify that it is used, and only once.
+
+        for size in 3..=5 {
+            let g = AnyGroup::new(size);
+            let mut all = vec![0 as u8; g.max_index];
+
+            // Verify the X,Y values are in the correct cell.
+            all.fill(0);
+
+            for idx in 0..g.width {
+                let grp = g.cell(idx);
+                // g.display();
+                for indexes in grp {
+                    let (x, y) = g.xy(*indexes);
+                    assert!(
+                        all[*indexes] == 0,
+                        "Size {} Index [{}]({},{}) already seen",
+                        size,
+                        *indexes,
+                        x + 1,
+                        y + 1
+                    );
+                    all[*indexes] = 1;
+                    assert_eq!(g.which_cell(x, y), idx, "Verify [{}]({},{}) in cell {}", *indexes, x, y, idx);
+                }
+            }
+
+            for indexes in 0..g.max_index {
+                let (x, y) = g.xy(indexes);
+                assert!(
+                    all[indexes] == 1,
+                    "Size {} Index [{}]({},{}) not seen",
+                    size,
+                    indexes,
+                    x + 1,
+                    y + 1
+                );
+            }
+
+            all.fill(0);
+            for idx in 0..g.width {
+                let grp = g.row(idx);
+
+                for indexes in grp {
+                    let (x, y) = g.xy(*indexes);
+                    assert_eq!(y, idx, "Expected row Y {} == {}", y, idx);
+                    assert!(
+                        all[*indexes] == 0,
+                        "Size {} row Index [{}]({},{}) already seen",
+                        size,
+                        *indexes,
+                        x + 1,
+                        y + 1
+                    );
+                    all[*indexes] = 1;
+                }
+            }
+            for indexes in 0..g.max_index {
+                let (x, y) = g.xy(indexes);
+                assert!(
+                    all[indexes] == 1,
+                    "Size {} row Index [{}]({},{}) not seen",
+                    size,
+                    indexes,
+                    x + 1,
+                    y + 1
+                );
+            }
+
+            all.fill(0);
+            for idx in 0..g.width {
+                let grp = g.column(idx);
+
+                for indexes in grp {
+                    let (x, y) = g.xy(*indexes);
+                    assert_eq!(x, idx, "Expected column X {} == {}", x, idx);
+
+                    assert!(
+                        all[*indexes] == 0,
+                        "Size {} col Index [{}]({},{}) already seen",
+                        size,
+                        *indexes,
+                        x + 1,
+                        y + 1
+                    );
+                    all[*indexes] = 1;
+                }
+            }
+            for indexes in 0..g.max_index {
+                let (x, y) = g.xy(indexes);
+                assert!(
+                    all[indexes] == 1,
+                    "Size {} col Index [{}]({},{}) not seen",
+                    size,
+                    indexes,
+                    x + 1,
+                    y + 1
+                );
+            }
+        }
     }
 }
 

+ 453 - 29
sudoku/src/sudoku.rs

@@ -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 {