Kaynağa Gözat

Moved to AnyBoard code.

See previous commit for old 3x3 hard coded way to do things.
Steve Thielemann 4 ay önce
ebeveyn
işleme
5311714707
2 değiştirilmiş dosya ile 138 ekleme ve 39 silme
  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();
     }