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 4 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).
-
-#[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
-}
+// Used bo calculate_possible to return the solutions.
+pub type SudokuBoard = [u8; MAX_SIZE as usize];
 
 
-impl Bits {
-    /// clear all bits
-    pub fn clear(&mut self) {
-        self.0 = 0;
-    }
+/*
+pub type Possible = [Bits; MAX_SIZE as usize];
+pub type SudokuPossible = [Bits; MAX_SIZE as usize];
+*/
 
 
-    /// set bit to state of value.
-    pub fn set(&mut self, bit: u8, value: bool) {
-        self.0.set_bit(bit as usize, value);
-    }
+#[derive(Debug, Clone, Copy)]
+pub struct Board([u8; MAX_SIZE as usize]);
+#[derive(Debug, Clone, Copy)]
+pub struct BoardPossible([Bits; MAX_SIZE as usize]);
 
 
-    /// get state of bit.
-    pub fn get(&self, bit: u8) -> bool {
-        self.0.get_bit(bit as usize)
+impl Board {
+    pub fn new() -> Self {
+        let s = Self {
+            0: [0; MAX_SIZE as usize],
+        };
+        s
     }
     }
 
 
-    /// set bits on, given number of bits initially to set.
-    pub fn set_bits(&mut self, bits: u8) {
-        self.0 = set_bits(bits);
+    pub fn clear(&mut self) {
+        self.0 = [0; MAX_SIZE as usize];
     }
     }
 
 
-    /// 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 fn set(&mut self, x:u8, y:u8, value:u8) {
+        self.0[pos(x,y) as usize] = value;
     }
     }
-}
 
 
-struct BitsIterator<'a> {
-    possible: &'a Bits,
-    index: u8,
-}
-
-impl Bits {
-    fn iter(&self) -> BitsIterator {
-        BitsIterator {
-            possible: self,
-            index: 1,
-        }
+    pub fn get(&mut self, x:u8, y:u8) -> u8 {
+        self.0[pos(x,y) as usize]
     }
     }
-}
 
 
-impl<'a> Iterator for BitsIterator<'a> {
-    type Item = u8;
+    /// Load puzzle from a string.
+    /// 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> {
-        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)
+        for ch in s.chars() {
+            if ch != blank {
+                self.set(x,y, (ch as u8 - start_ch as u8)+1);
+            }
+            y += 1;
+            if y >= WIDTH {
+                y = 0;
+                x += 1;
+            }
         }
         }
     }
     }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::sudoku::*;
 
 
-    #[test]
-    fn check_possible_bitset() {
-        let mut p = Bits(0);
-
-        p.clear();
+    /// Save puzzle to a string.
+    /// Note, we load from (top,left) going down, to (bottom,left) by columns.
 
 
-        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);
+    pub fn save_to_tld(&mut self, start_ch: char, blank: char) -> String {
+        let mut result = String::new();
+        result.reserve(MAX_SIZE as usize);
+        let start_ch = (start_ch as u8 -1) as char;
+        let mut x:u8=0;
+        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 {
-    x + (y * WIDTH as u8)
+/// This is used as usize, so return that instead of 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 {
-    (x - 1) + ((y - 1) * WIDTH as u8)
+pub const fn pos1(x: u8, y: u8) -> usize {
+    (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) {
-    ((pos % WIDTH), (pos / WIDTH))
+pub const fn xy(pos: usize) -> (u8, u8) {
+    ((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();