Browse Source

Moved bits into own file. Fixed tests.

Tests can be run from main as:
    cargo test -p sudoku

Runs the sudoku package tests.
Steve Thielemann 11 months ago
parent
commit
767d21675a
4 changed files with 197 additions and 125 deletions
  1. 124 0
      sudoku/src/bits.rs
  2. 3 3
      sudoku/src/group.rs
  3. 1 0
      sudoku/src/lib.rs
  4. 69 122
      sudoku/src/sudoku.rs

+ 124 - 0
sudoku/src/bits.rs

@@ -0,0 +1,124 @@
+// Use bitfields instead of HashSets.
+use bit_field::BitField;
+
+// If I wanted to do 4x4 or 5x5, I would need more bits. (u32).
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct Bits(pub u16);
+
+/// Set bits number of bits to 1 (true)
+pub const fn set_bits(bits: u8) -> u16 {
+    (1 << (bits)) - 1
+}
+
+impl Bits {
+    /// clear all bits
+    pub fn clear(&mut self) {
+        self.0 = 0;
+    }
+
+    /// set bit to state of value.
+    pub fn set(&mut self, bit: u8, value: bool) {
+        self.0.set_bit(bit as usize, value);
+    }
+
+    /// get state of bit.
+    pub fn get(&self, bit: u8) -> bool {
+        self.0.get_bit(bit as usize)
+    }
+
+    /// set bits on, given number of bits initially to set.
+    pub fn set_bits(&mut self, bits: u8) {
+        self.0 = set_bits(bits);
+    }
+
+    /// count number of bits set.
+    pub fn count_set(&self) -> u8 {
+        let mut count = 0;
+        for i in 0..u16::BIT_LENGTH {
+            if self.get(i as u8) {
+                count += 1;
+            }
+        }
+        count
+    }
+}
+
+pub struct BitsIterator<'a> {
+    pub possible: &'a Bits,
+    pub index: u8,
+}
+
+impl Bits {
+    pub fn iter(&self) -> BitsIterator {
+        BitsIterator {
+            possible: self,
+            index: 1,
+        }
+    }
+}
+
+impl<'a> Iterator for BitsIterator<'a> {
+    type Item = u8;
+
+    fn next(&mut self) -> Option<u8> {
+        while (self.index < u16::BIT_LENGTH as u8) && (!self.possible.get(self.index)) {
+            self.index += 1;
+            // println!("index = {}", self.index);
+        }
+        if self.index == u16::BIT_LENGTH as u8 {
+            None
+        } else {
+            self.index += 1;
+            Some(self.index - 1)
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    // use crate::sudoku::*;
+    use crate::bits::*;
+
+    #[test]
+    fn check_possible_bitset() {
+        let mut p = Bits(0);
+
+        p.clear();
+
+        for i in 0..9 {
+            let mut result = p.get(i);
+            assert_eq!(result, false);
+            p.set(i, true);
+            result = p.get(i);
+            assert_eq!(result, true);
+        }
+    }
+
+    #[test]
+    fn check_possible_iter() {
+        let mut p = Bits(0);
+        p.set(3, true);
+        p.set(5, true);
+        p.set(6, true);
+        assert_eq!(3, p.count_set());
+        let values: Vec<u8> = p.iter().collect();
+        assert_eq!(values, vec!(3, 5, 6));
+        assert_eq!(3, p.count_set());
+        p.set(0, true);
+        assert_eq!(4, p.count_set());
+    }
+
+    #[test]
+    fn check_bits() {
+        // Set bits 0-5 (6 bits total)
+        let p = Bits(set_bits(6));
+
+        for i in 0..6 {
+            let result = p.get(i);
+            assert_eq!(result, true);
+        }
+        assert_eq!(p.get(6), false);
+    }
+}
+

+ 3 - 3
sudoku/src/group.rs

@@ -381,7 +381,7 @@ mod tests {
         let mut g = Group::new();
         let mut g = Group::new();
 
 
         for i in 0..9 {
         for i in 0..9 {
-            println!("Index={}", i);
+            // println!("Index={}", i);
             g.for_column(i, 1);
             g.for_column(i, 1);
             let new_g = for_column(i);
             let new_g = for_column(i);
             assert_eq!(g, *new_g);
             assert_eq!(g, *new_g);
@@ -393,7 +393,7 @@ mod tests {
         let mut g = Group::new();
         let mut g = Group::new();
 
 
         for i in 0..9 {
         for i in 0..9 {
-            println!("Index={}", i);
+            // println!("Index={}", i);
             g.for_row(1, i);
             g.for_row(1, i);
             let new_g = for_row(i);
             let new_g = for_row(i);
             assert_eq!(g, *new_g);
             assert_eq!(g, *new_g);
@@ -404,7 +404,7 @@ mod tests {
     fn check_cells() {
     fn check_cells() {
         let mut g = Group::new();
         let mut g = Group::new();
         for i in 0..9 {
         for i in 0..9 {
-            println!("Index={}", i);
+            // println!("Index={}", i);
             g.for_block((i % 3) * 3, (i / 3) * 3);
             g.for_block((i % 3) * 3, (i / 3) * 3);
             let new_g = for_cell(i);
             let new_g = for_cell(i);
             assert_eq!(g, *new_g);
             assert_eq!(g, *new_g);

+ 1 - 0
sudoku/src/lib.rs

@@ -1,6 +1,7 @@
 pub mod group;
 pub mod group;
 pub mod ksudoku;
 pub mod ksudoku;
 pub mod sudoku;
 pub mod sudoku;
+pub mod bits;
 
 
 // use crate::group::*;
 // use crate::group::*;
 use crate::sudoku::*;
 use crate::sudoku::*;

+ 69 - 122
sudoku/src/sudoku.rs

@@ -1,5 +1,6 @@
 // pub mod group;
 // pub mod group;
 use crate::group::*;
 use crate::group::*;
+use crate::bits::*;
 
 
 // use std::collections::HashSet;
 // use std::collections::HashSet;
 use std::string::String;
 use std::string::String;
@@ -10,9 +11,6 @@ const WIDTH: u8 = SIZE*SIZE;
 /// Size (width * height) of the board.
 /// Size (width * height) of the board.
 const MAX_SIZE: u8 = WIDTH * WIDTH; // 81;
 const MAX_SIZE: u8 = WIDTH * WIDTH; // 81;
 
 
-// Use bitfields instead of HashSets.
-use bit_field::BitField;
-
 extern crate rand_chacha;
 extern crate rand_chacha;
 // use rand::prelude::*;
 // use rand::prelude::*;
 use rand::seq::SliceRandom;
 use rand::seq::SliceRandom;
@@ -20,135 +18,83 @@ use rand_chacha::rand_core::SeedableRng;
 use rand_chacha::ChaCha20Rng;
 use rand_chacha::ChaCha20Rng;
 use rand::distributions::{Distribution, Uniform};
 use rand::distributions::{Distribution, Uniform};
 
 
-// If I wanted to do 4x4 or 5x5, I would need more bits. (u32).
+// Used bo calculate_possible to return the solutions.
-
+pub type SudokuBoard = [u8; MAX_SIZE as usize];
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct Bits(u16);
-
-/// Set bits number of bits to 1 (true)
-pub const fn set_bits(bits: u8) -> u16 {
-    (1 << (bits)) - 1
-}
 
 
-impl Bits {
+/*
-    /// clear all bits
+pub type Possible = [Bits; MAX_SIZE as usize];
-    pub fn clear(&mut self) {
+pub type SudokuPossible = [Bits; MAX_SIZE as usize];
-        self.0 = 0;
+*/
-    }
 
 
-    /// set bit to state of value.
+#[derive(Debug, Clone, Copy)]
-    pub fn set(&mut self, bit: u8, value: bool) {
+pub struct Board([u8; MAX_SIZE as usize]);
-        self.0.set_bit(bit as usize, value);
+#[derive(Debug, Clone, Copy)]
-    }
+pub struct BoardPossible([Bits; MAX_SIZE as usize]);
 
 
-    /// get state of bit.
+impl Board {
-    pub fn get(&self, bit: u8) -> bool {
+    pub fn new() -> Self {
-        self.0.get_bit(bit as usize)
+        let s = Self {
+            0: [0; MAX_SIZE as usize],
+        };
+        s
     }
     }
 
 
-    /// set bits on, given number of bits initially to set.
+    pub fn clear(&mut self) {
-    pub fn set_bits(&mut self, bits: u8) {
+        self.0 = [0; MAX_SIZE as usize];
-        self.0 = set_bits(bits);
     }
     }
 
 
-    /// count number of bits set.
+    pub fn set(&mut self, x:u8, y:u8, value:u8) {
-    pub fn count_set(&self) -> u8 {
+        self.0[pos(x,y) as usize] = value;
-        let mut count = 0;
-        for i in 0..u16::BIT_LENGTH {
-            if self.get(i as u8) {
-                count += 1;
-            }
-        }
-        count
     }
     }
-}
 
 
-struct BitsIterator<'a> {
+    pub fn get(&mut self, x:u8, y:u8) -> u8 {
-    possible: &'a Bits,
+        self.0[pos(x,y) as usize]
-    index: u8,
-}
-
-impl Bits {
-    fn iter(&self) -> BitsIterator {
-        BitsIterator {
-            possible: self,
-            index: 1,
-        }
     }
     }
-}
 
 
-impl<'a> Iterator for BitsIterator<'a> {
+    /// Load puzzle from a string.
-    type Item = u8;
+    /// Note, we load from (top,left) going down, to (bottom,left) by columns.
+    pub fn load_from_tld(&mut self, start_ch: char, blank: char, s: &str) {
+        self.clear();
+        let mut x: u8 = 0;
+        let mut y: u8 = 0;
 
 
-    fn next(&mut self) -> Option<u8> {
+        for ch in s.chars() {
-        while (self.index < u16::BIT_LENGTH as u8) && (!self.possible.get(self.index)) {
+            if ch != blank {
-            self.index += 1;
+                self.set(x,y, (ch as u8 - start_ch as u8)+1);
-            // println!("index = {}", self.index);
+            }
-        }
+            y += 1;
-        if self.index == u16::BIT_LENGTH as u8 {
+            if y >= WIDTH {
-            None
+                y = 0;
-        } else {
+                x += 1;
-            self.index += 1;
+            }
-            Some(self.index - 1)
         }
         }
     }
     }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::sudoku::*;
 
 
-    #[test]
+    /// Save puzzle to a string.
-    fn check_possible_bitset() {
+    /// Note, we load from (top,left) going down, to (bottom,left) by columns.
-        let mut p = Bits(0);
-
-        p.clear();
 
 
-        for i in 0..9 {
+    pub fn save_to_tld(&mut self, start_ch: char, blank: char) -> String {
-            let mut result = p.get(i);
+        let mut result = String::new();
-            assert_eq!(result, false);
+        result.reserve(MAX_SIZE as usize);
-            p.set(i, true);
+        let start_ch = (start_ch as u8 -1) as char;
-            result = p.get(i);
+        let mut x:u8=0;
-            assert_eq!(result, true);
+        let mut y:u8=0;
+        for _i in 0..MAX_SIZE {
+            if self.0[pos(x,y) as usize] == 0 {
+                result.push(blank);
+            } else {
+                result.push((start_ch as u8 + self.0[pos(x,y) as usize]) as char); 
+            }
+            y += 1;
+            if y >= WIDTH {
+                y = 0; 
+                x += 1;
+            }
         }
         }
+        result
     }
     }
 
 
-    #[test]
-    fn check_possible_iter() {
-        let mut p = Bits(0);
-        p.set(3, true);
-        p.set(5, true);
-        p.set(6, true);
-        assert_eq!(3, p.count_set());
-        let values: Vec<u8> = p.iter().collect();
-        assert_eq!(values, vec!(3, 5, 6));
-        assert_eq!(3, p.count_set());
-        p.set(0, true);
-        assert_eq!(4, p.count_set());
-    }
-
-    #[test]
-    fn check_bits() {
-        // Set bits 0-5 (6 bits total)
-        let p = Bits(set_bits(6));
-
-        for i in 0..6 {
-            let result = p.get(i);
-            assert_eq!(result, true);
-        }
-        assert_eq!(p.get(6), false);
-    }
 }
 }
-
-pub type SudokuBoard = [u8; MAX_SIZE as usize];
-pub type Possible = [Bits; MAX_SIZE as usize];
-pub type SudokuPossible = [Bits; MAX_SIZE as usize];
-
-#[derive(Debug, Clone, Copy)]
-pub struct Board([u8; MAX_SIZE as usize]);
-#[derive(Debug, Clone, Copy)]
-pub struct BoardPossible([Bits; MAX_SIZE as usize]);
-
 /*
 /*
 I probably should keep board and possible separate from one another.
 I probably should keep board and possible separate from one another.
 Possible is only used when solving the puzzles, and only used by the
 Possible is only used when solving the puzzles, and only used by the
@@ -207,18 +153,19 @@ pub struct Sudoku {
 }
 }
 
 
 /// Translate x,y to position in board.
 /// Translate x,y to position in board.
-pub const fn pos(x: u8, y: u8) -> u8 {
+/// This is used as usize, so return that instead of u8.
-    x + (y * WIDTH as u8)
+pub const fn pos(x: u8, y: u8) -> usize {
+    x as usize + (y as usize * WIDTH as usize) 
 }
 }
 
 
 /// Translate x,y (with starting index of 1) to position in board.
 /// Translate x,y (with starting index of 1) to position in board.
-pub const fn pos1(x: u8, y: u8) -> u8 {
+pub const fn pos1(x: u8, y: u8) -> usize {
-    (x - 1) + ((y - 1) * WIDTH as u8)
+    (x as usize - 1) + ((y as usize - 1) * WIDTH as usize)
 }
 }
 
 
 /// Translate post to x,y in board.
 /// Translate post to x,y in board.
-pub const fn xy(pos: u8) -> (u8, u8) {
+pub const fn xy(pos: usize) -> (u8, u8) {
-    ((pos % WIDTH), (pos / WIDTH))
+    ((pos % WIDTH as usize) as u8, (pos / WIDTH as usize) as u8)
 }
 }
 
 
 const DEBUG_OUTPUT: bool = false;
 const DEBUG_OUTPUT: bool = false;
@@ -284,7 +231,7 @@ impl Sudoku {
     /// This loads from (top,left) going right, to (top,right), by rows.
     /// This loads from (top,left) going right, to (top,right), by rows.
     pub fn load_from_tlr(&mut self, start_ch: char, blank: char, s: &str) {
     pub fn load_from_tlr(&mut self, start_ch: char, blank: char, s: &str) {
         self.clear();
         self.clear();
-        let mut i: u8 = 0;
+        let mut i: usize = 0;
 
 
         for ch in s.chars() {
         for ch in s.chars() {
             if ch != blank {
             if ch != blank {
@@ -471,7 +418,7 @@ impl Sudoku {
     /// - As possibilities are tried, it recursively calls itself to see
     /// - As possibilities are tried, it recursively calls itself to see
     ///   if there are any solutions with the given possibility.
     ///   if there are any solutions with the given possibility.
     fn calculate_possible(&mut self, total_solutions: &mut u16, solutions: &mut Vec<SudokuBoard>) -> bool {
     fn calculate_possible(&mut self, total_solutions: &mut u16, solutions: &mut Vec<SudokuBoard>) -> bool {
-        for idx in 0..MAX_SIZE {
+        for idx in 0..MAX_SIZE as usize {
             if self.board[idx as usize] == 0 {
             if self.board[idx as usize] == 0 {
                 // Ok, there's a blank here
                 // Ok, there's a blank here
 
 
@@ -576,7 +523,7 @@ impl Sudoku {
             possible: self.possible,
             possible: self.possible,
         };
         };
 
 
-        for idx in 0..MAX_SIZE {
+        for idx in 0..MAX_SIZE as usize {
             if self.board[idx as usize] == 0 {
             if self.board[idx as usize] == 0 {
                 let (x, y) = xy(idx);
                 let (x, y) = xy(idx);
                 let mut available: [u8; WIDTH as usize] = [0; WIDTH as usize];
                 let mut available: [u8; WIDTH as usize] = [0; WIDTH as usize];
@@ -710,7 +657,7 @@ impl Sudoku {
         // Pass 1: Look for singles in the possible sets.
         // Pass 1: Look for singles in the possible sets.
         let mut found_something = false;
         let mut found_something = false;
 
 
-        for i in 0..MAX_SIZE {
+        for i in 0..MAX_SIZE as usize {
             if self.possible[i as usize].count_set() == 1 {
             if self.possible[i as usize].count_set() == 1 {
                 // Get the value
                 // Get the value
                 let value = self.possible[i as usize].iter().next().unwrap();
                 let value = self.possible[i as usize].iter().next().unwrap();