puzzle-solver/tests/takuzu.rs

281 lines
7.9 KiB
Rust

//! Takuzu (A.K.A. Binairo).
//!
//! https://en.wikipedia.org/wiki/Takuzu
extern crate puzzle_solver;
use puzzle_solver::*;
use std::iter;
use std::rc::Rc;
const X: Val = -1;
/*--------------------------------------------------------------*/
#[derive(Debug)]
struct BinaryRepr {
// value = sum_i 2^i bits[i]
value: VarToken,
bits: Vec<VarToken>,
}
impl Constraint for BinaryRepr {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item = &'a VarToken> + 'a> {
Box::new(iter::once(&self.value).chain(&self.bits))
}
fn propagate(&self, search: &mut PuzzleSearch, var: VarToken, val: Val) -> PsResult<()> {
if var == self.value {
let mut val = val;
for &var in self.bits.iter() {
search.set_candidate(var, val & 1)?;
val >>= 1;
}
} else if let Some(bitpos) = self.bits.iter().position(|&v| v == var) {
let bit = 1 << bitpos;
let discard = search
.get_unassigned(self.value)
.filter(|c| c & bit != val * bit)
.collect::<Vec<_>>();
for c in discard.into_iter() {
search.remove_candidate(self.value, c)?;
}
}
Ok(())
}
fn substitute(&self, _from: VarToken, _to: VarToken) -> PsResult<Rc<dyn Constraint>> {
unimplemented!();
}
}
/*--------------------------------------------------------------*/
fn make_sums(size: usize) -> Vec<Val> {
let mut vec = Vec::new();
for val in 0..(1 << size) {
let mut count = 0;
let mut v = val as usize;
while v > 0 {
count += v & 1;
v >>= 1;
}
if count == size / 2 {
vec.push(val);
}
}
vec
}
fn make_takuzu(puzzle: &[Vec<Val>]) -> (Puzzle, Vec<Vec<VarToken>>) {
let height = puzzle.len();
assert!(height > 0 && height % 2 == 0);
let width = puzzle[0].len();
assert!(width > 0 && width % 2 == 0);
let row_candidates = make_sums(height);
let col_candidates = make_sums(width);
let mut sys = Puzzle::new();
let vars = sys.new_vars_2d(width, height, &[0, 1]);
let row_values = sys.new_vars(height, row_candidates);
let col_values = sys.new_vars(width, col_candidates);
for y in 0..height {
let total = (height as Val) / 2;
sys.equals(
total,
vars[y].iter().fold(LinExpr::from(0), |sum, &x| sum + x),
);
sys.add_constraint(BinaryRepr {
value: row_values[y],
bits: vars[y].clone(),
});
}
for x in 0..width {
let total = (width as Val) / 2;
sys.equals(
total,
vars.iter().fold(LinExpr::from(0), |sum, row| sum + row[x]),
);
sys.add_constraint(BinaryRepr {
value: col_values[x],
bits: (0..height).map(|y| vars[y][x]).collect(),
});
}
// No three in a row, i.e. not: 000, 111.
for y in 0..height {
for window in vars[y].windows(3) {
let disjunction = sys.new_var(&[1, 2]);
sys.equals(window[0] + window[1] + window[2], disjunction);
}
}
for x in 0..width {
for y in 0..(height - 2) {
let disjunction = sys.new_var(&[1, 2]);
sys.equals(
vars[y + 0][x] + vars[y + 1][x] + vars[y + 2][x],
disjunction,
);
}
}
sys.all_different(&row_values);
sys.all_different(&col_values);
for y in 0..height {
for x in 0..width {
if puzzle[y][x] != X {
sys.set_value(vars[y][x], puzzle[y][x]);
}
}
}
(sys, vars)
}
fn print_takuzu(dict: &Solution, vars: &[Vec<VarToken>]) {
for row in vars.iter() {
let sum = row.iter().fold(0, |sum, &var| 2 * sum + dict[var]);
for &var in row.iter() {
print!("{}", dict[var]);
}
println!(" = {}", sum);
}
}
fn verify_takuzu(dict: &Solution, vars: &[Vec<VarToken>], expected: &[Val]) {
for (row, &expected) in vars.iter().zip(expected) {
let sum = row.iter().fold(0, |sum, &var| 2 * sum + dict[var]);
assert_eq!(sum, expected);
}
}
#[test]
fn takuzu_grid1() {
let puzzle = vec![
vec![X, 1, 0, X, X, X],
vec![1, X, X, X, 0, X],
vec![X, X, 0, X, X, X],
vec![1, 1, X, X, 1, 0],
vec![X, X, X, X, 0, X],
vec![X, X, X, X, X, X],
];
let (mut sys, vars) = make_takuzu(&puzzle);
let solutions = sys.solve_all();
assert_eq!(solutions.len(), 6);
print_takuzu(&solutions[0], &vars);
println!("takuzu_grid1: {} guesses", sys.num_guesses());
}
#[test]
fn takuzu_grid2() {
let puzzle = vec![
vec![0, X, X, X, X, 1, 1, X, X, 0, X, X],
vec![X, X, X, 1, X, X, X, 0, X, X, X, X],
vec![X, 0, X, X, X, X, 1, X, X, X, 0, 0],
vec![1, X, X, 1, X, X, 1, 1, X, X, X, 1],
vec![X, X, X, X, X, X, X, X, X, 1, X, X],
vec![0, X, 0, X, X, X, 1, X, X, X, X, X],
vec![X, X, X, X, 0, X, X, X, X, X, X, X],
vec![X, X, X, X, 0, 1, X, 0, X, X, X, X],
vec![X, X, 0, 0, X, X, 0, X, 0, X, X, 0],
vec![X, X, X, X, X, 1, X, X, X, X, 1, X],
vec![1, 0, X, 0, X, X, X, X, X, X, X, X],
vec![X, X, 1, X, X, X, X, 1, X, X, 0, 0],
];
let expected = [
0b_0101_0110_1001,
0b_0101_0100_1011,
0b_1010_1011_0100,
0b_1001_0011_0011,
0b_0110_1100_1100,
0b_0100_1011_0011,
0b_1011_0010_1010,
0b_0011_0100_1101,
0b_1100_1001_0110,
0b_0101_0110_1010,
0b_1010_1001_0101,
0b_1010_1101_0100,
];
let (mut sys, vars) = make_takuzu(&puzzle);
let dict = sys.solve_unique().expect("solution");
print_takuzu(&dict, &vars);
verify_takuzu(&dict, &vars, &expected);
println!("takuzu_grid2: {} guesses", sys.num_guesses());
}
#[test]
fn takuzu_grid3() {
let puzzle = vec![
vec![X, X, X, 0, X, 0, X, X, X, X, 0, X],
vec![1, X, X, X, X, X, X, 1, X, X, X, 1],
vec![X, X, 1, 1, X, X, X, X, X, X, 0, X],
vec![X, 0, X, X, X, X, X, X, X, X, X, 0],
vec![X, X, X, 0, X, X, 1, 1, 0, X, X, X],
vec![0, X, 0, 0, X, 0, X, 1, X, X, 0, X],
vec![X, X, X, X, X, X, 0, X, X, X, 0, X],
vec![1, X, 1, X, 0, X, X, X, X, X, X, X],
vec![X, X, X, X, X, X, 1, 0, 1, X, 0, X],
vec![X, 1, X, X, 0, X, X, X, X, 0, 0, X],
vec![X, X, X, 1, X, X, X, 0, X, X, X, X],
vec![X, X, X, X, X, 1, 1, X, X, 1, X, X],
];
let expected = [
0b_1010_1001_1001,
0b_1100_1001_0011,
0b_0011_0110_1100,
0b_1011_0100_1010,
0b_0100_1011_0011,
0b_0100_1011_0101,
0b_1011_0100_1100,
0b_1011_0101_0010,
0b_0100_1010_1101,
0b_0110_0101_1001,
0b_1001_1010_0110,
0b_0101_0110_0110,
];
let (mut sys, vars) = make_takuzu(&puzzle);
let dict = sys.solve_unique().expect("solution");
print_takuzu(&dict, &vars);
verify_takuzu(&dict, &vars, &expected);
println!("takuzu_grid3: {} guesses", sys.num_guesses());
}
#[test]
fn takuzu_grid4() {
let puzzle = vec![
vec![X, X, X, X, X, 1, 1, X, X, 0, X, X],
vec![X, X, X, 1, X, X, X, 0, X, X, X, X],
vec![X, 0, X, X, X, X, 1, X, X, X, 0, 0],
vec![X, X, X, 1, X, X, 1, 1, X, X, X, 1],
vec![X, X, X, X, X, X, X, X, X, 1, X, X],
vec![X, X, 0, X, X, X, 1, X, X, X, X, X],
vec![X, X, X, X, 0, X, X, X, X, X, X, X],
vec![X, X, X, X, 0, 1, X, 0, X, X, X, X],
vec![X, X, 0, 0, X, X, 0, X, 0, X, X, 0],
vec![X, X, X, X, X, 1, X, X, X, X, 1, X],
vec![X, X, X, 0, X, X, X, X, X, X, X, X],
vec![X, X, 1, X, X, X, X, 1, X, X, 0, 0],
];
let (mut sys, vars) = make_takuzu(&puzzle);
let dict = sys.solve_any().expect("solution");
print_takuzu(&dict, &vars);
println!("takuzu_grid4: {} guesses", sys.num_guesses());
}