121 lines
3.5 KiB
Rust
121 lines
3.5 KiB
Rust
//! Killer Sudoku.
|
|
//!
|
|
//! https://en.wikipedia.org/wiki/Killer_sudoku
|
|
|
|
extern crate puzzle_solver;
|
|
|
|
use puzzle_solver::{LinExpr, Puzzle, Solution, Val, VarToken};
|
|
|
|
const SQRT_SIZE: usize = 3;
|
|
const SIZE: usize = 9;
|
|
type Board = [[Val; SIZE]; SIZE];
|
|
type Point = (usize, usize);
|
|
|
|
fn make_killer_sudoku(board: &[(Val, Vec<Point>)]) -> (Puzzle, Vec<Vec<VarToken>>) {
|
|
let mut sys = Puzzle::new();
|
|
let vars = sys.new_vars_with_candidates_2d(SIZE, SIZE, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
|
|
for y in 0..SIZE {
|
|
sys.all_different(&vars[y]);
|
|
}
|
|
|
|
for x in 0..SIZE {
|
|
sys.all_different(vars.iter().map(|row| &row[x]));
|
|
}
|
|
|
|
for block in 0..SIZE {
|
|
let x0 = SQRT_SIZE * (block % SQRT_SIZE);
|
|
let y0 = SQRT_SIZE * (block / SQRT_SIZE);
|
|
sys.all_different((0..SIZE).map(|n| &vars[y0 + (n / SQRT_SIZE)][x0 + (n % SQRT_SIZE)]));
|
|
}
|
|
|
|
for &(total, ref points) in board.iter() {
|
|
sys.equals(
|
|
total,
|
|
points
|
|
.iter()
|
|
.fold(LinExpr::from(0), |sum, &(x, y)| sum + vars[y][x]),
|
|
);
|
|
}
|
|
|
|
(sys, vars)
|
|
}
|
|
|
|
fn print_sudoku(dict: &Solution, vars: &[Vec<VarToken>]) {
|
|
for y in 0..SIZE {
|
|
if y % SQRT_SIZE == 0 {
|
|
println!();
|
|
}
|
|
|
|
for x in 0..SIZE {
|
|
print!(
|
|
"{} {}",
|
|
if x % SQRT_SIZE == 0 { " " } else { "" },
|
|
dict[vars[y][x]]
|
|
);
|
|
}
|
|
println!();
|
|
}
|
|
}
|
|
|
|
fn verify_sudoku(dict: &Solution, vars: &[Vec<VarToken>], expected: &Board) {
|
|
for y in 0..SIZE {
|
|
for x in 0..SIZE {
|
|
assert_eq!(dict[vars[y][x]], expected[y][x]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn killersudoku_wikipedia() {
|
|
let puzzle = [
|
|
(3, vec![(0, 0), (1, 0)]),
|
|
(15, vec![(2, 0), (3, 0), (4, 0)]),
|
|
(22, vec![(5, 0), (4, 1), (5, 1), (4, 2)]),
|
|
(4, vec![(6, 0), (6, 1)]),
|
|
(16, vec![(7, 0), (7, 1)]),
|
|
(15, vec![(8, 0), (8, 1), (8, 2), (8, 3)]),
|
|
(25, vec![(0, 1), (1, 1), (0, 2), (1, 2)]),
|
|
(17, vec![(2, 1), (3, 1)]),
|
|
(9, vec![(2, 2), (3, 2), (3, 3)]),
|
|
(8, vec![(5, 2), (5, 3), (5, 4)]),
|
|
(20, vec![(6, 2), (7, 2), (6, 3)]),
|
|
(6, vec![(0, 3), (0, 4)]),
|
|
(14, vec![(1, 3), (2, 3)]),
|
|
(17, vec![(4, 3), (4, 4), (4, 5)]),
|
|
(17, vec![(7, 3), (6, 4), (7, 4)]),
|
|
(13, vec![(1, 4), (2, 4), (1, 5)]),
|
|
(20, vec![(3, 4), (3, 5), (3, 6)]),
|
|
(12, vec![(8, 4), (8, 5)]),
|
|
(27, vec![(0, 5), (0, 6), (0, 7), (0, 8)]),
|
|
(6, vec![(2, 5), (1, 6), (2, 6)]),
|
|
(20, vec![(5, 5), (5, 6), (6, 6)]),
|
|
(6, vec![(6, 5), (7, 5)]),
|
|
(10, vec![(4, 6), (3, 7), (4, 7), (3, 8)]),
|
|
(14, vec![(7, 6), (8, 6), (7, 7), (8, 7)]),
|
|
(8, vec![(1, 7), (1, 8)]),
|
|
(16, vec![(2, 7), (2, 8)]),
|
|
(15, vec![(5, 7), (6, 7)]),
|
|
(13, vec![(4, 8), (5, 8), (6, 8)]),
|
|
(17, vec![(7, 8), (8, 8)]),
|
|
];
|
|
|
|
let expected = [
|
|
[2, 1, 5, 6, 4, 7, 3, 9, 8],
|
|
[3, 6, 8, 9, 5, 2, 1, 7, 4],
|
|
[7, 9, 4, 3, 8, 1, 6, 5, 2],
|
|
[5, 8, 6, 2, 7, 4, 9, 3, 1],
|
|
[1, 4, 2, 5, 9, 3, 8, 6, 7],
|
|
[9, 7, 3, 8, 1, 6, 4, 2, 5],
|
|
[8, 2, 1, 7, 3, 9, 5, 4, 6],
|
|
[6, 5, 9, 4, 2, 8, 7, 1, 3],
|
|
[4, 3, 7, 1, 6, 5, 2, 8, 9],
|
|
];
|
|
|
|
let (mut sys, vars) = make_killer_sudoku(&puzzle);
|
|
let dict = sys.solve_any().expect("solution");
|
|
print_sudoku(&dict, &vars);
|
|
verify_sudoku(&dict, &vars, &expected);
|
|
println!("killersudoku_wikipedia: {} guesses", sys.num_guesses());
|
|
}
|