Prechádzať zdrojové kódy

Initial work on AnySolver and solve by logic.

Still to do is "pairs".  I don't know if that is enough to solve
the 4x4 and 5x5 puzzles, I might need to expand that to triples
as well.  I'll see.
Steve Thielemann 3 mesiacov pred
rodič
commit
fa4f28f75e
1 zmenil súbory, kde vykonal 349 pridanie a 25 odobranie
  1. 349 25
      sudoku/src/sudoku.rs

+ 349 - 25
sudoku/src/sudoku.rs

@@ -445,7 +445,7 @@ impl AnyBoard {
         solutions.reserve(max as usize);
         workset.brute_force(&mut total_solutions, &mut solutions, &groups);
 
-        /* 
+        /*
         if solutions.len() > 0 {
             println!("*** A Solution:");
             solutions[0].display();
@@ -694,20 +694,21 @@ impl AnySolver {
         );
 
         let mut g = self.group.row(y);
-        let value: usize = value as usize;
+        let val: usize = value as usize;
         for g in g {
-            self.possible.set(*g, value, false);
+            self.possible.set(*g, val, false);
         }
         g = self.group.column(x);
         for g in g {
-            self.possible.set(*g, value, false);
+            self.possible.set(*g, val, false);
         }
         g = self.group.cell(self.group.which_cell(x, y));
         for g in g {
-            self.possible.set(*g, value, false);
+            self.possible.set(*g, val, false);
         }
         let idx = self.possible.pos(x, y);
         self.possible.possible[idx] = GenBits::<u32>(0); // .clear();
+        self.board.board[idx] = value;
     }
 
     /// Validate the board
@@ -867,23 +868,182 @@ impl AnySolver {
     /// - Call solve until it returns false.
     /// - It might not be solved (if guessing is required).
     pub fn solve_logic(&mut self) -> bool {
+        self.reset_possible();
+        // self.board.display();
+        // self.possible.display();
+
         // Pass 1: Look for singles in the possible sets.
         let mut found_something = false;
+        let mut pass1 = false;
 
-        for i in 0 .. self.possible.max_index {
-            if self.board.board[i] != 0 {
-                // Skip, if position already filled.
-                continue;
+        // Repeat pass 1 until all have been found.
+        let mut pass1_again = true;
+
+        let width = self.group.width;
+        let grp = self.group.clone();
+        let mut pass2 = false;
+
+        while pass1_again {
+            // Pass 1 - look for singles.
+
+            for i in 0..self.possible.max_index {
+                if self.board.board[i] != 0 {
+                    // Skip, if position already filled.
+                    continue;
+                }
+
+                if self.possible.possible[i].count_set() == 1 {
+                    // Get value
+                    let value = self.possible.possible[i].iter().next().unwrap() + 1;
+                    let pos = self.board.xy(i);
+                    // println!("SET {}({},{})={}", i, pos.0 + 1, pos.1 + 1, value);
+                    self.set(pos.0, pos.1, value);
+                    found_something = true;
+                }
             }
-            if self.possible.possible[i].count_set() == 1 {
-                // Get value
-                let value = self.possible.possible[i].iter().next().unwrap();
-                let pos = self.board.xy(i);
-                self.set(pos.0, pos.1, value);
-                found_something = true;
+
+            if found_something {
+                // We found something this pass.
+                // - set pass1 to true (pass 1 found something)
+                // - reset found_something so we can try again.
+                pass1 = true;
+                found_something = false;
+                continue;
+            } else {
+                // Nothing found with this run of pass 1.
+
+                // Pass 2 - Look for singles within the groups.
+                
+                found_something = false;
+
+                let mut values = Bits(0); // HashSet<u8> = HashSet::new();
+
+                let mut group_process = |this: &mut Self, grp: &[usize]| {
+                    // Collect all the possible values within the group.
+                    values.clear();
+                    for gidx in 0..width {
+                        // println!("possible: {:?}", this.possible[grp.items[gidx as usize] as usize]);
+                        for v in this.possible.possible[grp[gidx as usize]].iter() {
+                            values.set(v, true);
+                        }
+                        // values.extend(this.possible[grp.0[gidx as usize] as usize]);
+                        // println!("now     : {:?}", this.possible[grp.items[gidx as usize] as usize]);
+                    }
+        
+                    // println!("values {:?}", values);
+        
+                    // Now, check for singles.
+                    for v in values.iter() {
+                        // println!("Check Value: {}", v);
+        
+                        let mut count = 0;
+                        let mut pos = 0;
+        
+                        for gidx in 0..width {
+                            if this.possible.possible[grp[gidx as usize]].get(v as usize) {
+                                // if this.possible[grp.0[gidx as usize] as usize].contains(&v) {
+                                count += 1;
+                                pos = grp[gidx as usize];
+                                if count > 1 {
+                                    break;
+                                } else {
+                                    // print!(" IDX {} POS {} ", gidx, pos);
+                                }
+                            }
+                        }
+                        if count == 1 {
+                            // don't need this, it was v!
+                            // let value = this.possible[pos as usize].iter().next().cloned().unwrap();
+                            let xy = this.board.xy(pos);
+                            // this.possible.display();
+                            // this.board.display();
+                            this.set(xy.0, xy.1, v+1);
+                            // println!("SET {} ({},{}) = {}", pos, xy.0 + 1, xy.1 + 1, v+1);
+        
+                            found_something = true;
+                        }
+                    }
+                };
+        
+                // Change to 0..WIDTH ...  Keep it simple.
+                for i in 0..width {
+                    // println!("Column {i}:");
+                    let mut g = grp.column(i);
+                    group_process(self, g);
+                    // println!("Row {i}:");
+                    g = grp.row(i);
+                    group_process(self, g);
+                    // println!("Cell {i}:");
+                    g = grp.cell(i);
+                    group_process(self, g);
+                }        
+        
+                if found_something == true {
+                    pass2 = true;
+                    // Ok, pass 2 found something.
+                    found_something = false;
+                    continue;
+                }
+
+                //
+                // - If pass1 set, reset the found_something (because it
+                //   did find something)
+                // - Clear the pass1_again flag, we're done with pass 1.
+                if pass1 {
+                    pass1_again = false;
+                    found_something = true;
+                }
             }
         }
 
+        // After changing Pass1 to loop, I don't think I need to go over the
+        // groups now.  Besides, possible should be covering all the groups
+        // anyway!
+
+        /*
+        println!("Set2:");
+        self.board.display();
+        self.possible.display();
+        */
+
+        // This is a mess here.  It looks obsoleted by the above pass1 loop.
+
+        // No, it isn't.
+
+        /*
+ Set2:
+╔═══╦═══╦═══╗
+║  1║7 8║3  ║
+║ 73║   ║68 ║
+║286║934║751║
+╠═══╬═══╬═══╣
+║   ║1 7║   ║
+║ 2 ║   ║ 9 ║
+║ 14║   ║86 ║
+╠═══╬═══╬═══╣
+║  8║3 1║9  ║
+║   ║   ║   ║
+║432║879║516║
+╚═══╩═══╩═══╝
+(1,1):59        (2,1):459       (3,1):          (4,1):          (5,1):256       (6,1):          (7,1):          (8,1):24        (9,1):249       
+(1,2):59        (2,2):          (3,2):          (4,2):25        (5,2):125       (6,2):25        (7,2):          (8,2):          (9,2):249       
+(1,3):          (2,3):          (3,3):          (4,3):          (5,3):          (6,3):          (7,3):          (8,3):          (9,3):          
+(1,4):35689     (2,4):569       (3,4):59        (4,4):          (5,4):245689    (6,4):          (7,4):24        (8,4):234       (9,4):2345      
+(1,5):35678     (2,5):          (3,5):57        (4,5):456       (5,5):4568      (6,5):356       (7,5):14        (8,5):          (9,5):3457      
+(1,6):3579      (2,6):          (3,6):          (4,6):25        (5,6):259       (6,6):235       (7,6):          (8,6):          (9,6):2357      
+(1,7):567       (2,7):56        (3,7):          (4,7):          (5,7):2456      (6,7):          (7,7):          (8,7):247       (9,7):247       
+(1,8):15679     (2,8):569       (3,8):579       (4,8):2456      (5,8):2456      (6,8):256       (7,8):24        (8,8):2347      (9,8):23478     
+(1,9):          (2,9):          (3,9):          (4,9):          (5,9):          (6,9):          (7,9):          (8,9):          (9,9):          
+
+        See:  
+        (2,1) only has a 4 in cell/column.
+        (5,2) only has a 1 in cell.
+        (7,5) only has a 1 in column.
+        (1,8) only has a 1 (rows/column).
+        (9,8) only has 8.
+         */
+
+        /* 
         let width = self.group.width;
         let grp = self.group.clone();
         let mut values = Bits(0); // HashSet<u8> = HashSet::new();
@@ -900,32 +1060,36 @@ impl AnySolver {
                 // println!("now     : {:?}", this.possible[grp.items[gidx as usize] as usize]);
             }
 
-            // println!("values {:?}", values);
+            println!("values {:?}", values);
 
             // Now, check for singles.
             for v in values.iter() {
+                println!("Check Value: {}", v);
+
                 let mut count = 0;
                 let mut pos = 0;
-                for gidx in 0..width{
-                    if this.possible.possible[grp[gidx as usize] ].get(v as usize) {
+
+                for gidx in 0..width {
+                    if this.possible.possible[grp[gidx as usize]].get(v as usize) {
                         // if this.possible[grp.0[gidx as usize] as usize].contains(&v) {
                         count += 1;
                         pos = grp[gidx as usize];
                         if count > 1 {
                             break;
+                        } else {
+                            print!(" IDX {} POS {} ", gidx, pos);
                         }
                     }
                 }
                 if count == 1 {
                     // don't need this, it was v!
                     // let value = this.possible[pos as usize].iter().next().cloned().unwrap();
-                    /*
-                    if debug {
-                        println!("Set2 {:?} to {}", xy(pos), v);
-                    }
-                    */
                     let xy = this.board.xy(pos);
+                    this.possible.display();
+                    this.board.display();
                     this.set(xy.0, xy.1, v);
+                    println!("SET {} ({},{}) = {}", pos, xy.0 + 1, xy.1 + 1, v);
+
                     found_something = true;
                 }
             }
