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, } 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!"); } } } }