You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
6.8 KiB
256 lines
6.8 KiB
|
|
mod board;
|
|
mod solver;
|
|
mod mysolver;
|
|
mod hecht;
|
|
mod utils;
|
|
|
|
use hecht::HechtSolver;
|
|
use board::Board;
|
|
use solver::{Solver, Generator, SodokuComplexity};
|
|
use clap::{Parser, Subcommand};
|
|
|
|
static FIELDS: [[u8;81];10] =
|
|
[
|
|
[
|
|
0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0
|
|
],
|
|
[
|
|
7,0,6,3,0,8,0,0,9,
|
|
0,0,0,2,6,0,3,0,0,
|
|
0,5,0,0,0,0,0,8,0,
|
|
0,0,0,9,0,1,8,0,2,
|
|
4,0,0,0,0,0,0,0,6,
|
|
9,0,3,8,0,6,0,0,0,
|
|
0,9,0,0,0,0,0,7,0,
|
|
0,0,5,0,2,4,0,0,0,
|
|
6,0,0,1,0,3,2,0,4
|
|
],
|
|
[
|
|
0,0,4,1,0,0,3,0,0,
|
|
0,7,0,0,0,8,0,0,1,
|
|
0,0,0,0,0,0,7,0,9,
|
|
8,0,3,0,5,9,0,0,2,
|
|
0,0,9,6,0,1,5,0,0,
|
|
1,0,0,8,2,0,9,0,4,
|
|
6,0,1,0,0,0,0,0,0,
|
|
4,0,0,3,0,0,0,9,0,
|
|
0,0,8,0,0,4,2,0,0
|
|
],
|
|
[
|
|
0,0,0,0,2,3,4,0,8,
|
|
0,0,0,0,7,4,6,9,0,
|
|
0,0,0,8,9,0,0,0,7,
|
|
0,0,9,3,0,0,7,4,0,
|
|
0,7,0,0,4,0,0,3,0,
|
|
0,8,3,0,0,7,2,0,0,
|
|
5,0,0,0,1,9,0,0,0,
|
|
0,9,2,4,3,0,0,0,0,
|
|
3,0,6,7,5,0,0,0,0
|
|
],
|
|
[
|
|
0,0,0,0,6,0,0,3,0,
|
|
0,9,6,0,0,5,0,0,4,
|
|
0,0,0,7,0,1,0,6,0,
|
|
0,1,0,0,0,3,0,9,5,
|
|
0,4,9,0,1,0,7,2,0,
|
|
7,3,0,5,0,0,0,1,0,
|
|
0,6,0,9,0,7,0,0,0,
|
|
4,0,0,8,0,0,9,5,0,
|
|
0,2,0,0,5,0,0,0,0
|
|
],
|
|
[
|
|
9,0,6,1,0,7,3,0,4,
|
|
0,8,3,0,0,5,0,0,7,
|
|
0,0,0,0,0,0,2,0,9,
|
|
8,4,5,3,7,1,9,2,6,
|
|
3,0,2,5,6,8,0,0,1,
|
|
1,0,7,9,2,4,0,3,0,
|
|
6,0,9,4,3,2,7,0,8,
|
|
0,3,0,0,1,6,5,0,0,
|
|
2,1,8,7,5,0,0,0,3
|
|
],
|
|
[
|
|
0,8,0,0,0,0,0,0,0,
|
|
0,0,0,6,3,0,0,4,1,
|
|
0,0,0,5,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,9,0,
|
|
0,0,0,0,0,0,0,5,0,
|
|
9,0,0,0,0,0,0,0,0,
|
|
0,0,0,8,0,2,0,0,0,
|
|
0,5,0,0,9,0,0,0,0,
|
|
0,0,0,4,5,0,9,0,0
|
|
],
|
|
[
|
|
0,0,0,0,0,7,0,2,0,
|
|
0,0,4,0,0,0,0,3,0,
|
|
0,1,0,0,9,0,0,0,5,
|
|
0,0,0,0,0,6,3,0,0,
|
|
6,0,0,0,0,0,1,7,0,
|
|
0,7,3,4,0,0,0,0,2,
|
|
0,0,0,0,6,0,0,9,0,
|
|
3,0,7,0,8,0,5,0,0,
|
|
0,5,1,0,0,0,0,0,0
|
|
],
|
|
[
|
|
0,0,0,0,0,0,0,1,0,
|
|
0,3,0,0,0,4,0,0,0,
|
|
0,0,0,0,0,0,3,9,0,
|
|
0,4,0,8,0,2,0,0,0,
|
|
5,0,0,0,0,0,0,0,0,
|
|
0,0,9,0,6,0,2,5,0,
|
|
8,0,0,0,3,0,9,0,0,
|
|
9,0,0,4,0,0,0,8,0,
|
|
1,0,0,6,0,9,0,0,5
|
|
],
|
|
[ // Cannot be solved!
|
|
1,7,3,0,0,0,0,4,0,
|
|
0,0,0,0,0,0,9,0,5,
|
|
0,5,9,6,7,0,0,0,0,
|
|
0,2,0,0,8,0,0,0,7,
|
|
0,0,1,0,0,0,8,2,6,
|
|
7,0,0,0,0,0,0,0,1,
|
|
0,0,0,1,0,3,0,0,0,
|
|
0,0,0,2,5,0,0,0,0,
|
|
0,4,0,0,0,0,0,8,3
|
|
]
|
|
];
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
// Note this useful idiom: importing names from outer (for mod tests) scope.
|
|
use super::*;
|
|
use rstest::*;
|
|
|
|
#[rstest]
|
|
#[case(0, true)]
|
|
#[case(1, true)]
|
|
#[case(2, true)]
|
|
#[case(3, true)]
|
|
#[case(4, true)]
|
|
#[case(5, true)]
|
|
#[case(6, true)]
|
|
#[case(7, true)]
|
|
#[case(8, true)]
|
|
#[case(9, false)]
|
|
fn solve_sudoku(#[case] field : usize, #[case] valid : bool) {
|
|
let pg = Board::from_array(&FIELDS[field]);
|
|
let solver = HechtSolver::new();
|
|
let result = solver.solve(&pg);
|
|
|
|
assert!(result.is_some() == valid);
|
|
if valid {
|
|
let result_pg = result.unwrap();
|
|
assert!(result_pg.is_valid());
|
|
assert!(result_pg.is_solved());
|
|
|
|
assert!(result_pg.contains(&pg));
|
|
}
|
|
}
|
|
|
|
#[rstest]
|
|
#[case(0, true)]
|
|
#[case(1, false)]
|
|
#[case(2, false)]
|
|
#[case(3, false)]
|
|
#[case(4, false)]
|
|
#[case(5, false)]
|
|
#[case(6, true)]
|
|
#[case(7, false)]
|
|
#[case(8, false)]
|
|
fn uniqueness_test(#[case] field : usize, #[case] multi : bool) {
|
|
let pg = Board::from_array(&FIELDS[field]);
|
|
let solver = HechtSolver::new();
|
|
let result = solver.is_unique(&pg);
|
|
|
|
assert!(result != multi)
|
|
}
|
|
|
|
#[test]
|
|
fn generator_test_creates_valid_boards() {
|
|
let solver = HechtSolver::new();
|
|
let board = solver.generate(SodokuComplexity::Hard);
|
|
assert!(board.is_valid());
|
|
}
|
|
|
|
#[test]
|
|
fn generator_test_creates_not_solved_boards() {
|
|
let solver = HechtSolver::new();
|
|
let board = solver.generate(SodokuComplexity::Hard);
|
|
assert!(!board.is_solved());
|
|
}
|
|
}
|
|
|
|
#[derive(Subcommand, Debug)]
|
|
enum Commands {
|
|
// Simply let the solver solve the sudoku
|
|
Solve,
|
|
// Let the solver determine if there are multiple solutions
|
|
Unique,
|
|
// Instead of solving ... generate a sudoku
|
|
Generate,
|
|
}
|
|
|
|
/// Sudoku solver program
|
|
#[derive(Parser, Debug)]
|
|
#[command(author, version, about, long_about = None)]
|
|
struct Args {
|
|
/// Sudoku index number to use
|
|
#[arg(short, long, default_value_t = 1,value_parser = clap::value_parser!(u8).range(0..FIELDS.len() as i64))]
|
|
scenario: u8,
|
|
|
|
#[command(subcommand)]
|
|
command: Option<Commands>,
|
|
}
|
|
|
|
fn main() {
|
|
|
|
let args = Args::parse();
|
|
|
|
let pg = Board::from_array(&FIELDS[args.scenario as usize]);
|
|
|
|
pg.print();
|
|
|
|
let solver = HechtSolver::new();
|
|
|
|
// solver.test(&pg);
|
|
|
|
match &args.command {
|
|
Some(Commands::Unique) => {
|
|
if solver.is_unique(&pg) {
|
|
println!("Solver states, that this sudoku has only one solution!")
|
|
} else {
|
|
println!("Solver states, that this sudoku has multiple possible solutions!");
|
|
}
|
|
}
|
|
Some(Commands::Generate) => {
|
|
let board = solver.generate(SodokuComplexity::Hard);
|
|
board.print();
|
|
}
|
|
_ => {
|
|
if let Some(result) = solver.solve(&pg) {
|
|
if !result.contains(&pg) {
|
|
println!("Solver modified predefined fields!");
|
|
} if !result.is_valid() {
|
|
println!("Solver has not correctly solved the sudoku!");
|
|
} else if !result.is_solved() {
|
|
println!("Solver was not able to solve the sudoku!");
|
|
} else {
|
|
println!("Solver found a solution for this sudoku!!");
|
|
result.print();
|
|
}
|
|
} else {
|
|
println!("Solver was not able to solve the sodoku!");
|
|
}
|
|
}
|
|
}
|
|
}
|