Преглед на файлове

Code cleanup. Ksudoku loads and solves test files.

Steve Thielemann преди 4 месеца
родител
ревизия
6588ebb097
променени са 4 файла, в които са добавени 342 реда и са изтрити 37 реда
  1. 1 0
      sudoku/Cargo.toml
  2. 115 37
      sudoku/src/group.rs
  3. 216 0
      sudoku/src/ksudoku.rs
  4. 10 0
      sudoku/src/sudoku.rs

+ 1 - 0
sudoku/Cargo.toml

@@ -11,6 +11,7 @@ num = "0.4.3"
 rand = "0.8.5"
 rand_chacha = "0.3.1"
 rand_core = { version = "0.6.4", features = ["getrandom"] }
+strum = { version = "0.26.3", features = ["derive"] }
 xml = "0.8.20"
 
 [lib]

+ 115 - 37
sudoku/src/group.rs

@@ -6,14 +6,39 @@ New code -
 The old code below only handles 3x3 puzzles only!
  */
 
+// use std::fmt;
+use strum::EnumIter;
+use strum::IntoEnumIterator;
+// use strum_macros::EnumIter;
+
+#[derive(Debug, EnumIter, PartialEq, Copy, Clone)]
+pub enum Groups {
+    Row,
+    Column,
+    Cell,
+}
+
+/*
+ impl fmt::Display for Groups {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{:?}", self)
+        // or, alternatively:
+        // fmt::Debug::fmt(self, f)
+    }
+}
+*/
+
 #[derive(Debug, Clone)]
 pub struct AnyGroup {
     pub size: u8,
     pub width: u8,
     pub max_index: usize,
+    pub groups: [Vec<usize>; 3],
+    /*
     pub row: Vec<usize>,
     pub column: Vec<usize>,
     pub cell: Vec<usize>,
+    */
 }
 
 /// Find the number of digits needed to display given number.
@@ -29,14 +54,18 @@ fn find_number_width(mut number: usize) -> usize {
 
 impl AnyGroup {
     pub fn new(board_size: u8) -> Self {
-        let width = board_size as usize * board_size as usize;
+        let b_width = board_size as usize * board_size as usize;
+        let size = b_width * b_width;
         let mut g = AnyGroup {
             size: board_size,
             width: board_size * board_size,
-            max_index: width * width,
-            row: vec![0; width * width],
-            column: vec![0; width * width],
-            cell: vec![0; width * width],
+            max_index: size,
+            groups: [vec![0; size], vec![0; size], vec![0; size]],
+            /*
+            row: vec![0; b_width * b_width],
+            column: vec![0; size],
+            cell: vec![0; size],
+            */
         };
         g.calculate();
         g
@@ -51,25 +80,31 @@ impl AnyGroup {
         ((index % width) as u8, (index / width) as u8)
     }
 
+    pub fn group(&self, g: Groups, row: u8) -> &[usize] {
+        let width = self.width as usize;
+        let start = row as usize * width;
+        &self.groups[g as usize][start..start + width]
+    }
+
     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]
+        &self.groups[Groups::Row as usize][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]
+        &self.groups[Groups::Column as usize][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]
+        &self.groups[Groups::Cell as usize][start..start + width]
     }
 
     /// Which cell contains (x,y)?
@@ -81,8 +116,11 @@ impl AnyGroup {
         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);
+                self.groups[Groups::Row as usize][index] = self.pos(x, y);
+                self.groups[Groups::Column as usize][index] = self.pos(y, x);
+
+                // 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;
@@ -92,11 +130,15 @@ impl AnyGroup {
                 //    x_mod + y_mod * self.size;
                 // y is correct:
                 //    x_off + y_off * self.size
-                self.cell[index] = self.pos(
+                self.groups[Groups::Cell as usize][index] =
+                    self.pos(x_mod + y_mod * self.size, 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
                 );
+                */
             }
         }
     }
@@ -124,6 +166,23 @@ impl AnyGroup {
             println!("");
         };
 
