puzzle-solver/tests/killersudoku.rs

121 lines
3.5 KiB
Rust
Raw Normal View History

//! Killer Sudoku.
//!
//! https://en.wikipedia.org/wiki/Killer_sudoku
extern crate puzzle_solver;
2021-12-07 20:56:04 +04:00
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();
2021-12-10 13:20:36 +04:00
let vars = sys.new_vars_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);
2021-12-07 20:56:04 +04:00
sys.all_different((0..SIZE).map(|n| &vars[y0 + (n / SQRT_SIZE)][x0 + (n % SQRT_SIZE)]));
}
for &(total, ref points) in board.iter() {
2021-12-07 20:56:04 +04:00
sys.equals(
total,
points
.iter()
.fold(LinExpr::from(0), |sum, &(x, y)| sum + vars[y][x]),
);
}
(sys, vars)
}
2021-12-08 11:17:32 +04:00
fn print_sudoku(dict: &Solution, vars: &[Vec<VarToken>]) {
for y in 0..SIZE {
if y % SQRT_SIZE == 0 {
println!();
}
for x in 0..SIZE {
2021-12-07 20:56:04 +04:00
print!(
2021-12-08 11:17:32 +04:00
"{} {}",
2021-12-07 20:56:04 +04:00
if x % SQRT_SIZE == 0 { " " } else { "" },
dict[vars[y][x]]
);
}
println!();
}
}
2021-12-08 11:17:32 +04:00
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 = [
2021-12-07 20:56:04 +04:00
(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 = [
2021-12-07 20:56:04 +04:00
[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());
}