puzzle-solver/tests/takuzu.rs
David Wang 2f20aa4086 Add test: Takuzu (A.K.A. Binairo).
takuzu_grid1: 12 guesses.
takuzu_grid2: 0 guesses.
takuzu_grid3: 0 guesses.
takuzu_grid4: 20724 guesses (all solutions).
2017-03-21 08:44:26 +11:00

267 lines
7.5 KiB
Rust

//! Takuzu (A.K.A. Binairo).
//!
//! https://en.wikipedia.org/wiki/Takuzu
extern crate puzzle_solver;
use std::iter;
use std::rc::Rc;
use puzzle_solver::*;
const X: Val = -1;
/*--------------------------------------------------------------*/
struct BinaryRepr {
// value = sum_i 2^i bits[i]
value: VarToken,
bits: Vec<VarToken>,
}
impl Constraint for BinaryRepr {
fn vars<'a>(&'a self) -> Box<Iterator<Item=&'a VarToken> + 'a> {
Box::new(iter::once(&self.value).chain(&self.bits))
}
fn on_assigned(&self, search: &mut PuzzleSearch, var: VarToken, val: Val)
-> PsResult<()> {
if var == self.value {
let mut val = val;
for &var in self.bits.iter() {
try!(search.set_candidate(var, val & 1));
val = 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() {
try!(search.remove_candidate(self.value, c));
}
}
Ok(())
}
fn substitute(&self, _from: VarToken, _to: VarToken)
-> PsResult<Rc<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 = count + (v & 1);
v = v >> 1;
}
if count == size / 2 {
vec.push(val);
}
}
vec
}
fn make_takuzu(puzzle: &Vec<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_with_candidates_2d(width, height, &[0,1]);
let row_values = sys.new_vars_with_candidates_1d(height, &row_candidates);
let col_values = sys.new_vars_with_candidates_1d(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_with_candidates(&[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_with_candidates(&[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<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<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_010101101001,
0b_010101001011,
0b_101010110100,
0b_100100110011,
0b_011011001100,
0b_010010110011,
0b_101100101010,
0b_001101001101,
0b_110010010110,
0b_010101101010,
0b_101010010101,
0b_101011010100 ];
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_101010011001,
0b_110010010011,
0b_001101101100,
0b_101101001010,
0b_010010110011,
0b_010010110101,
0b_101101001100,
0b_101101010010,
0b_010010101101,
0b_011001011001,
0b_100110100110,
0b_010101100110 ];
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());
}