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 tháng trước cách đây
mục cha
commit
767d21675a
4 tập tin đã thay đổi với 197 bổ sung125 xóa
  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();
 
         for i in 0..9 {
-            println!("Index={}", i);
+            // println!("Index={}", i);
             g.for_column(i, 1);
             let new_g = for_column(i);
             assert_eq!(g, *new_g);
@@ -393,7 +393,7 @@ mod tests {
         let mut g = Group::new();
 
         for i in 0..9 {
-            println!("Index={}", i);
+            // println!("Index={}", i);
             g.for_row(1, i);
             let new_g = for_row(i);
             assert_eq!(g, *new_g);
@@ -404,7 +404,7 @@ mod tests {
     fn check_cells() {
         let mut g = Group::new();
         for i in 0..9 {
-            println!("Index={}", i);
+            // println!("Index={}", i);
             g.for_block((i % 3) * 3, (i / 3) * 3);
             let new_g = for_cell(i);
             assert_eq!(g, *new_g);

+ 1 - 0
sudoku/src/lib.rs

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

+ 69 - 122
sudoku/src/sudoku.rs

@@ -1,5 +1,6 @@
 // pub mod group;
 use crate::group::*;
+use crate::bits::*;
 
 // use std::collections::HashSet;
 use std::string::String;
@@ -10,9 +11,6 @@ const WIDTH: u8 = SIZE*SIZE;
 /// Size (width * height) of the board.
 const MAX_SIZE: u8 = WIDTH * WIDTH; // 81;
 
-// Use bitfields instead of HashSets.
-use bit_field::BitField;
-
 extern crate rand_chacha;
 // use rand::prelude::*;
 use rand::seq::SliceRandom;
@@ -20,135 +18,83 @@ use rand_chacha::rand_core::SeedableRng;
 use rand_chacha::ChaCha20Rng;
 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.
 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.
-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.
-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.
-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;
@@ -284,7 +231,7 @@ impl Sudoku {
     /// 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) {
         self.clear();
-        let mut i: u8 = 0;
+        let mut i: usize = 0;
 
         for ch in s.chars() {
             if ch != blank {
@@ -471,7 +418,7 @@ impl Sudoku {
     /// - As possibilities are tried, it recursively calls itself to see
     ///   if there are any solutions with the given possibility.
     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 {
                 // Ok, there's a blank here
 
@@ -576,7 +523,7 @@ impl Sudoku {
             possible: self.possible,
         };
 
-        for idx in 0..MAX_SIZE {
+        for idx in 0..MAX_SIZE as usize {
             if self.board[idx as usize] == 0 {
                 let (x, y) = xy(idx);
                 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.
         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 {
                 // Get the value
                 let value = self.possible[i as usize].iter().next().unwrap();