Переглянути джерело

Working find_pairs, that does something now.

WARNING:  validate_board destroys find_pairs' work.
Steve Thielemann 3 місяців тому
батько
коміт
95cc267844
3 змінених файлів з 279 додано та 44 видалено
  1. 19 1
      sudoku/src/bits.rs
  2. 16 0
      sudoku/src/group.rs
  3. 244 43
      sudoku/src/sudoku.rs

+ 19 - 1
sudoku/src/bits.rs

@@ -5,8 +5,9 @@ use bit_field::BitField;
 
 use num::Integer;
 use std::ops::RangeBounds;
+use std::fmt;
 
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq)]
 pub struct GenBits<T: Integer + BitField>(pub T);
 
 impl<T: Integer + BitField> GenBits<T> {
@@ -38,14 +39,31 @@ impl<T: Integer + BitField> GenBits<T> {
         count
     }
 
+    /// Display bits that are set.
+    /// +1 is added to the display, so it matches the cell values (1..N)
     pub fn display(&self) -> String {
         self.iter()
             .map(|i| (i+1).to_string())
+            // .map(|i| i.to_string())
             .collect::<Vec<_>>().join(",")
     }
 
 }
 
+impl<T: Integer + BitField + ToString> fmt::Debug for GenBits<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let result = self.iter()
+        // .map(|i| (i+1).to_string())
+        .map(|i| i.to_string())
+        .collect::<Vec<_>>().join(",");
+        if result.is_empty() {
+            return write!(f, "{{}}")
+        } else {
+        return write!(f, "<{}>", result)
+        }
+    }
+}
+
 pub struct GenBitsIterator<'a, T: Integer + BitField> {
     pub possible: &'a GenBits<T>,
     pub index: usize,

+ 16 - 0
sudoku/src/group.rs

@@ -112,6 +112,22 @@ impl AnyGroup {
         (x / self.size) + (y / self.size) * self.size
     }
 
