فهرست منبع

Moved to AnyBoard code.

See previous commit for old 3x3 hard coded way to do things.
Steve Thielemann 10 ماه پیش
والد
کامیت
5311714707
2فایلهای تغییر یافته به همراه138 افزوده شده و 39 حذف شده
  1. 44 15
      src/main.rs
  2. 94 24
      sudoku/src/sudoku.rs

+ 44 - 15
src/main.rs

@@ -7,8 +7,6 @@ use sudoku::ksudoku::{load_ksudoku, save_ksudoku, Ksudoku};
 use sudoku::sudoku::*;
 
 fn main() {
-    let mut s = Sudoku::new();
-
     let args = command!()
         .arg(
             arg!(-f --file <FILE> "Filename to load")
@@ -21,8 +19,14 @@ fn main() {
             )
             .action(ArgAction::SetTrue),
         )
-        .arg(arg!(-b --brute ... "Brute Force solver").action(ArgAction::SetTrue))
+        .arg(arg!(-b --brute ... "Brute-force solver").action(ArgAction::SetTrue))
         .arg(arg!(-d --debug ... "Debug output").action(ArgAction::SetTrue))
+        .arg(
+            arg!(-s --size <SIZE>... "Puzzle size")
+                .required(false)
+                .default_value("3")
+                .value_parser(value_parser!(u8)),
+        )
         .get_matches();
 
     let mut debug: bool = false;
@@ -31,6 +35,10 @@ fn main() {
     }
 
     if args.get_flag("make") {
+        let size: u8 = *args.get_one::<u8>("size").unwrap();
+        let mut s = AnyBoard::new(size);
+
+        /*
         s.make();
         let mut p = s.clone();
 
@@ -55,6 +63,7 @@ fn main() {
                 println!("Saved!");
             }
         }
+        */
         return;
     }
 
@@ -69,27 +78,46 @@ fn main() {
 
         let puzzle = result.unwrap();
 
-        // verify it is known to us.
+        if puzzle.specific_type != "Plain" || puzzle.puzzle_type != "sudoku" {
+            panic!(
+                "We support Plain/sudoku not {}/{}",
+                puzzle.specific_type, puzzle.puzzle_type
+            );
+        }
 
-        assert!(puzzle.specific_type == "Plain");
-        assert!(puzzle.puzzle_type == "sudoku");
+        let size = (puzzle.order as f64).sqrt() as u8;
 
-        // This section will be updated, once AnyBoard is completed.
-        if puzzle.order != 9 {
-            panic!("We only support (3x3) puzzles at this time.")
+        if size < 3 || size > 5 {
+            panic!("We only support sizes 3, 4, and 5 (not {})", size);
         }
 
+        let mut s = AnyBoard::new(size);
+        let mut solution = AnyBoard::new(size);
+
         // Ksudoku is stored TLD.
-        s.load_from_tld('b', '_', &puzzle.puzzle);
+        let result = s.load_from_tld('b', '_', &puzzle.puzzle);
+        if result.is_ok() {
         s.display();
+        } else {
+            panic!("Failed loading puzzle: {}", result.unwrap_err());
+        }
+
+        let result = solution.load_from_tld('b', '_', &puzzle.solution);
+        if result.is_err() {
+            panic!("Failed loading result: {}", result.unwrap_err());
+        }
 
         if args.get_flag("brute") {
-            println!("Solutions: {}", s.bruteforce_solver());
-        } else {
-            if debug {
-                s.display_possible();
+            let (found, solutions) = s.brute_force_solver(2);
+            println!("Solutions: {}", found);
+            if found != 0 {
+                if solutions[0] != solution {
+                    println!("The solution doesn't match the one from the file:");
+                    solution.display();
+                }
             }
-
+        } else {
+            /*
             while s.solve(debug) {
                 println!("Try it again...");
                 if debug {
@@ -110,6 +138,7 @@ fn main() {
                     s.display();
                 }
             }
+            */
         }
     }
 }

+ 94 - 24
sudoku/src/sudoku.rs

@@ -113,7 +113,7 @@ impl error::Error for GameLoadError {}
 // const DEBUG_OUTPUT: bool = false;
 
 // Vec doesn't implement Copy ...
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct AnyBoard {
     pub size: u8,
     pub width: u8,
@@ -400,6 +400,30 @@ impl AnyBoard {
         }
     }
 
+    pub fn to_strings(&self) -> Vec<String> {
+        let mut result = Vec::<String>::new();
+        let alpha_display = self.width >= 10;
+
+        for y in 0..self.width {
+            let mut line = String::new();
+            line.reserve(self.width as usize);
+            for x in 0..self.width {
+                let value = self.get(x, y);
+                if value == 0 {
+                    line.push(' ');
+                } else {
+                    if alpha_display {
+                        line.push((value - 1 + 'A' as u8) as char);
+                    } else {
+                        line.push((value + '0' as u8) as char);
+                    }
+                }
+            }
+            result.push(line);
+        }
+        result
+    }
+
     /// Is the puzzle completed?
     /// Have all of the locations been filled with a value?
     /// - This does not validate that it is a correct puzzle.
@@ -413,12 +437,12 @@ impl AnyBoard {
         true
     }
 
-    pub fn brute_force_solver(&self) -> u16 {
+    pub fn brute_force_solver(&self, max: u16) -> (u16, Vec<AnyBoard>) {
         let mut workset = self.clone();
         let mut total_solutions: u16 = 0;
         let mut solutions: Vec<AnyBoard> = Vec::new();
         let groups = AnyGroup::new(workset.size);
-        solutions.reserve(1);
+        solutions.reserve(max as usize);
         workset.brute_force(&mut total_solutions, &mut solutions, &groups);
 
         if solutions.len() > 0 {
@@ -426,7 +450,7 @@ impl AnyBoard {
             solutions[0].display();
             println!("***");
         }
-        total_solutions
+        (total_solutions, solutions)
     }
 
     fn brute_force(
@@ -543,7 +567,7 @@ impl AnyPossible {
             index < self.max_index,
             "Index {} >= {}",
             index,
-            self.max_index 
+            self.max_index
         );
         self.possible[index].set(value - 1, state);
     }
@@ -554,7 +578,7 @@ impl AnyPossible {
             index < self.max_index,
             "Index {} >= {}",
             index,
-            self.max_index 
+            self.max_index
         );
         self.possible[index].get(value - 1)
     }
@@ -589,7 +613,7 @@ impl AnyPossible {
                 // let stuff: String = values.into_iter().map(|i| i.to_string().push_str(",")).collect::<String>();
                 let stuff: String = values
                     .into_iter()
-                    .map(|i| (i+1).to_string())
+                    .map(|i| (i + 1).to_string())
                     .collect::<String>();
                 /*
                 if stuff.len() > 1 {
@@ -643,7 +667,7 @@ impl AnySolver {
     }
 
     pub fn clear(&mut self) {
-        let board_size:u8 = self.board.size;
+        let board_size: u8 = self.board.size;
         self.board = AnyBoard::new(board_size);
         self.possible = AnyPossible::new(board_size);
         self.reset_possible();
@@ -680,7 +704,7 @@ impl AnySolver {
         for g in g {
             self.possible.set(*g, value, false);
         }
-        let idx = self.possible.pos(x,y);
+        let idx = self.possible.pos(x, y);
         self.possible.possible[idx] = GenBits::<u32>(0); // .clear();
     }
 
@@ -775,7 +799,7 @@ impl AnySolver {
 
     pub fn make(&mut self) {
         let mut rng = ChaCha20Rng::from_entropy();
-        self.reset_possible(); 
+        self.reset_possible();
         self.fill_board(&mut rng);
     }
 
@@ -786,12 +810,12 @@ impl AnySolver {
 
         for idx in 0..max_index {
             if self.board.board[idx] == 0 {
-                let (x,y) = self.board.xy(idx);
-                let mut available :Vec<u8> = Vec::new();
-                available.reserve(width as usize); 
+                let (x, y) = self.board.xy(idx);
+                let mut available: Vec<u8> = Vec::new();
+                available.reserve(width as usize);
 
                 for t in self.possible.possible[idx].iter() {
-                    available.push(t+1);
+                    available.push(t + 1);
                 }
                 if available.len() == 0 {
                     return false;
@@ -801,12 +825,12 @@ impl AnySolver {
                 // Randomize the possible items.
                 available.shuffle(rng);
                 // available.as_mut_slice().shuffle(rng);
-                
+
                 // print!("{:?}", available);
                 for value in available.into_iter() {
                     assert!(value != 0);
 
-                    self.set(x,y, value);
+                    self.set(x, y, value);
                     // self.board.display();
                     // self.possible.display();
 
@@ -832,7 +856,7 @@ impl AnySolver {
         }
         // We've visited everything, and it isn't 0.
         true
-    }    
+    }
 
     // This seems .. broken.  Not sure.  4x4 kicked me.
     pub fn brute_force_solver(&mut self) -> u16 {
@@ -850,6 +874,7 @@ impl AnySolver {
             solutions[0].display();
             println!("***");
         }
+
         total_solutions
     }
 
@@ -925,14 +950,52 @@ mod tests {
             "__c_____e_h__cb___bd___ei_ch_jb__d___________i_eh__b__dg___ij_f_i__jg_____b_____g",
         );
         assert!(result.is_ok());
-        board.display();
+        let strings = board.to_strings();
+        assert_eq!(
+            strings,
+            vec![
+                "  17 83  ",
+                " 73   68 ",
+                "2  9 4  1",
+                "   1 7   ",
+                " 2     9 ",
+                " 14   86 ",
+                "  83 19  ",
+                "         ",
+                "4 2   5 6"
+            ]
+        );
+
+        // board.display();
         let mut solver = AnySolver::new_from(&board);
         assert!(solver.validate_board());
 
         board = AnyBoard::new(4);
         let result = board.load_from_tld('b', '_', "_bo_j_m_f__dp__ge_h_pcfdo___q__n___qio______df___f__l___hpnm_i___obig_p_qhl__k_m_dq_cn______o_g_p_____bi_kc__jn______fo____gi______eb____jd______jk__ml_bn_____i_m_b______oq_nj_d_n__jck_m_fgbq___i_medp___n__b___dg______qjk___j__p___fgohl_d_qo__mq__g_d_p_le_");
         assert!(result.is_ok());
-        board.display();
+        let strings = 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 "                
+            ]
+        );
+        // board.display();
 
         let mut solver = AnySolver::new_from(&board);
         assert!(solver.validate_board());
@@ -955,11 +1018,15 @@ mod tests {
             "__c_____e_h__cb___bd___ei_ch_jb__d___________i_eh__b__dg___ij_f_i__jg_____b_____g",
         );
         assert!(result.is_ok());
+        assert_eq!(3, board.size);
+        assert_eq!(9, board.width);
+        assert_eq!(81, board.max_index);
+
         board.display();
         let mut solver = AnySolver::new_from(&board);
         assert!(solver.validate_board());
-        let solutions = solver.board.brute_force_solver();
-        assert!(solutions == 1, "Expected 1 solution, got {}", solutions);
+        let solutions = solver.board.brute_force_solver(2);
+        assert!(solutions.0 == 1, "Expected 1 solution, got {}", solutions.0);
 
         // 4x4 board takes 40 minutes
 
@@ -971,8 +1038,8 @@ mod tests {
 
             let mut solver = AnySolver::new_from(&board);
             assert!(solver.validate_board());
-            let solutions = solver.board.brute_force_solver();
-            assert!(solutions == 1);
+            let solutions = solver.board.brute_force_solver(2);
+            assert!(solutions.0 == 1);
         }
 
         /*
@@ -989,7 +1056,10 @@ mod tests {
 
     #[test]
     fn make_board() {
-        let mut solver = AnySolver::new(4);
+        // Making a 4x4 board varies (0.3-60 seconds).
+        // Maybe we don't want to do this in a test, because of the randomness involved.
+
+        let mut solver = AnySolver::new(3);
         solver.make();
         solver.board.display();
     }