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).
This commit is contained in:
parent
b22c6466ca
commit
2f20aa4086
266
tests/takuzu.rs
Normal file
266
tests/takuzu.rs
Normal file
@ -0,0 +1,266 @@
|
||||
//! 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());
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user