puzzle-solver/tests/killersudoku.rs

116 lines
3.3 KiB
Rust
Raw Normal View History

//! 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<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<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());
}