use std::time::Instant;
use xcc::Matrix;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let arg = match std::env::args().nth(1) {
Some(path) => path,
None => {
println!("Usage: sudoku <path to sudoku file>");
println!("Example: sudoku examples/sudoku17.txt");
std::process::exit(1);
}
};
let input = std::fs::read_to_string(arg)?;
let mut count = 0;
let start_time = Instant::now();
for line in input.lines() {
if line.starts_with('#') {
continue;
}
count += 1;
match solve_sudoku(line) {
Ok(solution) => println!("{}", solution),
Err(msg) => println!("Error: {}", msg),
}
}
let elapsed = start_time.elapsed();
println!("Solved {} puzzles in {:?}", count, elapsed);
Ok(())
}
pub fn solve_sudoku(input: &str) -> Result<String, &'static str> {
let mut matrix = build_matrix(input);
match matrix.solve_unique() {
xcc::Unique::None => Err("No solution"),
xcc::Unique::One(solution) => {
let placements = solution.meanings(&matrix);
let mut grid = ['.'; 81];
for Placement { row, col, value } in placements {
grid[row * 9 + col] = char::from_digit(*value, 10).unwrap();
}
Ok(grid.iter().collect())
}
xcc::Unique::Ambiguous(_, _) => Err("Multiple solutions"),
}
}
fn build_matrix(input: &str) -> Matrix<Placement> {
let mut builder = Matrix::builder();
for row in 0..9 {
for col in 0..9 {
builder.add_primary_item(format!("F{}{}", row, col));
}
}
for row in 0..9 {
for value in 1..10 {
builder.add_primary_item(format!("R{}{}", row, value));
}
}
for col in 0..9 {
for value in 1..10 {
builder.add_primary_item(format!("C{}{}", col, value));
}
}
for box_row in 0..3 {
for box_col in 0..3 {
for value in 1..10 {
builder.add_primary_item(format!("B{}{}", box_row * 3 + box_col, value));
}
}
}
let chars = input.chars().collect::<Vec<_>>();
for (row, cells) in chars.chunks(9).enumerate() {
for (col, &c) in cells.iter().enumerate() {
let box_num = row / 3 * 3 + col / 3;
let values = if c == '.' {
vec![1, 2, 3, 4, 5, 6, 7, 8, 9]
} else {
let value = c.to_digit(10).expect("invalid digit");
vec![value]
};
for value in values {
builder.add_option(
Placement { row, col, value },
[
format!("F{}{}", row, col),
format!("R{}{}", row, value),
format!("C{}{}", col, value),
format!("B{}{}", box_num, value),
],
);
}
}
}
builder.build().expect("Failed to build matrix")
}
#[derive(Debug)]
struct Placement {
row: usize,
col: usize,
value: u32,
}