puzzle-solver/tests/kakuro.rs
David Wang c93b9430c1 Add test: Kakuro.
kakuro_wikipedia: 9 guesses.
2017-03-04 08:57:52 +11:00

106 lines
3.3 KiB
Rust

//! Kakuro.
//!
//! https://en.wikipedia.org/wiki/Kakuro
extern crate puzzle_solver;
use puzzle_solver::{LinExpr,Puzzle,Solution,Val,VarToken};
const SIZE: usize = 7;
const X: Val = 0;
type Board = [[Val; SIZE]; SIZE];
type KakuroVars = Vec<Vec<Option<VarToken>>>;
enum Rule {
H{ total: Val, y: usize, x1: usize, x2: usize },
V{ total: Val, x: usize, y1: usize, y2: usize },
}
fn make_kakuro(board: &[Rule]) -> (Puzzle, KakuroVars) {
let mut sys = Puzzle::new();
let mut vars = vec![vec![None; SIZE]; SIZE];
for rule in board.iter() {
let (total, x1, y1, x2, y2) = match rule {
&Rule::H{total, y, x1, x2} => (total, x1, y, x2, y),
&Rule::V{total, x, y1, y2} => (total, x, y1, x, y2),
};
let mut vec = Vec::new();
for y in y1..(y2 + 1) {
for x in x1..(x2 + 1) {
let var = vars[y][x].unwrap_or_else(|| {
let var = sys.new_var_with_candidates(&[1,2,3,4,5,6,7,8,9]);
vars[y][x] = Some(var);
var
});
vec.push(var);
}
}
sys.all_different(&vec);
sys.equals(total, vec.iter().fold(LinExpr::from(0), |sum, &var| sum + var));
}
(sys, vars)
}
fn print_kakuro(dict: &Solution, vars: &KakuroVars) {
for y in 0..SIZE {
for x in 0..SIZE {
if let Some(var) = vars[y][x] {
print!(" {}", dict[var]);
} else {
print!(" .");
}
}
println!();
}
}
fn verify_kakuro(dict: &Solution, vars: &KakuroVars, expected: &Board) {
for y in 0..SIZE {
for x in 0..SIZE {
let val = vars[y][x].map_or(X, |var| dict[var]);
assert_eq!(val, expected[y][x]);
}
}
}
#[test]
fn kakuro_wikipedia() {
let puzzle = [
Rule::H{ total:16, y:0, x1:0, x2:1 }, Rule::H{ total:24, y:0, x1:4, x2:6},
Rule::H{ total:17, y:1, x1:0, x2:1 }, Rule::H{ total:29, y:1, x1:3, x2:6},
Rule::H{ total:35, y:2, x1:0, x2:4 },
Rule::H{ total: 7, y:3, x1:1, x2:2 }, Rule::H{ total: 8, y:3, x1:4, x2:5},
Rule::H{ total:16, y:4, x1:2, x2:6 },
Rule::H{ total:21, y:5, x1:0, x2:3 }, Rule::H{ total: 5, y:5, x1:5, x2:6},
Rule::H{ total: 6, y:6, x1:0, x2:2 }, Rule::H{ total: 3, y:6, x1:5, x2:6},
Rule::V{ total:23, x:0, y1:0, y2:2 }, Rule::V{ total:11, x:0, y1:5, y2:6},
Rule::V{ total:30, x:1, y1:0, y2:3 }, Rule::V{ total:10, x:1, y1:5, y2:6},
Rule::V{ total:15, x:2, y1:2, y2:6 },
Rule::V{ total:17, x:3, y1:1, y2:2 }, Rule::V{ total: 7, x:3, y1:4, y2:5},
Rule::V{ total:27, x:4, y1:0, y2:4 },
Rule::V{ total:12, x:5, y1:0, y2:1 }, Rule::V{ total:12, x:5, y1:3, y2:6},
Rule::V{ total:16, x:6, y1:0, y2:1 }, Rule::V{ total: 7, x:6, y1:4, y2:6},
];
let expected = [
[ 9, 7, X, X, 8, 7, 9 ],
[ 8, 9, X, 8, 9, 5, 7 ],
[ 6, 8, 5, 9, 7, X, X ],
[ X, 6, 1, X, 2, 6, X ],
[ X, X, 4, 6, 1, 3, 2 ],
[ 8, 9, 3, 1, X, 1, 4 ],
[ 3, 1, 2, X, X, 2, 1 ] ];
let (mut sys, vars) = make_kakuro(&puzzle);
let dict = sys.solve_any().expect("solution");
print_kakuro(&dict, &vars);
verify_kakuro(&dict, &vars, &expected);
println!("kakuro_wikipedia: {} guesses", sys.num_guesses());
}