sudoko/
lib.rs

1mod solver;
2mod strategies;
3mod sudoku;
4mod utils;
5
6#[cfg(feature = "wasm")]
7mod wasm;
8
9pub use solver::{Difficulty, SudokuSolver};
10pub use strategies::*;
11pub use sudoku::{Cell, Sudoku};
12
13#[cfg(feature = "wasm")]
14pub use wasm::*;
15
16use std::{fs, process};
17
18pub fn solve_puzzle(puzzle_str: &str, size_str: &str) {
19    let size = match size_str.parse::<usize>() {
20        Ok(s) => s,
21        Err(_) => {
22            eprintln!("Invalid size: {size_str}");
23            process::exit(1);
24        }
25    };
26
27    let puzzle = match Sudoku::from_string(puzzle_str, size) {
28        Ok(p) => p,
29        Err(e) => {
30            eprintln!("Error parsing puzzle: {e}");
31            process::exit(1);
32        }
33    };
34
35    println!("Original puzzle:");
36    println!("{puzzle}");
37
38    let mut solver = SudokuSolver::new();
39
40    match solver.solve_with_stats(puzzle) {
41        Ok((solution, stats)) => {
42            println!("Solution found!");
43            println!("{solution}");
44            println!("\nSolver Statistics:");
45            println!("Iterations: {}", stats.iterations);
46            println!("Cells filled: {}", stats.cells_filled);
47            println!("Backtrack steps: {}", stats.backtrack_steps);
48            println!("Strategies used:");
49            for (strategy, count) in stats.strategies_used {
50                println!("  {strategy}: {count}");
51            }
52        }
53        Err(e) => {
54            eprintln!("Failed to solve puzzle: {e}");
55            process::exit(1);
56        }
57    }
58}
59
60pub fn solve_from_file(file_path: &str, size_str: &str) {
61    let puzzle_str = match fs::read_to_string(file_path) {
62        Ok(content) => content.trim().to_string(),
63        Err(e) => {
64            eprintln!("Error reading file: {e}");
65            process::exit(1);
66        }
67    };
68
69    solve_puzzle(&puzzle_str, size_str);
70}
71
72pub fn generate_puzzle(size_str: &str, difficulty_str: &str) {
73    let size = match size_str.parse::<usize>() {
74        Ok(s) => s,
75        Err(_) => {
76            eprintln!("Invalid size: {size_str}");
77            process::exit(1);
78        }
79    };
80
81    let difficulty = match difficulty_str.to_lowercase().as_str() {
82        "easy" => Difficulty::Easy,
83        "medium" => Difficulty::Medium,
84        "hard" => Difficulty::Hard,
85        "expert" => Difficulty::Expert,
86        _ => {
87            eprintln!(
88                "Invalid difficulty: {difficulty_str}. Use easy, medium, hard, or expert"
89            );
90            process::exit(1);
91        }
92    };
93
94    let mut solver = SudokuSolver::new();
95
96    match solver.generate_puzzle(size, difficulty) {
97        Ok(puzzle) => {
98            println!("Generated {difficulty_str} puzzle ({size}x{size}):");
99            println!("{puzzle}");
100        }
101        Err(e) => {
102            eprintln!("Failed to generate puzzle: {e}");
103            process::exit(1);
104        }
105    }
106}
107
108pub fn validate_puzzle(puzzle_str: &str, size_str: &str) {
109    let size = match size_str.parse::<usize>() {
110        Ok(s) => s,
111        Err(_) => {
112            eprintln!("Invalid size: {size_str}");
113            process::exit(1);
114        }
115    };
116
117    let puzzle = match Sudoku::from_string(puzzle_str, size) {
118        Ok(p) => p,
119        Err(e) => {
120            eprintln!("Error parsing puzzle: {e}");
121            process::exit(1);
122        }
123    };
124
125    println!("Puzzle:");
126    println!("{puzzle}");
127
128    if puzzle.is_valid() {
129        println!("✓ Puzzle is valid!");
130
131        if puzzle.is_complete() {
132            println!("✓ Puzzle is complete and solved!");
133        } else {
134            println!("! Puzzle is valid but not yet complete.");
135        }
136    } else {
137        println!("✗ Puzzle is invalid!");
138
139        if !puzzle.is_valid_rows() {
140            println!("  - Invalid rows detected");
141        }
142        if !puzzle.is_valid_cols() {
143            println!("  - Invalid columns detected");
144        }
145        if !puzzle.is_valid_boxes() {
146            println!("  - Invalid boxes detected");
147        }
148    }
149}
150
151pub fn get_hint(puzzle_str: &str, size_str: &str) {
152    let size = match size_str.parse::<usize>() {
153        Ok(s) => s,
154        Err(_) => {
155            eprintln!("Invalid size: {size_str}");
156            process::exit(1);
157        }
158    };
159
160    let mut puzzle = match Sudoku::from_string(puzzle_str, size) {
161        Ok(p) => p,
162        Err(e) => {
163            eprintln!("Error parsing puzzle: {e}");
164            process::exit(1);
165        }
166    };
167
168    println!("Current puzzle:");
169    println!("{puzzle}");
170
171    let mut solver = SudokuSolver::new();
172
173    match solver.get_hint(&mut puzzle) {
174        Some((row, col, value)) => {
175            println!(
176                "Hint: Place {} at position ({}, {})",
177                value,
178                row + 1,
179                col + 1
180            );
181            puzzle.set(row, col, value).unwrap();
182            println!("\nPuzzle with hint applied:");
183            println!("{puzzle}");
184        }
185        None => {
186            println!("No obvious hint available. You might need to use more advanced techniques.");
187        }
188    }
189}