Ver Fonte

Added AnyGroup.

This is the dynamic group builder, for any size board.
Steve Thielemann há 4 meses atrás
pai
commit
336b9d0f8e
2 ficheiros alterados com 262 adições e 21 exclusões
  1. 143 0
      sudoku/src/group.rs
  2. 119 21
      sudoku/src/sudoku.rs

+ 143 - 0
sudoku/src/group.rs

@@ -3,6 +3,142 @@ use std::fmt;
 // load sudoku
 use crate::*;
 
+/*
+New code -
+    AnyGroup
+        Handles any size puzzles.
+
+The old code below only handles 3x3 puzzles only!
+ */
+
+#[derive(Debug, Clone)]
+pub struct AnyGroup {
+    pub size: u8,
+    pub width: u8,
+    pub row: Vec<usize>,
+    pub column: Vec<usize>,
+    pub cell: Vec<usize>,
+}
+
+impl AnyGroup {
+    pub fn new(board_size: u8) -> Self {
+        let width = board_size as usize * board_size as usize;
+        let mut g = AnyGroup {
+            size: board_size,
+            width: board_size * board_size,
+            row: vec![0; width * width],
+            column: vec![0; width * width],
+            cell: vec![0; width * width],
+        };
+        g.calculate();
+        g
+    }
+
+    pub fn pos(&self, x: u8, y: u8) -> usize {
+        x as usize + y as usize * self.width as usize
+    }
+
+    pub fn xy(&self, index: usize) -> (u8, u8) {
+        let width = self.width as usize;
+        ((index % width) as u8, (index / width) as u8)
+    }
+
+    pub fn row(&self, row: u8) -> &[usize] {
+        // return slice of row:
+        let width = self.width as usize;
+        let start = row as usize * width;
+        &self.row[start..start + width]
+    }
+
+    pub fn column(&self, column: u8) -> &[usize] {
+        // return slice of row:
+        let width = self.width as usize;
+        let start = column as usize * width;
+        &self.column[start..start + width]
+    }
+
+    pub fn cell(&self, cell: u8) -> &[usize] {
+        // return slice of cell:
+        let width = self.width as usize;
+        let start = cell as usize * width;
+        &self.cell[start..start + width]
+    }
+
+    /// Which cell contains (x,y)?
+    pub fn which_cell(&self, x: u8, y: u8) -> u8 {
+        (x / self.size) + (y / self.size) * self.size
+    }
+
+    pub fn calculate(&mut self) {
+        for y in 0..self.width {
+            for x in 0..self.width {
+                let index = x as usize + y as usize * self.width as usize;
+                self.row[index] = self.pos(x, y);
+                self.column[index] = self.pos(y, x);
+
+                let x_off = x / self.size;
+                let y_off = y / self.size;
+                let x_mod = x % self.size;
+                let y_mod = y % self.size;
+                // x is correct:
+                //    x_mod + y_mod * self.size;
+                // y is correct:
+                //    x_off + y_off * self.size
+                self.cell[index] = self.pos(
+                     x_mod +  y_mod * self.size,
+                     x_off + y_off * self.size
+                    // x_mod + x_off * self.size,
+                    // y_off
+                );
+            }
+        }
+        /*
+                for i in 0..self.width {
+                    println!("row {}: {:?}", i, self.row(i));
+                    println!("col {}: {:?}", i, self.column(i));
+                    println!("cel {}: {:?}", i, self.cell(i));
+                }
+        */
+    }
+
+    pub fn display(&self) {
+        println!("rows:");
+        println!("{:?}", self.row);
+        for i in 0..self.width {
+            let r = self.row(i);
+            print!("[{:2}]: ", i);
+            for j in r {
+                let xy = self.xy(*j);
+                print!("{:2}({:2},{:2}) ", j, xy.0, xy.1);
+            }
+            println!("");
+        }
+        println!("columns:");
+        println!("{:?}", self.column);
+        for i in 0..self.width {
+            let r = self.column(i);
+            print!("[{:2}]: ", i);
+            for j in r {
+                let xy = self.xy(*j);
+                print!("{:2}({:2},{:2}) ", j, xy.0, xy.1);
+            }
+            println!("");
+        }
+
+        println!("cells:");
+        println!("{:?}", self.cell);
+        for i in 0..self.width {
+            let r = self.cell(i);
+            print!("[{:2}]: ", i);
+            for j in r {
+                let xy = self.xy(*j);
+                print!("{:2}({:2},{:2}) ", j, xy.0, xy.1);
+            }
+            println!("");
+        }
+    }
+}
+
 /// Width of the sudoku board.
 const WIDTH: u8 = 9;
 // /// Size (width * height) of the board.