+        for g in Groups::iter() {
+            println!("{:?}:", g);
+
+            for i in 0..self.width {
+                let r = self.group(g, i);
+                /*
+                print!("[{:2}]: ", i);
+                for j in r {
+                    let xy = self.xy(*j);
+                    print!("{0:1$}({2:2},{3:2}) ", j, max_index_size, xy.0, xy.1);
+                }
+                println!("");
+                */
+                printer(i, r);
+            }
+        }
+        /*
         println!("rows:");
         // println!("{:?}", self.row);
         for i in 0..self.width {
@@ -167,56 +226,74 @@ impl AnyGroup {
             */
             printer(i, r);
         }
+        */
     }
 }
 
-
 #[cfg(test)]
 mod tests {
     use crate::group::*;
 
     #[test]
+    /// Verify that each index (0..max_index) is defined in each group.
+    /// - Verify that it is used, and only once.
     fn check_dynamic() {
-        // Verify that each index (0..max_index) is defined in each group.
-        // - Verify that it is used, and only once.
+
 
         for size in 3..=5 {
             let g = AnyGroup::new(size);
+            // g.display();
             let mut all = vec![0 as u8; g.max_index];
 
             // Verify the X,Y values are in the correct cell.
-            all.fill(0);
+            for gr in Groups::iter() {
+                all.fill(0);
 
-            for idx in 0..g.width {
-                let grp = g.cell(idx);
-                // g.display();
-                for indexes in grp {
-                    let (x, y) = g.xy(*indexes);
+                for idx in 0..g.width {
+                    let grp = g.group(gr, idx); // cell(idx);
+                                                // g.display();
+                    for indexes in grp {
+                        let (x, y) = g.xy(*indexes);
+                        assert!(
+                            all[*indexes] == 0,
+                            "{:?} Size {} Index [{}]({},{}) already seen",
+                            gr,
+                            size,
+                            *indexes,
+                            x + 1,
+                            y + 1
+                        );
+                        all[*indexes] = 1;
+                        if gr == Groups::Cell {
+                            assert_eq!(
+                                g.which_cell(x, y),
+                                idx,
+                                "Verify {:?} [{}]({},{}) in cell {}",
+                                gr,
+                                *indexes,
+                                x,
+                                y,
+                                idx
+                            );
+                        }
+                    }
+                }
+                for indexes in 0..g.max_index {
+                    let (x, y) = g.xy(indexes);
                     assert!(
-                        all[*indexes] == 0,
-                        "Size {} Index [{}]({},{}) already seen",
+                        all[indexes] == 1,
+                        "{:?} Size {} Index [{}]({},{}) not seen",
+                        gr,
                         size,
-                        *indexes,
+                        indexes,
                         x + 1,
                         y + 1
                     );
-                    all[*indexes] = 1;
-                    assert_eq!(g.which_cell(x, y), idx, "Verify [{}]({},{}) in cell {}", *indexes, x, y, idx);
                 }
+    
             }
 
-            for indexes in 0..g.max_index {
-                let (x, y) = g.xy(indexes);
-                assert!(
-                    all[indexes] == 1,
-                    "Size {} Index [{}]({},{}) not seen",
-                    size,
-                    indexes,
-                    x + 1,
-                    y + 1
-                );
-            }
-
+            /* 
             all.fill(0);
             for idx in 0..g.width {
                 let grp = g.row(idx);
@@ -277,6 +354,7 @@ mod tests {
                     y + 1
                 );
             }
+            */
         }
     }
 }

+ 216 - 0
sudoku/src/ksudoku.rs

@@ -310,4 +310,220 @@ mod tests {
             ]
         );
     }
