ソースを参照

Checking in notes and changes.

Move GenBits to Vec<bool>, easier, saner, quicker.
Notes on solving the logic puzzle (possible clearing).
Added #[must_use] on expensive return values.
Steve Thielemann 1 ヶ月 前
コミット
e48c220de2
8 ファイル変更150 行追加11 行削除
  1. 6 0
      .cargo/config.toml
  2. 0 6
      Cargo.toml
  3. 4 0
      restore.sh
  4. 10 0
      save.sh
  5. 14 0
      sudoku/src/bits.rs
  6. 5 0
      sudoku/src/group.rs
  7. 1 0
      sudoku/src/ksudoku.rs
  8. 110 5
      sudoku/src/sudoku.rs

+ 6 - 0
.cargo/config.toml

@@ -0,0 +1,6 @@
+[source.crates-io]
+replace-with = "vendored-sources"
+
+[source.vendored-sources]
+directory = "vendor"
+

+ 0 - 6
Cargo.toml

@@ -17,9 +17,3 @@ sudoku = { path = "sudoku" }
 # name = "benchmark"
 # harness = false
 
-[source.crates-io]
-replace-with = "vendored-sources"
-
-[source.vendored-sources]
-directory = "vendor"
-

+ 4 - 0
restore.sh

@@ -0,0 +1,4 @@
+#!/bin/bash
+
+tar -x --skip-old-files -f $1
+

+ 10 - 0
save.sh

@@ -0,0 +1,10 @@
+#!/usr/bin/bash
+
+# rar a -r -s -qo+ -ag-YYYY-MM-DD-NN -m5 sudoku .git/ src/ sudoku/ vendor/ .gitignore Cargo.* test-out
+# This is great, except rar/unrar not available on rpi OS.
+
+STAMP=`date +"%Y-%m-%d"`
+
+tar --exclude="target" -zcvf sudoku.${STAMP}.tar.gz sudoku/ .git/ src/ vendor/ .cargo/ .gitignore Cargo.* test-out
+# rar a -r -s "-x*/target/" -qo+ -ag-YYYY-MM-DD-NN -m5 sudoku .git/ src/ sudoku/ vendor/ .gitignore Cargo.* test-out
+

+ 14 - 0
sudoku/src/bits.rs

@@ -1,4 +1,16 @@
 // Use bitfields instead of HashSets.
+
+/*
+Or MAYBE, just use Vec<bool> ?!
+While that would use more memory, it would certainly be faster!
+Is saving a few bytes (these days) worth it, really?
+
+The iterator would be interesting...
+
+And we could handle any crazy size as well... :O
+
+ */
+
 use bit_field::BitField;
 
 // If I wanted to do 4x4 or 5x5, I would need more bits. (u32).
@@ -19,6 +31,7 @@ impl<T: Integer + BitField> GenBits<T> {
         self.0.set_bit(bit, value);
     }
 
+    #[must_use]
     pub fn get(&self, bit: usize) -> bool {
         self.0.get_bit(bit)
     }
@@ -29,6 +42,7 @@ impl<T: Integer + BitField> GenBits<T> {
         }
     }
 
+    #[must_use]
     pub fn count_set(&self) -> u8 {
         let mut count: u8 = 0;
         for i in 0..T::BIT_LENGTH {

+ 5 - 0
sudoku/src/group.rs

@@ -107,27 +107,32 @@ impl AnyGroup {
         &self.groups[Groups::Cell as usize][start..start + width]
     }
 
+    #[must_use]
     /// Which cell contains (x,y)?
     pub fn which_cell(&self, x: u8, y: u8) -> u8 {
         (x / self.size) + (y / self.size) * self.size
     }
 
+    #[must_use]
     /// Convert index to x,y offsets.
     pub fn cell_offset(&self, idx:u8) -> (u8,u8) {
         (idx % self.size, idx / self.size)
     }
     
+    #[must_use]
     /// 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)
     }
 
+    #[must_use]
     /// 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)
     }
 
+    #[must_use]
     /// 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 {

+ 1 - 0
sudoku/src/ksudoku.rs

@@ -48,6 +48,7 @@ impl Default for Ksudoku {
     }
 }
 
+#[must_use]
 /// Load ksudoku file, returning Ksudoku structure.
 /// - It is up to the caller to determine if they can handle the types
 ///   and size (order).

+ 110 - 5
sudoku/src/sudoku.rs

@@ -71,8 +71,9 @@ impl AnyBoard {
         }
     }
 
-    /// Calculate index position of (x,y)
     #[inline]