@@ -410,6 +546,13 @@ mod tests {
             assert_eq!(g, *new_g);
         }
     }
+
+    #[test]
+    fn check_dynamic() {
+        let g = AnyGroup::new(3);
+        g.display();
+        assert!(false);
+    }
 }
 
 impl Group {

+ 119 - 21
sudoku/src/sudoku.rs

@@ -114,6 +114,8 @@ impl error::Error for GameLoadError {}
 #[derive(Debug, Clone)]
 pub struct AnyBoard {
     pub size: u8,
+    pub width: u8,
+    pub max_index: usize,
     pub board: Vec<u8>,
 }
 
@@ -126,11 +128,14 @@ impl AnyBoard {
         let n = board_size as usize;
         let s = AnyBoard {
             size: board_size,
+            width: board_size*board_size,
+            max_index: n*n*n*n,
             board: vec![0; n * n * n * n],
         };
         s
     }
 
+    /*
     /// Max board width (size*size)
     pub fn width(&self) -> u8 {
         self.size * self.size
@@ -140,6 +145,7 @@ impl AnyBoard {
     pub fn max_index(&self) -> usize {
         self.width() as usize * self.width() as usize
     }
+    */
 
     /// Clear out the board
     pub fn clear(&mut self) {
@@ -148,12 +154,12 @@ impl AnyBoard {
 
     /// Calculate index position of (x,y)
     pub fn pos(&self, x: u8, y: u8) -> usize {
-        x as usize + y as usize * self.width() as usize
+        x as usize + y as usize * self.width as usize
     }
 
     /// Set a position in the board with a value
     pub fn set(&mut self, x: u8, y: u8, value: u8) {
-        assert!(value <= self.size * self.size);
+        assert!(value <= self.width);
         let index = self.pos(x, y);
         assert!(index <= self.board.capacity());
         self.board[index] = value;
@@ -177,13 +183,13 @@ impl AnyBoard {
         let mut x: u8 = 0;
         let mut y: u8 = 0;
 
-        if s.len() != self.max_index() {
+        if s.len() != self.max_index {
             // self.size * self.size*self.size*self.size {
             return Err(Box::new(GameLoadError {
                 message: format!(
                     "String exceeds ({}) expected length {}.",
                     s.len(),
-                    self.size * self.size
+                    self.width
                 ),
             }));
         }
@@ -191,20 +197,20 @@ impl AnyBoard {
         for ch in s.chars() {
             if ch != blank {
                 let value = (ch as u8 - start_ch as u8) + 1;
-                if value == 0 || value > self.width() {
+                if value == 0 || value > self.width {
                     return Err(Box::new(GameLoadError {
                         message: format!(
                             "String symbol ({}) represents value {}, expecting 1 to {}.",
                             ch,
                             value,
-                            self.size * self.size
+                            self.width
                         ),
                     }));
                 }
                 self.set(x, y, value);
             }
             y += 1;
-            if y >= self.size * self.size {
+            if y >= self.width {
                 y = 0;
                 x += 1;
             }
@@ -215,12 +221,12 @@ impl AnyBoard {
     /// 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();
-        result.reserve(self.max_index());
+        result.reserve(self.max_index);
         let start_ch = (start_ch as u8 - 1) as char;
         let mut x: u8 = 0;
         let mut y: u8 = 0;
 
-        for _i in 0..self.max_index() {
+        for _i in 0..self.max_index {
             let value = self.get(x, y);
             if value == 0 {
                 result.push(blank);
@@ -228,7 +234,7 @@ impl AnyBoard {
                 result.push((start_ch as u8 + value) as char);
             }
             y += 1;
-            if y >= self.width() {
+            if y >= self.width {
                 y = 0;
                 x += 1;
             }
@@ -236,9 +242,68 @@ impl AnyBoard {
         result
     }
 
+    /// Load puzzle from string (top,left) going right.
+    pub fn load_from_tlr(
+        &mut self,
+        start_ch: char,
+        blank: char,
+        s: &str,
+    ) -> Result<(), Box<dyn error::Error>> {
+        self.clear();
+
+        if s.len() != self.max_index {
+            // self.size * self.size*self.size*self.size {
+            return Err(Box::new(GameLoadError {
+                message: format!(
+                    "String exceeds ({}) expected length {}.",
+                    s.len(),
+                    self.width
+                ),
+            }));
+        }
+
+        let mut i: usize = 0;
+
+        for ch in s.chars() {
+            if ch != blank {
+                let value = (ch as u8 - start_ch as u8) + 1;
+                if value == 0 || value > self.width {
+                    return Err(Box::new(GameLoadError {
+                        message: format!(
+                            "String symbol ({}) represents value {}, expecting 1 to {}.",
+                            ch,
+                            value,
+                            self.width
+                        ),
+                    }));
+                }
+                self.board[i] = value;
+                i += 1;
+            }
+        }
+        Ok(())
+    }
+
+    /// 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();
+        result.reserve(self.max_index);
+        let start_ch = (start_ch as u8 - 1) as char;
+
+        for i in 0..self.max_index {
+            let value = self.board[i];
+            if value == 0 {
+                result.push(blank);
+            } else {
+                result.push((start_ch as u8 + value) as char);
+            }
+        }
+        result
+    }
+
     pub fn display(&self) {
         let line = "═".repeat(self.size as usize);
-        let alpha_display = self.width() >= 10;
+        let alpha_display = self.width >= 10;
         // println!("╔{}╦{}╦{}╗", line, line, line);
 
         println!("alpha = {}", alpha_display);
@@ -253,9 +318,9 @@ impl AnyBoard {
         }
         println!("╗");
 
-        for y in 0..self.width() {
+        for y in 0..self.width {
             print!("║");
-            for x in 0..self.width() {
+            for x in 0..self.width {
                 let value = self.get(x, y);
                 if value == 0 {
                     print!(" ");
@@ -272,7 +337,7 @@ impl AnyBoard {
             }
             println!("");
             if y % self.size == self.size - 1 {
-                if y + 1 == self.width() {
+                if y + 1 == self.width {
                     // Bottom
                     for i in 0..self.size {
                         if i == 0 {
@@ -299,6 +364,20 @@ impl AnyBoard {
             }
         }
     }
+
+    /// Is the puzzle completed?
+    /// Have all of the locations been filled with a value?
+    /// - This does not validate that it is a correct puzzle.
+    ///   It doesn't check for duplicate digits (for example).
+    pub fn complete(&self) -> bool {
+        for i in 0..self.max_index {
+            if self.board[i] == 0 {
+                return false;
+            }
+        }
+        true
+    }
+
 }
 
 // Need to use u32, so 5*5=25, 25 bits can be accessed.
@@ -307,25 +386,35 @@ impl AnyBoard {
 #[derive(Debug, Clone)]
 pub struct AnyPossible {
     pub size: u8,
+    pub width: u8,
+    pub max_index: usize,
     pub possible: Vec<GenBits<u32>>,
 }
 
 impl AnyPossible {
     pub fn new(board_size: u8) -> Self {
         let mut initial = GenBits::<u32>(0);
-        initial.set_bits(1..board_size);
+        let width = board_size * board_size;
+        initial.set_bits(1..width);
+
         Self {
             size: board_size,
-            possible: vec![initial; board_size as usize * board_size as usize],
+            width: width,
+            max_index: width as usize * width as usize,
+            possible: vec![initial; width as usize * width as usize],
         }
     }
 
     pub fn clear(&mut self) {
         let mut initial = GenBits::<u32>(0);
-        initial.set_bits(1..self.size);
-        self.possible = vec![initial; self.size as usize * self.size as usize];
+        // let width = self.size * self.size;
+        initial.set_bits(1..self.width);
+
+        self.possible = vec![initial; self.max_index]; 
+        // width as usize * width as usize];
     }
 
+    #[inline]
     pub fn set(&mut self, index: usize, value: usize, state: bool) {
         self.possible[index].set(value, state);
     }
@@ -341,7 +430,7 @@ impl AnySolver {
     pub fn new(initial_board: &AnyBoard) -> Self {
         let mut s = AnySolver {
             board: initial_board.clone(),
-            possible: AnyPossible::new(initial_board.max_index() as u8),
+            possible: AnyPossible::new(initial_board.size),
         };
         s.reset_possible();
         s
@@ -372,8 +461,8 @@ impl AnySolver {
     /// - When something has changed, and the possibles are out of sync.
     pub fn reset_possible(&mut self) {
         self.possible.clear();
-        for y in 0..self.board.width() {
-            for x in 0..self.board.width() {
+        for y in 0..self.board.width {
+            for x in 0..self.board.width {
                 let value = self.board.get(x, y);
                 if value != 0 {
                     self.process_move(x, y, value);
@@ -381,6 +470,15 @@ impl AnySolver {
             }
         }
     }
+
+    /// 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) {
+        self.board.set(x, y, value);
+        self.process_move(x, y, value);
+    }
 }
 
 #[cfg(test)]