@@ -933,10 +1097,13 @@ impl AnySolver {
 
         // Change to 0..WIDTH ...  Keep it simple.
         for i in 0..width {
+            println!("Column {i}:");
             let mut g = grp.column(i);
             group_process(self, g);
+            println!("Row {i}:");
             g = grp.row(i);
             group_process(self, g);
+            println!("Cell {i}:");
             g = grp.cell(i);
             group_process(self, g);
         }
@@ -944,9 +1111,148 @@ impl AnySolver {
         if found_something {
             return found_something;
         }
+        */
+        return false;
+        
+
+        let width = self.group.width;
+        let grp = self.group.clone();
 
         // Pair processing.
+        for i in 0..width {
+            let mut g = grp.cell(i);
 
+            for gidx in 0..WIDTH - 1 {
+                let gpos = g[gidx as usize];
+                if self.possible.possible[gpos as usize].count_set() == 2 {
+                    // Found a pair
+                    for fidx in gidx + 1..width {
+                        let fpos = g[fidx as usize];
+                        if self.possible.possible[fpos as usize].count_set() == 2 {
+                            // Ok, there's another pair
+                            // if self.possible[gpos as usize].is_subset(&self.possible[fpos as usize])
+                            if self.possible.possible[gpos] == self.possible.possible[fpos as usize]
+                            {
+                                // Ok, they have the same values!
+                                // Ok, remove the items in the pair from the cell.
+                                // Don't touch the gpos/fpos records.  Keep those!
+                                let mut values: [u8; 2] = [0, 0];
+                                let mut vpos = 0;
+                                for z in self.possible.possible[gpos].iter() {
+                                    values[vpos] = z + 1;
+                                    vpos += 1;
+                                }
+
+                                let mut pair_removed = false;
+
+                                // Check to see if anything was removed.
+
+                                for remove in 0..width {
+                                    if (gidx == remove) || (fidx == remove) {
+                                        continue;
+                                    }
+                                    // Ok, these aren't the ones to save, so:
+                                    let rpos = g[remove as usize];
+                                    if self.possible.possible[rpos].get(values[0] as usize) {
+                                        self.possible.possible[rpos].set(values[0] as usize, false);
+                                        found_something = true;
+                                        pair_removed = true;
+                                    }
+
+                                    if self.possible.possible[rpos].get(values[1] as usize) {
+                                        self.possible.possible[rpos].set(values[1] as usize, false);
+                                        found_something = true;
+                                        pair_removed = true;
+                                    }
+                                }
+
+                                // Check the x's and y's to see if we can also process a row/column too.
+                                if self.possible.xy(gpos).0 == self.possible.xy(fpos).0 {
+                                    // Matching X - process column
+                                    let column = xy(gpos).0;
+
+                                    vpos = 0;
+                                    for z in self.possible.possible[gpos].iter() {
+                                        values[vpos] = z + 1;
+                                        vpos += 1;
+                                    }
+                                    for remove in 0..WIDTH {
+                                        if (remove == xy(gpos).1) || (remove == xy(fpos).1) {
+                                            continue;
+                                        }
+
+                                        let pos = self.possible.pos(column, remove);
+
+                                        if self.possible.possible[pos].get(values[0] as usize) {
+                                            self.possible.possible[pos]
+                                                .set(values[0] as usize, false);
+                                            found_something = true;
+                                            pair_removed = true;
+                                        }
+
+                                        if self.possible.possible[pos].get(values[1] as usize) {
+                                            self.possible.possible[pos]
+                                                .set(values[1] as usize, false);
+                                            found_something = true;
+                                            pair_removed = true;
+                                        }
+                                    }
+                                }
+
+                                if self.possible.xy(gpos).1 == self.possible.xy(fpos).1 {
+                                    // Matching Y - process row
+                                    let row = self.possible.xy(gpos).1;
+
+                                    vpos = 0;
+                                    for z in self.possible.possible[gpos].iter() {
+                                        values[vpos] = z + 1;
+                                        vpos += 1;
+                                    }
+                                    for remove in 0..width {
+                                        if (remove == self.possible.xy(gpos).0)
+                                            || (remove == self.possible.xy(fpos).0)
+                                        {
+                                            continue;
+                                        }
+                                        let pos = self.possible.pos(remove, row);
+
+                                        if self.possible.possible[pos].get(values[0] as usize) {
+                                            self.possible.possible[pos]
+                                                .set(values[0] as usize, false);
+                                            found_something = true;
+                                            pair_removed = true;
+                                        }
+
+                                        if self.possible.possible[pos].get(values[1] as usize) {
+                                            self.possible.possible[pos]
+                                                .set(values[1] as usize, false);
+                                            found_something = true;
+                                            pair_removed = true;
+                                        }
+                                    }
+                                }
+
+                                /*
+                                if pair_removed {
+                                    if debug {
+                                        println!(
+                                            "Pair found! {} {}: {} {:?} and {} {:?} !",
+                                            gidx,
+                                            fidx,
+                                            gpos,
+                                            xy(gpos),
+                                            fpos,
+                                            xy(fpos)
+                                        );
+                                    }
+                                }
+                                */
+                            }
+                        }
+                    }
+                }
+            }
+        }
         found_something
     }
 }