+    #[must_use]
+    /// Calculate index position of (x,y)
     pub fn pos(&self, x: u8, y: u8) -> usize {
         debug_assert!(
             x < self.width && y < self.width,
@@ -84,8 +85,9 @@ impl AnyBoard {
         x as usize + y as usize * self.width as usize
     }
 
-    /// Return (x,y) position for given index.
+    #[must_use]
     #[inline]
+    /// Return (x,y) position for given index.
     pub fn xy(&self, idx: usize) -> (u8, u8) {
         (
             (idx % self.width as usize) as u8,
@@ -115,6 +117,7 @@ impl AnyBoard {
         self.board[index] = value;
     }
 
+    #[must_use]
     /// Get value at position (x,y)
     pub fn get(&self, x: u8, y: u8) -> u8 {
         debug_assert!(
@@ -129,17 +132,20 @@ impl AnyBoard {
         self.board[index]
     }
 
+    #[must_use]
     /// Load from ksudoku format
     /// - uses load_from_tld with specifics
     pub fn load_ksudoku(&mut self, s: &str) -> Result<(), Box<dyn error::Error>> {
         self.load_from_tld('b', '_', s)
     }
 
+    #[must_use]
     /// Save to ksudoku format
     pub fn save_ksudoku(&self) -> String {
         self.save_to_tld('b', '_')
     }
 
+    #[must_use]
     /// Load puzzle from string (top,left) going down.
     pub fn load_from_tld(
         &mut self,
@@ -184,6 +190,7 @@ impl AnyBoard {
         Ok(())
     }
 
+    #[must_use]
     /// Save puzzle to a string (top,left) going down.
     pub fn save_to_tld(&self, start_ch: char, blank: char) -> String {
         let mut result = String::new();
@@ -208,6 +215,7 @@ impl AnyBoard {
         result
     }
 
+    #[must_use]
     /// Load puzzle from string (top,left) going right.
     pub fn load_from_tlr(
         &mut self,
@@ -248,6 +256,7 @@ impl AnyBoard {
         Ok(())
     }
 
+    #[must_use]
     /// Save puzzle to a string (top,left) going right.
     pub fn save_to_tlr(&self, start_ch: char, blank: char) -> String {
         let mut result = String::new();
@@ -328,6 +337,7 @@ impl AnyBoard {
         }
     }
 
+    #[must_use]
     /// Output board as strings.
     /// - Uses 1-9 (for 9x9), and A-? for others.
     /// - Used by tests to confirm the board is what we think it should be.
@@ -368,6 +378,7 @@ impl AnyBoard {
         true
     }
 
+    #[must_use]
     /// Solve by brute force
     /// - Returns up to max # of solutions.
     /// - Returns total solutions found, and vector of boards (up to max).
@@ -493,17 +504,25 @@ impl AnyPossible {
     // NOTE: values range from 1..width. Bits range from 0..width-1
 
     #[inline]
-    pub fn set(&mut self, index: usize, value: usize, state: bool) {
+    /// Set value's state for given index.
+    ///   - value range is from 1..=width.
+    ///   - return true if state changed.
+    pub fn set(&mut self, index: usize, value: usize, state: bool) -> bool {
         debug_assert!(
             index < self.max_index,
             "Index {} >= {}",
             index,
             self.max_index
         );
+        let changed = self.possible[index].get(value-1) != state;
         self.possible[index].set(value - 1, state);
+        changed
     }
 
+    #[must_use]
     #[inline]
+    /// Get state for given index and value.
+    ///   - value range is from 1..=width.
     pub fn get(&self, index: usize, value: usize) -> bool {
         debug_assert!(
             index < self.max_index,
@@ -514,7 +533,9 @@ impl AnyPossible {
         self.possible[index].get(value - 1)
     }
 
+    #[must_use]
     #[inline]
+    /// Return index for given (x,y).
     pub fn pos(&self, x: u8, y: u8) -> usize {
         debug_assert!(
             x < self.width && y < self.width,
@@ -525,8 +546,10 @@ impl AnyPossible {
         );
         x as usize + y as usize * self.width as usize
     }
-    /// Return (x,y) position for given index.
+
+    #[must_use]
     #[inline]
+    /// Return (x,y) position for given index.
     pub fn xy(&self, idx: usize) -> (u8, u8) {
         debug_assert!(idx < self.max_index, "Index {} >= {}", idx, self.max_index);
         (
@@ -535,6 +558,7 @@ impl AnyPossible {
         )
     }
 
+    #[must_use]
     /// Format all possible, finding max length.
     /// - Return vec<string> of each item formatted.
     /// - Return max length.
@@ -553,6 +577,7 @@ impl AnyPossible {
         (pos, max)
     }
 
+    #[must_use]
     pub fn find_pairs(&mut self, cellgroup: &[usize]) -> (Vec<(Vec<u8>, Vec<u8>)>, bool) {
         // result[value] = indexes where it is located?
         // Step 1: find values, and their position(s).
@@ -651,6 +676,34 @@ impl AnyPossible {
         (pairs, possibles_updated)
     }
 
+    #[must_use]
+    /// Find positions where each value is possible.
+    pub fn find_value_pos(&self) -> Vec<GenBits<u32>> {
+        let zero = GenBits::<u32>(0);
+        let mut result = vec![zero; self.size as usize];
+        
+        for index in 0..self.size {
+            if self.possible[index as usize] == zero {
+                continue;
+            }
+
+            // Ok, there's some value there.
+            // Would it be better to use the iterator here?
+            for v in self.possible[index as usize].iter() {
+                result[v as usize].set(index as usize, true);
+            }
+
+            /*
+            for v in 0..self.width {
+                if self.possible[index as usize].get(v as usize +1) {
+                    result[v as usize].set(index as usize, true);
+                }
+            }
+            */
+        }
+        result
+    }
+
     /// Display Possible
     /// - Collect all of the possibles, with max length.
     /// - Display formatted (using max length).
@@ -852,12 +905,16 @@ impl AnySolver {
         let size = self.board.size;
         let mut update: bool = false;
 
-        self.possible
+        let _pairs = self.possible
             .find_pairs(self.group.group(Groups::Cell, cell_index));
 
         // println!("Finalize Cell Pairs: {:?}", self.possible.find_pairs(self.group.group(Groups::Cell, index)));
 
+        // get positions of possible numbers - for every cell ?
+
         /*
+        Great, I put my comments in a deprecated function...
+
         Let me describe what I want to do here:
 
         ╔═══╦═══╦═══╗
@@ -886,6 +943,52 @@ impl AnySolver {
         In cell 3, 2 must be in the last column.
         In cell 6, 2 must be in the first column.  (3,7), (3,8) and (3,9) should not contain 2.
 
+        Top cell, 2 is in the middle (2,_). (it's not in the list of possible.)
+        Middle cell, 2 can only be in the last column. (3,*)
+        Bottom cell, 2 must be in (1,*).
+
+        How should I determine that?
+
+        0.)
+        Build a table of numbers, and their possible positions. (used in the next steps)
+        I probably need some helpers to give me a row/column of a given position... as well.
+        row/column iterators ?
+
+        1.)  
+        In a given cell's column/row, if a number is possible in that cell's column/row, remove it from the other cell's row/column.  
+        (Must be there, so remove it.)
+        (3,7), (3,8), (3,9) remove 2.
+
+        2.)
+        In a given cell's column/row, if a number isn't possible in the same column/row in the other cells 
+          -- remove that number as possible from that cell's other row/columns.
+
+        When updating the possibles - track which cells changed (so we can check/recheck just those).
+        After finalize, clear the changed flag (for that cell).
+
+        When allocating the blocks, possibly use the max value (size) -- this should keep the allocated blocks
+        the same (hopefully keep reusing the same size block of memory over and over)
+        -- or allocate it once and just reuse/init the same block over and over ? (reduce allocation/deallocations)
+        
+        Also the reverse should be possible:
+        ╔═══╦═══╦═══╗
+        ║T  ║   ║   ║
+        ║T  ║   ║   ║
+        ║T  ║   ║   ║
+        ╠═══╬═══╬═══╣
+        ║3tt║   ║   ║
+        ║4tt║   ║   ║
+        ║5tt║   ║   ║
+        ╠═══╬═══╬═══╣
+        ║   ║2  ║   ║
+        ║   ║   ║2  ║
+        ║7tt║   ║   ║
+        ╚═══╩═══╩═══╝
+
+        2's possible in column 2 and 3. (for cells 3 and 6).  Represented by 't'.
+        So, 2 must be in column 1 of cell 0.  Represented by 'T'.
+
+
          */
         let (cell_x, cell_y) = self.group.xy(self.group.group(Groups::Cell, cell_index)[0]);
         let x_range = cell_x..cell_x + self.board.size;
@@ -1071,6 +1174,7 @@ impl AnySolver {
         }
     }
 
+    #[must_use]
     pub fn get(&self, x: u8, y: u8) -> u8 {
         debug_assert!(
             x < self.board.width && y < self.board.width,
@@ -1821,6 +1925,7 @@ mod tests {
         solver.finalize_cell(3);
         solver.possible.display();
     }
+
     #[ignore]
     #[test]
     fn logic_test_pairs() {