+    /// Convert index to x,y offsets.
+    pub fn cell_offset(&self, idx:u8) -> (u8,u8) {
+        (idx % self.size, idx / self.size)
+    }
+    
+    /// Which index for given cell and (x,y)?
+    pub fn which_cell_index(&self, cell_index:u8, x:u8, y:u8) -> usize {
+        let (sx,sy) = self.cell_start(cell_index);
+        self.pos(sx + x, sy+y)
+    }
+
+    /// Where does a given cell index start?
+    pub fn cell_start(&self, cell_index:u8) -> (u8,u8) {
+        ((cell_index % self.size) * self.size, (cell_index/self.size) * self.size)
+    }
+
     /// Return index of cell (x,y)
     /// - This uses the groups to locate the index of the group.
     pub fn cell_index(&self, index: u8, x: u8, y: u8) -> usize {

+ 244 - 43
sudoku/src/sudoku.rs

@@ -7,7 +7,6 @@ use strum::IntoEnumIterator;
 extern crate rand_chacha;
 use rand::distributions::{Distribution, Uniform};
 use rand::seq::SliceRandom;
-
 use rand_chacha::rand_core::SeedableRng;
 use rand_chacha::ChaCha20Rng;
 
@@ -583,6 +582,11 @@ yet another.  I think that's how I mentally judge the puzzles.
 
  */
 
+pub struct Pairs {
+    pub values: [u8; 2],
+    pub pos: [u8; 2],
+}
+
 #[derive(Debug, Clone)]
 pub struct AnySolver {
     pub board: AnyBoard,
@@ -638,9 +642,10 @@ impl AnySolver {
     /// Process a move
     /// - Remove value from rows, columns, and cells.
     /// - Clear possibles from (x,y) position, it's filled.
-    /// - **THIS PANICS** debug_assert!, panics in test validated_board
-    /// TO FIX: This needs to return an error. (someway, somehow)
-    pub fn process_move(&mut self, x: u8, y: u8, value: u8) {
+    /// - Added finalize parameter
+    ///   - Use it when doing a single move.
+    ///   - Don't do it when setting up the board.  Call finalize_move then.
+    pub fn process_move(&mut self, x: u8, y: u8, value: u8, finalize: bool) {
         debug_assert!(
             x <= self.board.width && y <= self.board.width,
             "Expected ({}, {}) <= {}",
@@ -703,8 +708,9 @@ impl AnySolver {
         */
         self.cell_poss[cell_index as usize].set(value as usize - 1, false);
 
-        self.finalize_cell(cell_index);
-
+        if finalize {
+            self.finalize_cell(cell_index);
+        }
         // Should I do the checks here for the logic possible fix?
         // Or, should I make it a different function?
         // Put somewhere else, since it has to re-check the entire board!
@@ -712,16 +718,200 @@ impl AnySolver {
         // OR, just check row and columns?
     }
 
+    /// find_pairs:  Finds the pairs (and triples, and quads, and ...)
+    /// Must be a vector, because size is self.board.width (dynamic)
+    /// Returns vector[values 0..width], of positions values are seen.
+    /// [0] = "1" = vec![list of positions containing a possible 1]
+    pub fn find_pairs(&mut self, cell_index: u8) -> Vec<GenBits<u32>> {
+        // result[value] = indexes where it is located?
+
+        let initial = GenBits::<u32>(0);
+        let mut result: Vec<GenBits<u32>> = vec![initial; self.board.width as usize];
+
+        // Find starting row and column for given cell.
+        
+        // let (col, row) = self.group.cell_start(cell_index);
+        // let size = self.board.size;
+        /* 
+        let size = self.board.size;
+        let col = (cell_index % size) * size;
+        let row = (cell_index / size) * size;
+        */
+
+        // println!("Cell {} Starts at {},{}", cell_index, col, row);
+        let cellgroup = self.group.group(Groups::Cell, cell_index);
+
+        for value in 0..self.board.width {
+            // Locate every position with a 1
+            for pos in 0..self.board.width {
+                let cellindex = cellgroup[pos as usize];
+                /* 
+                let x = col + (pos % size);
+                let y = row + (pos / size);
+                */
+                // println!("Pos {} ({},{}) Value {}", pos, x, y, value);
+
+                if self
+                    .possible
+                    .get(cellindex, value as usize + 1)
+                    // .get(self.possible.pos(x, y), value as usize + 1)
+                {
+                    // println!("Found {} ({},{}) = {}", cell_index, x, y, value);
+                    result[value as usize].set(pos as usize, true);
+                }
+            }
+        }
+
+        // Ok, this is good, but not useful.  ;)
+
+        let zero_bits = GenBits::<u32>(0);
+        let width = self.board.width;
+        let mut possibles_updated : bool = false;
+
+        'outer: for idx in 0..width-1 {
+            // pos_found contains the positions.
+            let pos_found = result[idx as usize]; 
+            if pos_found == zero_bits {
+                continue;
+            }
+            if idx > 0 {
+                // Check previous for matches - already checked...
+                for check in 0..idx {
+                    if pos_found == result[check as usize] {
+                        continue 'outer;
+                    }
+                }
+            }
+
+            let count = pos_found.count_set();
+            if count == 1 {
+                // Single possible item here ... skip this for now.
+                continue;
+            }
+
+            let mut matches = 1;
+            // Pos contains the numbers in the cell.
+            let mut pos = Vec::<u8>::new();
+            pos.push(idx);
+
+            for look in idx+1..width {
+                if result[look as usize] == pos_found {
+                    matches += 1;
+                    pos.push(look);
+                }
+            }
+
+            if matches == count {
+                // Ok! We found a pair (or triple, or quad, or ...)
+                // Build new possible
+                let mut new_possible = zero_bits;
+                for p in &pos {
+                    new_possible.set(*p as usize, true);
+                }
+                for p in pos_found.iter() {
+                    if self.possible.possible[cellgroup[p as usize]] != new_possible {
+                        println!("Update pos {} with {:?}", p, new_possible);
+                        println!("Index is {} => {:?}", cellgroup[p as usize], self.group.xy(cellgroup[p as usize]));
+                        possibles_updated = true;
+                        self.possible.possible[cellgroup[p as usize]] = new_possible;
+                        println!("Updated!");
+                    }
+                }
+            }
+        }
+
+        result
+    }
+
     /// Finalize move on the board.
     /// - This uses the cell index last modified, so we have fewer
     ///   cells to check here. (2 * size) cells.
-    pub fn finalize_cell(&mut self, index:u8) {
-        let size = self.board.size;
+    pub fn finalize_cell(&mut self, index: u8) {
+        let size = self.board.width;
+        let mut update: bool = false;
+
+        /* 
+        let values = self.find_pairs(index);
+        // ok! Scan, and find pairs, triples, etc.
+        println!("Finalize Cell {}: {:?}", index, values);
+
+        'outer: for idx in 0..size-1 {
+            // Positions found
+            let pos_found = values[idx as usize];
+            if pos_found == (GenBits::<u32>(0)) {
+                println!("Idx {}: (empty)", idx);
+                continue;
+            }
+
+            if idx > 0 {
+                // check if previous done
+                for check in 0..idx {
+                    if pos_found == values[check as usize] {
+                        println!("Previously done {}  prev index {} {:?}", idx, check, pos_found);
+                        continue 'outer;
+                    }
+                }
+            }
+        */
+
+            /*
+            if idx == size-2 {
+                break 'outer;
+            }
+            */
+
+            // Check to see:
+            // - How many items are in it.
+            // - Can we find that many matches.
+
+        /*
+            let count = pos_found.count_set();
+
+            if count == 1 {
+                // Or - should we process this single possible value here?
+                println!("Idx {}: has one item.", idx);
+                continue;
+            }
+
+            let mut matches = 1;
+            let mut pos = Vec::<u8>::new();
+            pos.push(idx);
+
+            println!("Starting at {}, we're looking for {:?} [{} times]", idx, pos_found, count);
+
+            for look in idx+1..size {
+                if values[look as usize] == pos_found {
+                    matches += 1;
+                    pos.push(look);
+                    println!("Match found at {} ({} total)", look, matches);
+                } else {
+                    println!("{:?} did not match {:?} at {}", pos_found, values[look as usize], look);
+                }
+            }
+
+            println!("Final {} matches found, looking for {}", matches, count);
+
+            if matches == count {
+                // Pair (or something) found!
+                println!("Pairs: {:?} [value {}]", values, idx);
+                println!("FOUND: Index {} Count {} Value {:?} {:?}", index, count, pos_found, pos);
+                // Ok! In this position, remove any other possible!
+                // What position am I talking about here?
+                // let index_pos = self.possible.which_cell_index(index, )
+            } else {
+                println!("missed: Index {} Count {} Value {:?} {:?}", index, count, pos_found, pos);
+
+            }
+
+        }
+        */
+
+        // println!("Pairs: {:?}", self.find_pairs(index));
 
         // Check columns
         for c in 0..size {
             // Calculate the row and column for the given cell.
-            let _col = (index % size)*size + c;
+            let _col = (index % size) * size + c;
             // println!("Index {} COL {}", index, col);
         }
 
@@ -731,22 +921,19 @@ impl AnySolver {
             let _row = (index / size) * size + r;
             // println!("Index {} ROW {}", index, row);
         }
-
+        println!("finalize_cell ends...");
     }
 
     /// Finalize move(s) on the board.
     /// - This checks the board for logic fixes. (when column is full/has 1+pair/triple)
     pub fn finalize_move(&mut self) {
-        for idx in 0..3 {
-            let mut _ix: u8 = idx * 3;
-            let mut _iy: u8 = idx * 3;
-
-            println!("Index {} x {} y {}", idx, _ix, _iy);
-            // Check cell rows
-            
-            // Check cell columns
+        // Process the cells diagonally.
+        println!("TODO");
+        /* 
+        for idx in 0..self.board.size {
+            self.finalize_cell(idx + idx * self.board.size);
         }
-        assert!(false, "Boing!");
+        */
     }
 
     /// Validate the board
@@ -779,13 +966,14 @@ impl AnySolver {
                         // self.board.display();
                         return false;
                     }
-                    self.process_move(x, y, value);
+                    self.process_move(x, y, value, false);
                 } else {
                     has_blanks = true;
                 }
             }
         }
         // Ok, the pieces given fit correctly with the sudoku constraints.
+        self.finalize_move();
 
         if has_blanks {
             // Verify that the remaining value == 0 positions have possibles.
@@ -826,17 +1014,19 @@ impl AnySolver {
             for x in 0..self.board.width {
                 let value = self.board.get(x, y);
                 if value != 0 {
-                    self.process_move(x, y, value);
+                    self.process_move(x, y, value, false);
                 }
             }
         }
+        self.finalize_move();
     }
 
     /// set (x,y) to value.
     /// - This updates the board.
     /// - 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) {
+    /// TO FIX:  I think set also needs finalize ... to pass to process_move.
+    pub fn set(&mut self, x: u8, y: u8, value: u8, finalize: bool) {
         debug_assert!(
             x < self.board.width && y < self.board.width,
             "Expected ({}, {}) < {}",
@@ -853,7 +1043,7 @@ impl AnySolver {
 
         self.board.set(x, y, value);
         if value != 0 {
-            self.process_move(x, y, value);
+            self.process_move(x, y, value, finalize);
         }
     }
 
@@ -905,7 +1095,7 @@ impl AnySolver {
                 for value in available.into_iter() {
                     assert!(value != 0);
 
-                    self.set(x, y, value);
+                    self.set(x, y, value, true);
                     // self.board.display();
                     // self.possible.display();
 
@@ -958,7 +1148,7 @@ impl AnySolver {
             value = self.get(x, y);
         }
 
-        self.set(x, y, 0);
+        self.set(x, y, 0, true);
 
         // clone, and solve by logic.
         let mut puzcopy = self.clone();
@@ -970,7 +1160,8 @@ impl AnySolver {
         }
 
         // Not solvable, restore and return false.
-        self.set(x, y, value);
+        // Since this failed, don't bother to finalize.
+        self.set(x, y, value, false);
         return false;
     }
 
@@ -997,7 +1188,7 @@ impl AnySolver {
                     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);
+                    self.set(pos.0, pos.1, value, true);
                     found_something = true;
                 }
             }
@@ -1067,7 +1258,7 @@ impl AnySolver {
                         let xy = this.board.xy(pos);
                         // this.possible.display();
                         // this.board.display();
-                        this.set(xy.0, xy.1, v + 1);
+                        this.set(xy.0, xy.1, v + 1, true);
                         // println!("SET {} ({},{}) = {}", pos, xy.0 + 1, xy.1 + 1, v+1);
 
                         found_something = true;
@@ -1531,13 +1722,13 @@ mod tests {
         println!("--- logic_test ---");
     }
 
-    #[ignore]    
+    #[ignore]
     #[test]
     fn logic_test_pairs() {
         let mut board = AnyBoard::new(3);
         board.set(1, 0, 2);
         board.set(2, 1, 3);
-        board.set(2,2, 5);
+        board.set(2, 2, 5);
         board.set(1, 6, 3);
         board.set(0, 4, 4);
         board.set(1, 7, 5);
@@ -1559,23 +1750,33 @@ mod tests {
            ╚═══╩═══╩═══╝
            P = Possible pair (3,5)
 
-           (1,1):1,6,7,8,9         (2,1):                  (3,1):1,3,4,5,6,7,8,9
-           (1,2):1,6,7,8,9         (2,2):1,3,4,5,6,7,8,9   (3,2):1,3,4,5,6,7,8,9
-           (1,3):1,6,7,8,9         (2,3):1,3,4,5,6,7,8,9   (3,3):1,3,4,5,6,7,8,9
-           (1,4):                  (2,4):1,6,7,8,9         (3,4):1,2,6,7,8,9
-           (1,5):                  (2,5):1,6,7,8,9         (3,5):1,2,6,7,8,9
-           (1,6):                  (2,6):1,6,7,8,9         (3,6):1,2,6,7,8,9
-           (1,7):1,2,6,7,8,9       (2,7):1,3,4,5,6,7,8,9   (3,7):1,2,3,4,5,6,7,8,9
-           (1,8):1,2,6,7,8,9       (2,8):1,3,4,5,6,7,8,9   (3,8):1,2,3,4,5,6,7,8,9
-           (1,9):1,2,6,7,8,9       (2,9):1,3,4,5,6,7,8,9   (3,9):1,2,3,4,5,6,7,8,9
-           ^ This shows the logic bug.
-           (3,7), (3,8) and (3,9) should not contain 2!
-
+        (1,1):1,6,7,8,9         (2,1):                  (3,1):1,4,6,7,8,9       (4,1):1,3,4,5,6,7,8,9   (5,1):1,3,4,5,6,7,8,9   (6,1):1,3,4,5,6,7,8,9   (7,1):1,3,4,5,6,7,8,9   (8,1):1,3,4,5,6,7,8,9   (9,1):1,3,4,5,6,7,8,9
+        (1,2):1,6,7,8,9         (2,2):1,4,6,7,8,9       (3,2):                  (4,2):1,2,4,5,6,7,8,9   (5,2):1,2,4,5,6,7,8,9   (6,2):1,2,4,5,6,7,8,9   (7,2):1,2,4,5,6,7,8,9   (8,2):1,2,4,5,6,7,8,9   (9,2):1,2,4,5,6,7,8,9
+        (1,3):1,6,7,8,9         (2,3):1,4,6,7,8,9       (3,3):                  (4,3):1,2,3,4,6,7,8,9   (5,3):1,2,3,4,6,7,8,9   (6,3):1,2,3,4,6,7,8,9   (7,3):1,2,3,4,6,7,8,9   (8,3):1,2,3,4,6,7,8,9   (9,3):1,2,3,4,6,7,8,9
+        (1,4):1,2,3,5,6,7,8,9   (2,4):1,6,7,8,9         (3,4):1,2,6,7,8,9       (4,4):1,2,3,4,5,6,7,8,9 (5,4):1,2,3,4,5,6,7,8,9 (6,4):1,2,3,4,5,6,7,8,9 (7,4):1,2,3,4,5,6,7,8,9 (8,4):1,2,3,4,5,6,7,8,9 (9,4):1,2,3,4,5,6,7,8,9
+        (1,5):                  (2,5):1,6,7,8,9         (3,5):1,2,6,7,8,9       (4,5):1,2,3,5,6,7,8,9   (5,5):1,2,3,5,6,7,8,9   (6,5):1,2,3,5,6,7,8,9   (7,5):1,2,3,5,6,7,8,9   (8,5):1,2,3,5,6,7,8,9   (9,5):1,2,3,5,6,7,8,9
+        (1,6):1,2,3,5,6,7,8,9   (2,6):1,6,7,8,9         (3,6):1,2,6,7,8,9       (4,6):1,2,3,4,5,6,7,8,9 (5,6):1,2,3,4,5,6,7,8,9 (6,6):1,2,3,4,5,6,7,8,9 (7,6):1,2,3,4,5,6,7,8,9 (8,6):1,2,3,4,5,6,7,8,9 (9,6):1,2,3,4,5,6,7,8,9
+        (1,7):1,2,6,7,8,9       (2,7):                  (3,7):1,2,4,6,7,8,9     (4,7):1,2,4,5,6,7,8,9   (5,7):1,2,4,5,6,7,8,9   (6,7):1,2,4,5,6,7,8,9   (7,7):1,2,4,5,6,7,8,9   (8,7):1,2,4,5,6,7,8,9   (9,7):1,2,4,5,6,7,8,9
+        (1,8):1,2,6,7,8,9       (2,8):                  (3,8):1,2,4,6,7,8,9     (4,8):1,2,3,4,6,7,8,9   (5,8):1,2,3,4,6,7,8,9   (6,8):1,2,3,4,6,7,8,9   (7,8):1,2,3,4,6,7,8,9   (8,8):1,2,3,4,6,7,8,9   (9,8):1,2,3,4,6,7,8,9
+        (1,9):1,2,6,7,8,9       (2,9):1,4,6,7,8,9       (3,9):1,2,4,6,7,8,9     (4,9):1,2,3,4,5,6,7,8,9 (5,9):1,2,3,4,5,6,7,8,9 (6,9):1,2,3,4,5,6,7,8,9 (7,9):1,2,3,4,5,6,7,8,9 (8,9):1,2,3,4,5,6,7,8,9 (9,9):1,2,3,4,5,6,7,8,9
+        ^ This shows the logic bug.
+          (1,4) and (1,6) should only be 3,5.
+          (3,7), (3,8), and (3,9) should not contain 2.
         */
 
-        assert!(solver.validate_board());
+        println!("RIGHT HERE:");
+        println!("Pairs [cell index 3]: {:?}", solver.find_pairs(3));
+        solver.finalize_cell(3);
         solver.possible.display();
 
+        /*
+        Pairs: [{1, 4, 8, 6, 7, 0, 5, 2}, {6, 0, 8, 5, 2}, {0, 6}, {}, {0, 6}, {1, 4, 8, 6, 7, 0, 5, 2}, {1, 4, 8, 6, 7, 0, 5, 2}, {1, 4, 8, 6, 7, 0, 5, 2}, {1, 4, 8, 6, 7, 0, 5, 2}]
+        3 and 5 are {0,6} !
+         */
+        // Is validate_board destroying our work?!?
+        // YES IT IS!
+        assert!(solver.validate_board());
+        solver.possible.display();
     }
 
     #[test]