+
+    #[test]
+    fn solve_puzzles() {
+        let mut testfile = test_files_path();
+        testfile.push("9-puzzle");
+
+        let result = load_ksudoku(testfile);
+        assert!(result.is_ok());
+        let result = result.unwrap();
+        assert!(result.puzzle_type == "sudoku");
+        assert!(result.specific_type == "Plain");
+
+        let size = (result.order as f64).sqrt() as u8;
+        let mut board = AnyBoard::new(size);
+        let load = board.load_from_tld('b', '_', &result.puzzle);
+        let mut solution = AnyBoard::new(size);
+        let solution_load = solution.load_from_tld('b', '_', &result.solution);
+        assert!(load.is_ok());
+        assert!(solution_load.is_ok());
+
+        // Ok, puzzle is loaded!
+        let strings = board.to_strings();
+        assert_eq!(
+            strings,
+            vec![
+                "  4   8  ",
+                " 6  1  2 ",
+                "3  9 5  6",
+                "  72 16  ",
+                " 3     9 ",
+                "  18 35  ",
+                "9  3 6  2",
+                " 4  5  8 ",
+                "  3   7  ",
+            ]
+        );
+        // Verify the solution in the file.
+        let strings = solution.to_strings();
+        assert_eq!(
+            strings,
+            vec![
+                "714632859",
+                "569718324",
+                "328945176",
+                "457291638",
+                "836574291",
+                "291863547",
+                "975386412",
+                "642157983",
+                "183429765",
+            ]
+        );
+
+        // Solve puzzle
+        let mut solver = AnySolver::new_from(&board);
+        assert!(solver.solve_logic());
+        assert_eq!(solver.board, solution);
+
+        let mut testfile = test_files_path();
+        testfile.push("16-puzzle");
+
+        let result = load_ksudoku(testfile);
+        assert!(result.is_ok());
+        let result = result.unwrap();
+        assert!(result.puzzle_type == "sudoku");
+        assert!(result.specific_type == "Plain");
+
+        let size = (result.order as f64).sqrt() as u8;
+        let mut board = AnyBoard::new(size);
+        let load = board.load_from_tld('b', '_', &result.puzzle);
+        assert!(load.is_ok());
+        let mut solution = AnyBoard::new(size);
+        let solution_load = solution.load_from_tld('b', '_', &result.solution);
+        assert!(solution_load.is_ok());
+
+        // Ok, puzzle is loaded!
+        let strings: Vec<String> = board.to_strings();
+        assert_eq!(
+            strings,
+            vec![
+                " D    O    C  IN",
+                "A  ENC   IL     ",
+                "NG  AP   J MHC  ",
+                "  P H   D A  FOL",
+                "IOHKFB  A   L  P",
+                " BN  M E L ID   ",
+                "LE  O AN K BC   ",
+                " C    H    JO EF",
+                "EN GP    A    F ",
+                "   OG J IM L  NC",
+                "   MK B C N  PG ",
+                "C  L   F  PEMIKO",
+                "OPC  N H   F J  ",
+                "  EHJ I   MA  CK",
+                "     FM   IPA  D",
+                "FM  L    H    P "
+            ]
+        );
+
+        let strings = solution.to_strings();
+        assert_eq!(
+            strings,
+            vec![
+                "HDLJMKOBFPECGAIN",
+                "AKOENCFDGILHPMBJ",
+                "NGFBAPLIOJKMHCDE",
+                "MIPCHJEGDBANKFOL",
+                "IOHKFBDJAECGLNMP",
+                "JBNFCMPEHLOIDKAG",
+                "LEMDOGANPKFBCHJI",
+                "GCAPILHKMNDJOBEF",
+                "ENIGPOCMKABDJLFH",
+                "PFKOGEJAIMHLBDNC",
+                "DHJMKIBLCFNOEPGA",
+                "CABLDHNFJGPEMIKO",
+                "OPCAENKHBDGFIJLM",
+                "BLEHJDIPNOMAFGCK",
+                "KJGNBFMOLCIPAEHD",
+                "FMDILAGCEHJKNOPB",
+            ]
+        );
+
+        // Solve puzzle
+        let mut solver = AnySolver::new_from(&board);
+        assert!(solver.solve_logic());
+        assert_eq!(solver.board, solution);
+
+        let mut testfile = test_files_path();
+        testfile.push("25-puzzle");
+
+        let result = load_ksudoku(testfile);
+        assert!(result.is_ok());
+        let result = result.unwrap();
+        assert!(result.puzzle_type == "sudoku");
+        assert!(result.specific_type == "Plain");
+
+        let size = (result.order as f64).sqrt() as u8;
+        let mut board = AnyBoard::new(size);
+        let load = board.load_from_tld('b', '_', &result.puzzle);
+        assert!(load.is_ok());
+        let mut solution = AnyBoard::new(size);
+        let solution_load = solution.load_from_tld('b', '_', &result.solution);
+        assert!(solution_load.is_ok());
+
+        // Ok, puzzle is loaded!
+        // board.display();
+        let strings: Vec<String> = board.to_strings();
+        assert_eq!(
+            strings,
+            vec![
+                " T DPF    Y H R    WSX L ",
+                "L QF     J X C G     UB D",
+                " CK S LNRP  G  UTQO H MA ",
+                "JG   H S XELDUPM F B   RT",
+                "Y N RQ UCDOK AISJL HP G E",
+                "F  OA N UK VQI HY B DT  C",
+                "  S  A C VUE GJQ N I  R  ",
+                "  CPD JGMIA   OELSU VBK  ",
+                "  G LE D BHT XMW K PI Y  ",
+                " EXIBOWFLY     VAMTJUGQS ",
+                "G  HV TMI       EWF BR  O",
+                " A JFUK W  P D  M GLXE N ",
+                "R LN G             O QI F",
+                " S TCYP B  H O  X JNAK M ",
+                "M  XK ALJ       UHQ GP  Y",
+                " VTRYBSEFM     KWOICJNLG ",
+                "  U XD J WCN RTA E QY P  ",
+                "  HQN COVTJ   EBGDL WSF  ",
+                "  F  X Y LWB SVJ R T  O  ",
+                "E  CJ R AN GOF XH V MD  B",
+                "S V OI ANHDC TGLQJ YF X P",
+                "CX   L K RFUYBWO V A   DQ",
+                " FR H DQOC  K  INBW T UY ",
+                "T JL     G I P F     OC W",
+                " B KMV    Q J H    GRI E "
+            ]
+        );
+
+        let strings: Vec<String> = solution.to_strings();
+        assert_eq!(
+            strings,
+            vec![
+                "UTODPFIBGAYMHQRNKCEWSXJLV",
+                "LHQFIMYTEJSXWCNGVPAROUBKD",
+                "XCKESWLNRPVFGJBUTQODHYMAI",
+                "JGAVWHOSKXELDUPMIFYBQCNRT",
+                "YMNBRQVUCDOKTAISJLXHPWGFE",
+                "FJMOARNPUKLVQISHYGBXDTEWC",
+                "KWSYTAHCXVUEBGJQONDILFRPM",
+                "QRCPDTJGMIAYNWOELSUFVBKHX",
+                "VNGULEQDSBHTFXMWCKRPIAYOJ",
+                "HEXIBOWFLYRDPKCVAMTJUGQSN",
+                "GQPHVCTMISXJANLYEWFKBRDUO",
+                "OABJFUKVWQIPCDYRMTGLXESNH",
+                "RYLNUGXHDETWVMKPBASOCQIJF",
+                "WSETCYPRBFGHUOQDXIJNAKVML",
+                "MIDXKNALJOBRSEFCUHQVGPWTY",
+                "AVTRYBSEFMPQXHDKWOICJNLGU",
+                "BOUSXDGJHWCNLRTAFEMQYVPIK",
+                "IPHQNKCOVTJAMYEBGDLUWSFXR",
+                "DKFMGXUYQLWBISVJPRNTEHOCA",
+                "ELWCJPRIANKGOFUXHYVSMDTQB",
+                "SUVWOIEANHDCRTGLQJKYFMXBP",
+                "CXIGELMKTRFUYBWOSVPANJHDQ",
+                "PFRAHJDQOCMSKVXINBWETLUYG",
+                "TDJLQSBXYGNIEPAFRUHMKOCVW",
+                "NBYKMVFWPUQOJLHTDXCGRIAES",
+            ]
+        );
+
+        // solve puzzle
+        let mut solver = AnySolver::new_from(&board);
+        assert!(solver.solve_logic());
+        assert_eq!(solver.board, solution);
+        
+    }
 }

+ 10 - 0
sudoku/src/sudoku.rs

@@ -2,6 +2,8 @@
 use crate::bits::*;
 use crate::group::*;
 
+use strum::IntoEnumIterator;
+
 // use std::collections::HashSet;
 use std::string::String;
 
@@ -905,6 +907,13 @@ impl AnySolver {
                 }
             };
 
+            for gr in Groups::iter() {
+                for i in 0..width {
+                let g = grp.group(gr, i);
+                    group_process(self, g);
+                }
+            }
+            /*
             // Change to 0..WIDTH ...  Keep it simple.
             for i in 0..width {
                 // println!("Column {i}:");
@@ -917,6 +926,7 @@ impl AnySolver {
                 g = grp.cell(i);
                 group_process(self, g);
             }
+            */
 
             if found_something == true {
                 // Ok, pass 2 found something.