Sfoglia il codice sorgente

Working commandline parsing.

Steve Thielemann 9 mesi fa
parent
commit
683bda27aa
4 ha cambiato i file con 110 aggiunte e 37 eliminazioni
  1. 1 1
      Cargo.toml
  2. 31 24
      src/main.rs
  3. 4 4
      sudoku/src/group.rs
  4. 74 8
      sudoku/src/sudoku.rs

+ 1 - 1
Cargo.toml

@@ -6,7 +6,7 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-clap = { version = "4.4.18", features = ["derive"] }
+clap = { version = "4.4.18", features = ["cargo"] }
 sudoku = { path = "sudoku" }
 
 

+ 31 - 24
src/main.rs

@@ -1,38 +1,45 @@
-use clap::Parser;
+use clap::{arg, command, value_parser, ArgAction, Command};
+use std::path::PathBuf;
+
+// https://docs.rs/clap/latest/clap/_tutorial/chapter_0/index.html
 
 use sudoku::ksudoku::load_ksudoku;
 use sudoku::sudoku::*;
 
-// Command line arguments - via clap
-
-#[derive(Parser)]
-struct Cli {
-    /// The path to the file to
-    #[arg(short = 'f', long = "file", value_name = "FILENAME")]
-    filename: Option<std::path::PathBuf>,
-
-    /// Make a sudoku
-    #[arg(short = 'm', long = "make", action = clap::ArgAction::SetTrue)]
-    make: Option<bool>,
-}
-
 fn main() {
-    let args = Cli::parse();
     let mut s = Sudoku::new();
 
-    if args.make.is_some() {
+    let args = command!()
+        .arg(
+            arg!(-f --file <FILE> "Filename to load")
+                .required(false)
+                .value_parser(value_parser!(PathBuf)),
+        )
+        .arg(
+            arg!(
+                -m --make ... "Make a new sudoku puzzle"
+            )
+            .action(ArgAction::SetTrue),
+        )
+        .arg(arg!(-b --brute ... "Brute Force Solver").action(ArgAction::SetTrue))
+        .get_matches();
+
+    if args.get_flag("make") {
         s.make();
         s.display();
-        s.display_possible();
-    } else {
-        if args.filename.is_some() {
-            let puzzle = load_ksudoku(args.filename.unwrap()).unwrap();
+        return;
+    }
 
-            // Ksudoku is stored TLD.
-            s.load_from_tld('b', '_', puzzle.as_str());
-            s.display();
-            s.display_possible();
+    if let Some(filename) = args.get_one::<PathBuf>("file") {
+        let puzzle = load_ksudoku(filename.to_path_buf()).unwrap();
+        // Ksudoku is stored TLD.
+        s.load_from_tld('b', '_', puzzle.as_str());
+        s.display();
+        s.display_possible();
 
+        if args.get_flag("brute") {
+            println!("Solutions: {}", s.bruteforce_solver());
+        } else {
             while s.solve() {
                 println!("Try it again...");
                 s.display();

+ 4 - 4
sudoku/src/group.rs

@@ -349,12 +349,12 @@ impl fmt::Debug for Group {
     }
 }
 
-pub fn for_column(i: u8) -> &'static Group {
-    &GROUP_COLUMN[i as usize]
+pub fn for_column(y: u8) -> &'static Group {
+    &GROUP_COLUMN[y as usize]
 }
 
-pub fn for_row(i: u8) -> &'static Group {
-    &GROUP_ROW[i as usize]
+pub fn for_row(x: u8) -> &'static Group {
+    &GROUP_ROW[x as usize]
 }
 
 pub fn for_cell(i: u8) -> &'static Group {

+ 74 - 8
sudoku/src/sudoku.rs

@@ -14,9 +14,9 @@ use bit_field::BitField;
 
 extern crate rand_chacha;
 use rand::prelude::*;
+use rand::seq::SliceRandom;
 use rand_chacha::rand_core::SeedableRng;
 use rand_chacha::ChaCha20Rng;
-use rand::seq::SliceRandom;
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub struct Possible(u16);
@@ -340,20 +340,86 @@ impl Sudoku {
         true
     }
 
+    fn calculate_possible(&mut self, solutions: &mut u16) -> bool {
+        let backup = Sudoku {
+            board: self.board,
+            possible: self.possible,
+        };
+
+        for idx in 0..MAX_SIZE {
+            if self.board[idx as usize] == 0 {
+                // Ok, there's a blank here
+                let (x,y) = xy(idx);
+                'outer:
+                for possible in 1..=9 {
+                    let mut g = for_row(x);
+                    for p in g.0 {
+                        if self.board[p as usize] == possible {
+                            continue 'outer;
+                        }
+                    }
+                    g = for_column(y);
+                    for p in g.0 {
+                        if self.board[p as usize] == possible {
+                            continue 'outer;
+                        }
+                    }
+                    // check cell
+                    let cell = which_cell(x,y);
+                    g = for_cell(cell);
+                    for p in g.0 {
+                        if self.board[p as usize] == possible {
+                            continue 'outer;
+                        }
+                    }
+
+                    // Ok, it could go here!
+                    self.board[idx as usize] = possible;
+                    if self.puzzle_complete() {
+                        *solutions += 1;
+                        break;
+                    } else {
+                        if self.calculate_possible(solutions) {
+                            return true;
+                        }
+                    }
+                }
+            }
+            self.board[idx as usize] = 0;
+        }
+
+        false
+    }
+
+    pub fn bruteforce_solver(&self) -> u16 {
+        let mut workset = Sudoku {
+            board: self.board,
+            possible: [Possible(0); MAX_SIZE as usize],
+        }; // self.possible };
+
+        let mut solutions :u16 = 0;
+        workset.calculate_possible(&mut solutions);
+        
+
+        // find number of solutions.
+        solutions
+    }
+
     pub fn make(&mut self) {
         let mut rng = ChaCha20Rng::from_entropy();
 
         self.fill_board(&mut rng);
     }
 
-    pub fn fill_board(&mut self, rng : &mut ChaCha20Rng ) -> bool {
-        let backup = Sudoku{ board: self.board,
-            possible: self.possible 
+    fn fill_board(&mut self, rng: &mut ChaCha20Rng) -> bool {
+        let backup = Sudoku {
+            board: self.board,
+            possible: self.possible,
         };
 
         for idx in 0..MAX_SIZE {
             if self.board[idx as usize] == 0 {
-                let (x,y) = xy(idx);
+                let (x, y) = xy(idx);
                 let mut available: [u8; WIDTH as usize] = [0; WIDTH as usize];
                 let mut total_available: u8 = 0;
 
@@ -375,7 +441,7 @@ impl Sudoku {
                 available[0..total_available as usize].shuffle(rng);
                 for v_idx in 0..total_available {
                     let value = available[v_idx as usize];
-                    self.set(x,y, value);
+                    self.set(x, y, value);
                     if self.fill_board(rng) {
                         return true;
                     }
@@ -422,8 +488,8 @@ impl Sudoku {
                 let value = pick_one(self, &mut rng, i);
                 if value.is_some() {
                     let value = value.unwrap();
-                println!("Set({},{})={}", x, y, value);
-                self.set(x, y, value);
+                    println!("Set({},{})={}", x, y, value);
+                    self.set(x, y, value);
                 }
             }
         }