@@ -984,6 +1290,15 @@ mod tests {
         // board.display();
         let mut solver = AnySolver::new_from(&board);
         assert!(solver.validate_board());
+        
+        // solver.solve_logic();
+        
+        while (solver.solve_logic()) {
+            solver.board.display();
+        }
+        
+        assert!(solver.validate_board());
+        assert!(solver.board.complete());
 
         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_");
@@ -1014,6 +1329,15 @@ mod tests {
 
         let mut solver = AnySolver::new_from(&board);
         assert!(solver.validate_board());
+        solver.board.display();
+
+        while (solver.solve_logic()) {
+            solver.board.display();
+        }
+        
+        assert!(solver.validate_board());
+        solver.board.display();
+        assert!(solver.board.complete());
 
         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_");
@@ -1328,8 +1652,8 @@ impl<'a> BoardPossible<'a> {
 */
 
 /*
-  ___  _     _    ____          _      
- / _ \| | __| |  / ___|___   __| | ___ 
+  ___  _     _    ____          _
+ / _ \| | __| |  / ___|___   __| | ___
 | | | | |/ _` | | |   / _ \ / _` |/ _ \
 | |_| | | (_| | | |__| (_) | (_| |  __/
  \___/|_|\__,_|  \____\___/ \__,_|\___|