This commit is contained in:
Andrey Tkachenko 2021-12-07 20:56:04 +04:00
parent 74ea9c497f
commit da22e1dfbb
20 changed files with 951 additions and 583 deletions

View File

@ -3,7 +3,7 @@
use std::collections::HashMap;
use std::rc::Rc;
use crate::{Constraint,PsResult,PuzzleSearch,Val,VarToken};
use crate::{Constraint, PsResult, PuzzleSearch, Val, VarToken};
pub struct AllDifferent {
vars: Vec<VarToken>,
@ -22,7 +22,9 @@ impl AllDifferent {
/// puzzle_solver::constraint::AllDifferent::new(&vars);
/// ```
pub fn new<'a, I>(vars: I) -> Self
where I: IntoIterator<Item=&'a VarToken> {
where
I: IntoIterator<Item = &'a VarToken>,
{
AllDifferent {
vars: vars.into_iter().cloned().collect(),
}
@ -30,12 +32,11 @@ impl AllDifferent {
}
impl Constraint for AllDifferent {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item=&'a VarToken> + 'a> {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item = &'a VarToken> + 'a> {
Box::new(self.vars.iter())
}
fn on_assigned(&self, search: &mut PuzzleSearch, var: VarToken, val: Val)
-> PsResult<()> {
fn on_assigned(&self, search: &mut PuzzleSearch, var: VarToken, val: Val) -> PsResult<()> {
for &var2 in self.vars.iter().filter(|&v| *v != var) {
r#try!(search.remove_candidate(var2, val));
}
@ -75,13 +76,12 @@ impl Constraint for AllDifferent {
Ok(())
}
fn substitute(&self, from: VarToken, to: VarToken)
-> PsResult<Rc<dyn Constraint>> {
fn substitute(&self, from: VarToken, to: VarToken) -> PsResult<Rc<dyn Constraint>> {
if let Some(idx) = self.vars.iter().position(|&var| var == from) {
if !self.vars.contains(&to) {
let mut new_vars = self.vars.clone();
new_vars[idx] = to;
return Ok(Rc::new(AllDifferent{ vars: new_vars }));
return Ok(Rc::new(AllDifferent { vars: new_vars }));
}
}
@ -91,16 +91,16 @@ impl Constraint for AllDifferent {
#[cfg(test)]
mod tests {
use crate::{Puzzle,Val};
use crate::{Puzzle, Val};
#[test]
fn test_contradiction() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1]);
let v1 = puzzle.new_var_with_candidates(&[1]);
let v2 = puzzle.new_var_with_candidates(&[1,2,3]);
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3]);
puzzle.all_different(&[v0,v1,v2]);
puzzle.all_different(&[v0, v1, v2]);
let solution = puzzle.solve_any();
assert!(solution.is_none());
@ -110,25 +110,25 @@ mod tests {
fn test_elimination() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1]);
let v1 = puzzle.new_var_with_candidates(&[1,2,3]);
let v2 = puzzle.new_var_with_candidates(&[1,2,3]);
let v1 = puzzle.new_var_with_candidates(&[1, 2, 3]);
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3]);
puzzle.all_different(&[v0,v1,v2]);
puzzle.all_different(&[v0, v1, v2]);
let search = puzzle.step().expect("contradiction");
assert_eq!(search[v0], 1);
assert_eq!(search.get_unassigned(v1).collect::<Vec<Val>>(), &[2,3]);
assert_eq!(search.get_unassigned(v2).collect::<Vec<Val>>(), &[2,3]);
assert_eq!(search.get_unassigned(v1).collect::<Vec<Val>>(), &[2, 3]);
assert_eq!(search.get_unassigned(v2).collect::<Vec<Val>>(), &[2, 3]);
}
#[test]
fn test_contradiction_by_length() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1,2]);
let v1 = puzzle.new_var_with_candidates(&[1,2]);
let v2 = puzzle.new_var_with_candidates(&[1,2]);
let v0 = puzzle.new_var_with_candidates(&[1, 2]);
let v1 = puzzle.new_var_with_candidates(&[1, 2]);
let v2 = puzzle.new_var_with_candidates(&[1, 2]);
puzzle.all_different(&[v0,v1,v2]);
puzzle.all_different(&[v0, v1, v2]);
let search = puzzle.step();
assert!(search.is_none());
@ -137,15 +137,15 @@ mod tests {
#[test]
fn test_constrain_by_value() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1,2]);
let v1 = puzzle.new_var_with_candidates(&[1,2]);
let v2 = puzzle.new_var_with_candidates(&[1,2,3]);
let v0 = puzzle.new_var_with_candidates(&[1, 2]);
let v1 = puzzle.new_var_with_candidates(&[1, 2]);
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3]);
puzzle.all_different(&[v0,v1,v2]);
puzzle.all_different(&[v0, v1, v2]);
let search = puzzle.step().expect("contradiction");
assert_eq!(search.get_unassigned(v0).collect::<Vec<Val>>(), &[1,2]);
assert_eq!(search.get_unassigned(v1).collect::<Vec<Val>>(), &[1,2]);
assert_eq!(search.get_unassigned(v0).collect::<Vec<Val>>(), &[1, 2]);
assert_eq!(search.get_unassigned(v1).collect::<Vec<Val>>(), &[1, 2]);
assert_eq!(search[v2], 3);
}
}

View File

@ -1,10 +1,10 @@
//! Equality implementation.
use std::rc::Rc;
use num_rational::Ratio;
use num_traits::Zero;
use std::rc::Rc;
use crate::{Constraint,LinExpr,PsResult,PuzzleSearch,Val,VarToken};
use crate::{Constraint, LinExpr, PsResult, PuzzleSearch, Val, VarToken};
pub struct Equality {
// The equation: 0 = constant + coef1 * var1 + coef2 * var2 + ...
@ -25,19 +25,16 @@ impl Equality {
/// vars[0][0] + vars[0][1] + vars[0][2] - 15);
/// ```
pub fn new(eqn: LinExpr) -> Self {
Equality {
eqn: eqn,
}
Equality { eqn: eqn }
}
}
impl Constraint for Equality {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item=&'a VarToken> + 'a> {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item = &'a VarToken> + 'a> {
Box::new(self.eqn.coef.keys())
}
fn on_assigned(&self, search: &mut PuzzleSearch, _: VarToken, _: Val)
-> PsResult<()> {
fn on_assigned(&self, search: &mut PuzzleSearch, _: VarToken, _: Val) -> PsResult<()> {
let mut sum = self.eqn.constant;
let mut unassigned_var = None;
@ -108,16 +105,24 @@ impl Constraint for Equality {
let (min_bnd, max_bnd);
if coef > Ratio::zero() {
min_bnd = ((coef * Ratio::from_integer(max_val) - sum_max) / coef).ceil().to_integer();
max_bnd = ((coef * Ratio::from_integer(min_val) - sum_min) / coef).floor().to_integer();
min_bnd = ((coef * Ratio::from_integer(max_val) - sum_max) / coef)
.ceil()
.to_integer();
max_bnd = ((coef * Ratio::from_integer(min_val) - sum_min) / coef)
.floor()
.to_integer();
} else {
min_bnd = ((coef * Ratio::from_integer(max_val) - sum_min) / coef).ceil().to_integer();
max_bnd = ((coef * Ratio::from_integer(min_val) - sum_max) / coef).floor().to_integer();
min_bnd = ((coef * Ratio::from_integer(max_val) - sum_min) / coef)
.ceil()
.to_integer();
max_bnd = ((coef * Ratio::from_integer(min_val) - sum_max) / coef)
.floor()
.to_integer();
}
if min_val < min_bnd || max_bnd < max_val {
let (new_min, new_max)
= r#try!(search.bound_candidate_range(var, min_bnd, max_bnd));
let (new_min, new_max) =
r#try!(search.bound_candidate_range(var, min_bnd, max_bnd));
if coef > Ratio::zero() {
sum_min = sum_min + coef * Ratio::from_integer(new_min - min_val);
@ -134,26 +139,25 @@ impl Constraint for Equality {
Ok(())
}
fn substitute(&self, from: VarToken, to: VarToken)
-> PsResult<Rc<dyn Constraint>> {
fn substitute(&self, from: VarToken, to: VarToken) -> PsResult<Rc<dyn Constraint>> {
let mut eqn = self.eqn.clone();
if let Some(coef) = eqn.coef.remove(&from) {
eqn = eqn + coef * to;
}
Ok(Rc::new(Equality{ eqn: eqn }))
Ok(Rc::new(Equality { eqn: eqn }))
}
}
#[cfg(test)]
mod tests {
use crate::{Puzzle,Val};
use crate::{Puzzle, Val};
#[test]
fn test_contradiction() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[3]);
let v1 = puzzle.new_var_with_candidates(&[0,1]);
let v1 = puzzle.new_var_with_candidates(&[0, 1]);
puzzle.equals(v0 + 2 * v1, 4);
@ -165,7 +169,7 @@ mod tests {
fn test_assign() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1]);
let v1 = puzzle.new_var_with_candidates(&[1,2,3]);
let v1 = puzzle.new_var_with_candidates(&[1, 2, 3]);
puzzle.equals(v0 + v1, 4);
@ -177,13 +181,13 @@ mod tests {
#[test]
fn test_reduce_range() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1,2,3]);
let v1 = puzzle.new_var_with_candidates(&[3,4,5]);
let v0 = puzzle.new_var_with_candidates(&[1, 2, 3]);
let v1 = puzzle.new_var_with_candidates(&[3, 4, 5]);
puzzle.equals(v0 + v1, 5);
let search = puzzle.step().expect("contradiction");
assert_eq!(search.get_unassigned(v0).collect::<Vec<Val>>(), &[1,2]);
assert_eq!(search.get_unassigned(v1).collect::<Vec<Val>>(), &[3,4]);
assert_eq!(search.get_unassigned(v0).collect::<Vec<Val>>(), &[1, 2]);
assert_eq!(search.get_unassigned(v1).collect::<Vec<Val>>(), &[3, 4]);
}
}

View File

@ -7,16 +7,15 @@
use std::rc::Rc;
use crate::{PsResult,PuzzleSearch,Val,VarToken};
use crate::{PsResult, PuzzleSearch, Val, VarToken};
/// Constraint trait.
pub trait Constraint {
/// An iterator over the variables that are involved in the constraint.
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item=&'a VarToken> + 'a>;
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item = &'a VarToken> + 'a>;
/// Applied after a variable has been assigned.
fn on_assigned(&self, _search: &mut PuzzleSearch, _var: VarToken, _val: Val)
-> PsResult<()> {
fn on_assigned(&self, _search: &mut PuzzleSearch, _var: VarToken, _val: Val) -> PsResult<()> {
Ok(())
}
@ -29,8 +28,7 @@ pub trait Constraint {
///
/// Returns a new constraint with all instances of "from" replaced
/// with "to", or Err if a contradiction was found.
fn substitute(&self, from: VarToken, to: VarToken)
-> PsResult<Rc<dyn Constraint>>;
fn substitute(&self, from: VarToken, to: VarToken) -> PsResult<Rc<dyn Constraint>>;
}
pub use self::alldifferent::AllDifferent;

View File

@ -3,7 +3,7 @@
use std::iter;
use std::rc::Rc;
use crate::{Constraint,PsResult,PuzzleSearch,VarToken};
use crate::{Constraint, PsResult, PuzzleSearch, VarToken};
pub struct Unify {
var1: VarToken,
@ -33,7 +33,7 @@ impl Unify {
}
impl Constraint for Unify {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item=&'a VarToken> + 'a> {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item = &'a VarToken> + 'a> {
if self.var1 != self.var2 {
Box::new(iter::once(&self.var1).chain(iter::once(&self.var2)))
} else {
@ -49,11 +49,13 @@ impl Constraint for Unify {
}
}
fn substitute(&self, from: VarToken, to: VarToken)
-> PsResult<Rc<dyn Constraint>> {
fn substitute(&self, from: VarToken, to: VarToken) -> PsResult<Rc<dyn Constraint>> {
let var1 = if self.var1 == from { to } else { self.var1 };
let var2 = if self.var2 == from { to } else { self.var2 };
Ok(Rc::new(Unify{ var1: var1, var2: var2 }))
Ok(Rc::new(Unify {
var1: var1,
var2: var2,
}))
}
}
@ -64,8 +66,8 @@ mod tests {
#[test]
fn test_unify_alldifferent() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1,2]);
let v1 = puzzle.new_var_with_candidates(&[1,2]);
let v0 = puzzle.new_var_with_candidates(&[1, 2]);
let v1 = puzzle.new_var_with_candidates(&[1, 2]);
puzzle.all_different(&[v0, v1]);
puzzle.unify(v0, v1);
@ -77,9 +79,9 @@ mod tests {
#[test]
fn test_unify_equality() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1,2,3,4]);
let v1 = puzzle.new_var_with_candidates(&[1,2,3,4]);
let v2 = puzzle.new_var_with_candidates(&[1,2,3,4]);
let v0 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
let v1 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
puzzle.equals(v0 + 2 * v1 + v2, 6);
puzzle.unify(v0, v1);
@ -94,8 +96,8 @@ mod tests {
fn test_unify_unify() {
let mut puzzle = Puzzle::new();
let v0 = puzzle.new_var_with_candidates(&[1]);
let v1 = puzzle.new_var_with_candidates(&[1,2,3,4]);
let v2 = puzzle.new_var_with_candidates(&[1,2,3,4]);
let v1 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
puzzle.unify(v0, v1);
puzzle.unify(v1, v2);

View File

@ -5,16 +5,16 @@ extern crate bit_set;
extern crate num_rational;
extern crate num_traits;
use num_rational::Rational32;
use std::collections::HashMap;
use std::ops;
use num_rational::Rational32;
pub use crate::constraint::Constraint;
pub use crate::puzzle::Puzzle;
pub use crate::puzzle::PuzzleSearch;
/// A puzzle variable token.
#[derive(Copy,Clone,Debug,Eq,Hash,PartialEq)]
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct VarToken(usize);
/// The type of a puzzle variable's value (i.e. the candidate type).

View File

@ -1,25 +1,29 @@
//! Linear expressions.
use std::collections::HashMap;
use num_rational::{Ratio, Rational32};
use num_traits::{One, Zero};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::convert::From;
use std::ops::{Add,Mul,Neg,Sub};
use num_rational::{Ratio,Rational32};
use num_traits::{One,Zero};
use std::ops::{Add, Mul, Neg, Sub};
use crate::{Coef,LinExpr,VarToken};
use crate::{Coef, LinExpr, VarToken};
macro_rules! impl_commutative_op {
($LHS:ident + $RHS:ident) => {
impl Add<$RHS> for $LHS {
type Output = LinExpr;
fn add(self, rhs: $RHS) -> Self::Output { rhs + self }
fn add(self, rhs: $RHS) -> Self::Output {
rhs + self
}
}
};
($LHS:ident * $RHS:ident) => {
impl Mul<$RHS> for $LHS {
type Output = LinExpr;
fn mul(self, rhs: $RHS) -> Self::Output { rhs * self }
fn mul(self, rhs: $RHS) -> Self::Output {
rhs * self
}
}
};
}
@ -28,9 +32,11 @@ macro_rules! impl_subtract_op {
($LHS:ident - $RHS:ident) => {
impl Sub<$RHS> for $LHS {
type Output = LinExpr;
fn sub(self, rhs: $RHS) -> Self::Output { self + (-rhs) }
fn sub(self, rhs: $RHS) -> Self::Output {
self + (-rhs)
}
}
}
};
}
pub trait IntoCoef: Zero {
@ -38,11 +44,15 @@ pub trait IntoCoef: Zero {
}
impl IntoCoef for i32 {
fn into_coef(self) -> Coef { Ratio::from_integer(self) }
fn into_coef(self) -> Coef {
Ratio::from_integer(self)
}
}
impl IntoCoef for Rational32 {
fn into_coef(self) -> Coef { self }
fn into_coef(self) -> Coef {
self
}
}
/*--------------------------------------------------------------*/
@ -196,7 +206,7 @@ impl Add for LinExpr {
match self.coef.entry(x2) {
Entry::Vacant(e) => {
e.insert(a2);
},
}
Entry::Occupied(mut e) => {
let new_coef = *e.get() + a2;
if new_coef.is_zero() {
@ -204,7 +214,7 @@ impl Add for LinExpr {
} else {
*e.get_mut() = new_coef;
}
},
}
}
}
@ -218,8 +228,8 @@ impl_subtract_op!(LinExpr - LinExpr);
#[cfg(test)]
mod tests {
use num_rational::Ratio;
use crate::Puzzle;
use num_rational::Ratio;
#[test]
fn test_ops() {

View File

@ -1,5 +1,6 @@
//! The puzzle's state and rules.
use bit_set::BitSet;
use std::cell::Cell;
use std::collections::BTreeSet;
use std::fmt;
@ -7,21 +8,20 @@ use std::iter;
use std::mem;
use std::ops;
use std::rc::Rc;
use bit_set::BitSet;
use crate::{Constraint,LinExpr,PsResult,Solution,Val,VarToken};
use crate::constraint;
use crate::{Constraint, LinExpr, PsResult, Solution, Val, VarToken};
/// A collection of candidates.
#[derive(Clone,Debug,Eq,PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq)]
enum Candidates {
None, // A variable with no candidates.
Value(Val), // A variable set to its initial value.
Set(Rc<BTreeSet<Val>>), // A variable with a list of candidates.
None, // A variable with no candidates.
Value(Val), // A variable set to its initial value.
Set(Rc<BTreeSet<Val>>), // A variable with a list of candidates.
}
/// The state of a variable during the solution search.
#[derive(Clone,Debug)]
#[derive(Clone, Debug)]
enum VarState {
Assigned(Val),
Unassigned(Candidates),
@ -78,7 +78,7 @@ impl Candidates {
}
/// Get an iterator over all of the candidates of a variable.
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item=Val> + 'a> {
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = Val> + 'a> {
match self {
&Candidates::None => Box::new(iter::empty()),
&Candidates::Value(val) => Box::new(iter::once(val)),
@ -146,8 +146,7 @@ impl Puzzle {
/// let mut send_more_money = puzzle_solver::Puzzle::new();
/// send_more_money.new_vars_with_candidates_1d(8, &[0,1,2,3,4,5,6,7,8,9]);
/// ```
pub fn new_vars_with_candidates_1d(&mut self, n: usize, candidates: &[Val])
-> Vec<VarToken> {
pub fn new_vars_with_candidates_1d(&mut self, n: usize, candidates: &[Val]) -> Vec<VarToken> {
let mut vars = Vec::with_capacity(n);
for _ in 0..n {
vars.push(self.new_var_with_candidates(candidates));
@ -164,9 +163,12 @@ impl Puzzle {
/// let mut magic_square = puzzle_solver::Puzzle::new();
/// magic_square.new_vars_with_candidates_2d(3, 3, &[1,2,3,4,5,6,7,8,9]);
/// ```
pub fn new_vars_with_candidates_2d(self: &mut Puzzle,
width: usize, height: usize, candidates: &[Val])
-> Vec<Vec<VarToken>> {
pub fn new_vars_with_candidates_2d(
self: &mut Puzzle,
width: usize,
height: usize,
candidates: &[Val],
) -> Vec<Vec<VarToken>> {
let mut vars = Vec::with_capacity(height);
for _ in 0..height {
vars.push(self.new_vars_with_candidates_1d(width, candidates));
@ -216,12 +218,11 @@ impl Puzzle {
let VarToken(idx) = var;
match &self.candidates[idx] {
&Candidates::Value(_) =>
panic!("attempt to set fixed variable"),
&Candidates::Value(_) => panic!("attempt to set fixed variable"),
&Candidates::None => {
self.candidates[idx] = Candidates::Set(Rc::new(BTreeSet::new()));
},
}
&Candidates::Set(_) => (),
}
@ -250,8 +251,7 @@ impl Puzzle {
let VarToken(idx) = var;
match self.candidates[idx] {
Candidates::Value(_) =>
panic!("attempt to set fixed variable"),
Candidates::Value(_) => panic!("attempt to set fixed variable"),
Candidates::None => (),
@ -260,7 +260,7 @@ impl Puzzle {
for c in candidates.iter() {
cs.remove(c);
}
},
}
}
}
@ -281,8 +281,7 @@ impl Puzzle {
let VarToken(idx) = var;
match self.candidates[idx] {
Candidates::Value(_) =>
panic!("attempt to set fixed variable"),
Candidates::Value(_) => panic!("attempt to set fixed variable"),
Candidates::None => (),
@ -291,13 +290,15 @@ impl Puzzle {
let mut set = BTreeSet::new();
set.extend(candidates);
*cs = cs.intersection(&set).cloned().collect();
},
}
}
}
/// Add a constraint to the puzzle solution.
pub fn add_constraint<T>(&mut self, constraint: T)
where T: Constraint + 'static {
where
T: Constraint + 'static,
{
self.constraints.push(Rc::new(constraint));
}
@ -313,7 +314,9 @@ impl Puzzle {
/// send_more_money.all_different(&vars);
/// ```
pub fn all_different<'a, I>(&mut self, vars: I)
where I: IntoIterator<Item=&'a VarToken> {
where
I: IntoIterator<Item = &'a VarToken>,
{
self.add_constraint(constraint::AllDifferent::new(vars));
}
@ -328,8 +331,11 @@ impl Puzzle {
///
/// magic_square.equals(vars[0][0] + vars[0][1] + vars[0][2], 15);
/// ```
pub fn equals<L,R>(&mut self, lhs: L, rhs: R)
where L: Into<LinExpr>, R: Into<LinExpr> {
pub fn equals<L, R>(&mut self, lhs: L, rhs: R)
where
L: Into<LinExpr>,
R: Into<LinExpr>,
{
self.add_constraint(constraint::Equality::new(lhs.into() - rhs.into()));
}
@ -479,8 +485,7 @@ impl PuzzleConstraints {
}
/// Determine which variables wake up which constraints.
fn init_wake(constraints: &Vec<Rc<dyn Constraint>>, num_vars: usize)
-> Vec<BitSet> {
fn init_wake(constraints: &Vec<Rc<dyn Constraint>>, num_vars: usize) -> Vec<BitSet> {
let mut wake = vec![BitSet::new(); num_vars];
for cidx in 0..constraints.len() {
for &VarToken(idx) in constraints[cidx].vars() {
@ -498,8 +503,11 @@ impl<'a> PuzzleSearch<'a> {
/// Allocate a new puzzle searcher.
fn new(puzzle: &'a Puzzle) -> Self {
let constraints = PuzzleConstraints::new(puzzle);
let vars = puzzle.candidates.iter().map(|cs|
VarState::Unassigned(cs.clone())).collect();
let vars = puzzle
.candidates
.iter()
.map(|cs| VarState::Unassigned(cs.clone()))
.collect();
let mut wake = BitSet::new();
for cidx in 0..constraints.constraints.len() {
@ -538,8 +546,7 @@ impl<'a> PuzzleSearch<'a> {
}
/// Get an iterator over the candidates to an unassigned variable.
pub fn get_unassigned(&'a self, var: VarToken)
-> Box<dyn Iterator<Item=Val> + 'a> {
pub fn get_unassigned(&'a self, var: VarToken) -> Box<dyn Iterator<Item = Val> + 'a> {
let VarToken(idx) = var;
match &self.vars[idx] {
&VarState::Assigned(_) => Box::new(iter::empty()),
@ -556,19 +563,21 @@ impl<'a> PuzzleSearch<'a> {
&VarState::Unassigned(ref cs) => match cs {
&Candidates::None => Err(()),
&Candidates::Value(val) => Ok((val, val)),
&Candidates::Set(ref rc) => {
rc.iter().cloned().min().into_iter()
.zip(rc.iter().cloned().max()).next()
.ok_or(())
}
&Candidates::Set(ref rc) => rc
.iter()
.cloned()
.min()
.into_iter()
.zip(rc.iter().cloned().max())
.next()
.ok_or(()),
},
&VarState::Unified(other) => self.get_min_max(other),
}
}
/// Set a variable to a value.
pub fn set_candidate(&mut self, var: VarToken, val: Val)
-> PsResult<()> {
pub fn set_candidate(&mut self, var: VarToken, val: Val) -> PsResult<()> {
let VarToken(idx) = var;
match &self.vars[idx] {
@ -583,8 +592,7 @@ impl<'a> PuzzleSearch<'a> {
if let &VarState::Unified(other) = &self.vars[idx] {
self.set_candidate(other, val)
} else if let &mut VarState::Unassigned(Candidates::Set(ref mut rc))
= &mut self.vars[idx] {
} else if let &mut VarState::Unassigned(Candidates::Set(ref mut rc)) = &mut self.vars[idx] {
if rc.contains(&val) {
let mut set = Rc::make_mut(rc);
set.clear();
@ -600,8 +608,7 @@ impl<'a> PuzzleSearch<'a> {
}
/// Remove a single candidate from a variable.
pub fn remove_candidate(&mut self, var: VarToken, val: Val)
-> PsResult<()> {
pub fn remove_candidate(&mut self, var: VarToken, val: Val) -> PsResult<()> {
let VarToken(idx) = var;
match &self.vars[idx] {
@ -616,8 +623,7 @@ impl<'a> PuzzleSearch<'a> {
if let &VarState::Unified(other) = &self.vars[idx] {
self.remove_candidate(other, val)
} else if let &mut VarState::Unassigned(Candidates::Set(ref mut rc))
= &mut self.vars[idx] {
} else if let &mut VarState::Unassigned(Candidates::Set(ref mut rc)) = &mut self.vars[idx] {
if rc.contains(&val) {
let mut set = Rc::make_mut(rc);
set.remove(&val);
@ -630,25 +636,31 @@ impl<'a> PuzzleSearch<'a> {
}
/// Bound an variable to the given range.
pub fn bound_candidate_range(&mut self, var: VarToken, min: Val, max: Val)
-> PsResult<(Val, Val)> {
pub fn bound_candidate_range(
&mut self,
var: VarToken,
min: Val,
max: Val,
) -> PsResult<(Val, Val)> {
let VarToken(idx) = var;
match &self.vars[idx] {
&VarState::Assigned(v) =>
&VarState::Assigned(v) => {
if min <= v && v <= max {
return Ok((v, v))
return Ok((v, v));
} else {
return Err(())
},
return Err(());
}
}
&VarState::Unassigned(ref cs) => match cs {
&Candidates::None => return Err(()),
&Candidates::Value(v) =>
&Candidates::Value(v) => {
if min <= v && v <= max {
return Ok((v, v))
return Ok((v, v));
} else {
return Err(())
},
return Err(());
}
}
&Candidates::Set(_) => (),
},
&VarState::Unified(_) => (),
@ -656,22 +668,26 @@ impl<'a> PuzzleSearch<'a> {
if let &VarState::Unified(other) = &self.vars[idx] {
self.bound_candidate_range(other, min, max)
} else if let &mut VarState::Unassigned(Candidates::Set(ref mut rc))
= &mut self.vars[idx] {
} else if let &mut VarState::Unassigned(Candidates::Set(ref mut rc)) = &mut self.vars[idx] {
let &curr_min = rc.iter().min().expect("candidates");
let &curr_max = rc.iter().max().expect("candidates");
if curr_min < min || max < curr_max {
{
let mut set = Rc::make_mut(rc);
*set = set.iter()
*set = set
.iter()
.filter(|&val| min <= *val && *val <= max)
.cloned()
.collect();
self.wake.union_with(&self.constraints.wake[idx]);
}
rc.iter().cloned().min().into_iter()
.zip(rc.iter().cloned().max()).next()
rc.iter()
.cloned()
.min()
.into_iter()
.zip(rc.iter().cloned().max())
.next()
.ok_or(())
} else {
Ok((curr_min, curr_max))
@ -687,11 +703,14 @@ impl<'a> PuzzleSearch<'a> {
return;
}
let next_unassigned = self.vars.iter().enumerate().min_by_key(
|&(_, vs)| match vs {
&VarState::Unassigned(ref cs) => cs.len(),
_ => ::std::usize::MAX,
});
let next_unassigned = self
.vars
.iter()
.enumerate()
.min_by_key(|&(_, vs)| match vs {
&VarState::Unassigned(ref cs) => cs.len(),
_ => ::std::usize::MAX,
});
if let Some((idx, &VarState::Unassigned(ref cs))) = next_unassigned {
if cs.len() == 0 {
@ -716,9 +735,10 @@ impl<'a> PuzzleSearch<'a> {
}
} else {
// No unassigned variables remaining.
let vars = (0..self.puzzle.num_vars).map(|idx|
self[VarToken(idx)]).collect();
solutions.push(Solution{ vars: vars });
let vars = (0..self.puzzle.num_vars)
.map(|idx| self[VarToken(idx)])
.collect();
solutions.push(Solution { vars: vars });
}
}
@ -784,8 +804,7 @@ impl<'a> PuzzleSearch<'a> {
}
/// Unify the "from" variable with the "to" variable.
pub fn unify(&mut self, from: VarToken, to: VarToken)
-> PsResult<()> {
pub fn unify(&mut self, from: VarToken, to: VarToken) -> PsResult<()> {
if from == to {
return Ok(());
}
@ -796,19 +815,19 @@ impl<'a> PuzzleSearch<'a> {
let VarToken(search) = from;
let VarToken(replace) = to;
match (&self.vars[search], &self.vars[replace]) {
(&VarState::Assigned(val1), &VarState::Assigned(val2)) =>
return bool_to_result(val1 == val2),
(&VarState::Assigned(val1), &VarState::Assigned(val2)) => {
return bool_to_result(val1 == val2)
}
// Unified variables should have been substituted out previously.
(&VarState::Unified(_), _) | (_, &VarState::Unified(_)) =>
panic!("internal representation corrupt"),
(&VarState::Unified(_), _) | (_, &VarState::Unified(_)) => {
panic!("internal representation corrupt")
}
(&VarState::Unassigned(_), &VarState::Assigned(_)) =>
(to, from),
(&VarState::Unassigned(_), &VarState::Assigned(_)) => (to, from),
(&VarState::Assigned(_), &VarState::Unassigned(_))
| (&VarState::Unassigned(_), &VarState::Unassigned(_)) =>
(from, to),
| (&VarState::Unassigned(_), &VarState::Unassigned(_)) => (from, to),
}
};
@ -825,9 +844,11 @@ impl<'a> PuzzleSearch<'a> {
if let &VarState::Assigned(val) = &self.vars[search] {
r#try!(self.set_candidate(to, val));
} else {
if let (&mut VarState::Unassigned(Candidates::Set(ref mut rc1)),
&mut VarState::Unassigned(Candidates::Set(ref mut rc2)))
= get_two_mut(&mut self.vars, search, replace) {
if let (
&mut VarState::Unassigned(Candidates::Set(ref mut rc1)),
&mut VarState::Unassigned(Candidates::Set(ref mut rc2)),
) = get_two_mut(&mut self.vars, search, replace)
{
*rc2 = Rc::new(rc2.intersection(rc1).cloned().collect());
if rc2.is_empty() {
return Err(());
@ -848,16 +869,16 @@ impl<'a> fmt::Debug for PuzzleSearch<'a> {
match var {
&VarState::Assigned(val) => {
write!(f, " var {}: {}", idx, val)?;
},
}
&VarState::Unassigned(ref cs) => {
write!(f, " var {}:", idx)?;
for val in cs.iter() {
write!(f, " {}", val)?;
}
},
}
&VarState::Unified(VarToken(other)) => {
write!(f, " var {}: var {}", idx, other)?;
},
}
}
}
write!(f, "}}")?;
@ -891,8 +912,7 @@ fn bool_to_result(cond: bool) -> PsResult<()> {
}
}
fn get_two_mut<'a, T>(slice: &'a mut [T], a: usize, b: usize)
-> (&'a mut T, &'a mut T) {
fn get_two_mut<'a, T>(slice: &'a mut [T], a: usize, b: usize) -> (&'a mut T, &'a mut T) {
assert!(a != b);
if a < b {
let (mut l, mut r) = slice.split_at_mut(b);

View File

@ -4,7 +4,7 @@
extern crate puzzle_solver;
use puzzle_solver::{Puzzle,Solution,Val,VarToken};
use puzzle_solver::{Puzzle, Solution, Val, VarToken};
const WIDTH: usize = 8;
const HEIGHT: usize = 8;
@ -40,9 +40,15 @@ fn make_hidato(board: &Board) -> (Puzzle, Vec<VarToken>) {
let stride = WIDTH as Val;
let deltas = [
-stride - 1, -stride, -stride + 1,
-1, 1,
stride - 1, stride, stride + 1 ];
-stride - 1,
-stride,
-stride + 1,
-1,
1,
stride - 1,
stride,
stride + 1,
];
for i in 1..vars.len() {
let step = sys.new_var_with_candidates(&deltas);
@ -84,24 +90,26 @@ fn verify_hidato(dict: &Solution, vars: &Vec<VarToken>, expected: &Board) {
#[test]
fn hidato_wikipedia() {
let puzzle = [
[ 0, 33, 35, 0, 0, NA, NA, NA ],
[ 0, 0, 24, 22, 0, NA, NA, NA ],
[ 0, 0, 0, 21, 0, 0, NA, NA ],
[ 0, 26, 0, 13, 40, 11, NA, NA ],
[ 27, 0, 0, 0, 9, 0, 1, NA ],
[ NA, NA, 0, 0, 18, 0, 0, NA ],
[ NA, NA, NA, NA, 0, 7, 0, 0 ],
[ NA, NA, NA, NA, NA, NA, 5, 0 ] ];
[0, 33, 35, 0, 0, NA, NA, NA],
[0, 0, 24, 22, 0, NA, NA, NA],
[0, 0, 0, 21, 0, 0, NA, NA],
[0, 26, 0, 13, 40, 11, NA, NA],
[27, 0, 0, 0, 9, 0, 1, NA],
[NA, NA, 0, 0, 18, 0, 0, NA],
[NA, NA, NA, NA, 0, 7, 0, 0],
[NA, NA, NA, NA, NA, NA, 5, 0],
];
let expected = [
[ 32, 33, 35, 36, 37, NA, NA, NA ],
[ 31, 34, 24, 22, 38, NA, NA, NA ],
[ 30, 25, 23, 21, 12, 39, NA, NA ],
[ 29, 26, 20, 13, 40, 11, NA, NA ],
[ 27, 28, 14, 19, 9, 10, 1, NA ],
[ NA, NA, 15, 16, 18, 8, 2, NA ],
[ NA, NA, NA, NA, 17, 7, 6, 3 ],
[ NA, NA, NA, NA, NA, NA, 5, 4 ] ];
[32, 33, 35, 36, 37, NA, NA, NA],
[31, 34, 24, 22, 38, NA, NA, NA],
[30, 25, 23, 21, 12, 39, NA, NA],
[29, 26, 20, 13, 40, 11, NA, NA],
[27, 28, 14, 19, 9, 10, 1, NA],
[NA, NA, 15, 16, 18, 8, 2, NA],
[NA, NA, NA, NA, 17, 7, 6, 3],
[NA, NA, NA, NA, NA, NA, 5, 4],
];
let (mut sys, vars) = make_hidato(&puzzle);
let dict = sys.solve_any().expect("solution");

View File

@ -4,7 +4,7 @@
extern crate puzzle_solver;
use puzzle_solver::{LinExpr,Puzzle,Solution,Val,VarToken};
use puzzle_solver::{LinExpr, Puzzle, Solution, Val, VarToken};
const SIZE: usize = 7;
const X: Val = 0;
@ -12,8 +12,18 @@ 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 },
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) {
@ -22,15 +32,15 @@ fn make_kakuro(board: &[Rule]) -> (Puzzle, KakuroVars) {
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),
&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]);
let var = sys.new_var_with_candidates(&[1, 2, 3, 4, 5, 6, 7, 8, 9]);
vars[y][x] = Some(var);
var
});
@ -40,7 +50,10 @@ fn make_kakuro(board: &[Rule]) -> (Puzzle, KakuroVars) {
}
sys.all_different(&vec);
sys.equals(total, vec.iter().fold(LinExpr::from(0), |sum, &var| sum + var));
sys.equals(
total,
vec.iter().fold(LinExpr::from(0), |sum, &var| sum + var),
);
}
(sys, vars)
@ -71,31 +84,161 @@ fn verify_kakuro(dict: &Solution, vars: &KakuroVars, expected: &Board) {
#[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},
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 ] ];
[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");

View File

@ -4,7 +4,7 @@
extern crate puzzle_solver;
use puzzle_solver::{LinExpr,Puzzle,Solution,Val,VarToken};
use puzzle_solver::{LinExpr, Puzzle, Solution, Val, VarToken};
const SQRT_SIZE: usize = 3;
const SIZE: usize = 9;
@ -13,7 +13,7 @@ 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]);
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]);
@ -26,12 +26,16 @@ fn make_killer_sudoku(board: &[(Val, Vec<Point>)]) -> (Puzzle, Vec<Vec<VarToken>
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)]));
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.equals(
total,
points
.iter()
.fold(LinExpr::from(0), |sum, &(x, y)| sum + vars[y][x]),
);
}
(sys, vars)
@ -44,9 +48,11 @@ fn print_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
}
for x in 0..SIZE {
print!("{}{}",
if x % SQRT_SIZE == 0 { " " } else { "" },
dict[vars[y][x]]);
print!(
"{}{}",
if x % SQRT_SIZE == 0 { " " } else { "" },
dict[vars[y][x]]
);
}
println!();
}
@ -63,49 +69,48 @@ fn verify_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>, expected: &Board) {
#[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)]),
(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 ] ];
[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");

View File

@ -4,7 +4,7 @@
extern crate puzzle_solver;
use puzzle_solver::{LinExpr,Puzzle,Solution,Val,VarToken};
use puzzle_solver::{LinExpr, Puzzle, Solution, Val, VarToken};
fn make_magic_square(n: usize) -> (Puzzle, Vec<Vec<VarToken>>, VarToken) {
let mut sys = Puzzle::new();
@ -19,16 +19,28 @@ fn make_magic_square(n: usize) -> (Puzzle, Vec<Vec<VarToken>>, VarToken) {
sys.all_different(vars.iter().flat_map(|it| it));
for y in 0..n {
sys.equals(total, vars[y].iter().fold(LinExpr::from(0), |sum, &x| sum + x));
sys.equals(
total,
vars[y].iter().fold(LinExpr::from(0), |sum, &x| sum + x),
);
}
for x in 0..n {
sys.equals(total, vars.iter().fold(LinExpr::from(0), |sum, row| sum + row[x]));
sys.equals(
total,
vars.iter().fold(LinExpr::from(0), |sum, row| sum + row[x]),
);
}
{
sys.equals(total, (0..n).fold(LinExpr::from(0), |sum, i| sum + vars[i][i]));
sys.equals(total, (0..n).fold(LinExpr::from(0), |sum, i| sum + vars[i][n - i - 1]));
sys.equals(
total,
(0..n).fold(LinExpr::from(0), |sum, i| sum + vars[i][i]),
);
sys.equals(
total,
(0..n).fold(LinExpr::from(0), |sum, i| sum + vars[i][n - i - 1]),
);
}
// Sum of all digits = sum of all rows (columns) = total * n.

View File

@ -4,9 +4,9 @@
extern crate puzzle_solver;
use puzzle_solver::*;
use std::collections::HashMap;
use std::rc::Rc;
use puzzle_solver::*;
const WIDTH: usize = 20;
const HEIGHT: usize = 20;
@ -23,7 +23,7 @@ struct Nonogram {
rule: Vec<usize>,
}
#[derive(Clone,Copy)]
#[derive(Clone, Copy)]
enum AutoFillResult {
// Too many solutions, no need to find any more
SearchEnded,
@ -45,11 +45,14 @@ impl Nonogram {
}
}
fn autofill(&self,
trial: &mut Vec<Val>, pos: usize, rule_idx: usize,
accum: &mut Vec<Val>,
cache: &mut HashMap<(usize, usize), AutoFillResult>)
-> AutoFillResult {
fn autofill(
&self,
trial: &mut Vec<Val>,
pos: usize,
rule_idx: usize,
accum: &mut Vec<Val>,
cache: &mut HashMap<(usize, usize), AutoFillResult>,
) -> AutoFillResult {
assert!(pos <= trial.len() && rule_idx <= self.rule.len());
let key = (pos, rule_idx);
@ -65,8 +68,10 @@ impl Nonogram {
*a = *a | *t;
}
if accum.iter().all(|&t|
t < FLAG_UNKNOWN || t == FLAG_UNKNOWN | FLAG_ON | FLAG_OFF) {
if accum
.iter()
.all(|&t| t < FLAG_UNKNOWN || t == FLAG_UNKNOWN | FLAG_ON | FLAG_OFF)
{
return AutoFillResult::SearchEnded;
}
@ -75,9 +80,8 @@ impl Nonogram {
// Not enough space.
if rule_idx < self.rule.len() {
let required_space
= self.rule[rule_idx..].iter().sum::<usize>()
+ (self.rule.len() - rule_idx - 1);
let required_space =
self.rule[rule_idx..].iter().sum::<usize>() + (self.rule.len() - rule_idx - 1);
if pos + required_space > trial.len() {
return AutoFillResult::Conflict;
@ -93,16 +97,17 @@ impl Nonogram {
}
match self.autofill(trial, pos + 1, rule_idx, accum, cache) {
res@AutoFillResult::SearchEnded => return res,
res@AutoFillResult::SolutionFound => result = res,
res @ AutoFillResult::SearchEnded => return res,
res @ AutoFillResult::SolutionFound => result = res,
AutoFillResult::Conflict => (),
}
}
// Try filled.
if trial[pos] != FLAG_OFF
&& rule_idx < self.rule.len()
&& pos + self.rule[rule_idx] <= trial.len() {
&& rule_idx < self.rule.len()
&& pos + self.rule[rule_idx] <= trial.len()
{
let mut ok = true;
let mut pos = pos;
@ -127,8 +132,8 @@ impl Nonogram {
if ok {
match self.autofill(trial, pos, rule_idx + 1, accum, cache) {
res@AutoFillResult::SearchEnded => return res,
res@AutoFillResult::SolutionFound => result = res,
res @ AutoFillResult::SearchEnded => return res,
res @ AutoFillResult::SolutionFound => result = res,
AutoFillResult::Conflict => (),
}
}
@ -140,7 +145,7 @@ impl Nonogram {
}
impl Constraint for Nonogram {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item=&'a VarToken> + 'a> {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item = &'a VarToken> + 'a> {
Box::new(self.vars.iter())
}
@ -159,32 +164,31 @@ impl Constraint for Nonogram {
match self.autofill(&mut trial, 0, 0, &mut accum, &mut cache) {
AutoFillResult::Conflict => return Err(()),
AutoFillResult::SearchEnded => (),
AutoFillResult::SolutionFound =>
AutoFillResult::SolutionFound => {
for idx in 0..self.vars.len() {
if accum[idx] == FLAG_UNKNOWN | FLAG_OFF {
r#try!(search.set_candidate(self.vars[idx], 0));
} else if accum[idx] == FLAG_UNKNOWN | FLAG_ON {
r#try!(search.set_candidate(self.vars[idx], 1));
}
},
}
}
}
Ok(())
}
fn substitute(&self, _search: VarToken, _replace: VarToken)
-> PsResult<Rc<dyn Constraint>> {
fn substitute(&self, _search: VarToken, _replace: VarToken) -> PsResult<Rc<dyn Constraint>> {
unimplemented!();
}
}
/*--------------------------------------------------------------*/
fn make_nonogram(rows: &[Vec<usize>], cols: &[Vec<usize>])
-> (Puzzle, Vec<Vec<VarToken>>) {
fn make_nonogram(rows: &[Vec<usize>], cols: &[Vec<usize>]) -> (Puzzle, Vec<Vec<VarToken>>) {
let mut sys = Puzzle::new();
let (w, h) = (cols.len(), rows.len());
let vars = sys.new_vars_with_candidates_2d(w, h, &[0,1]);
let vars = sys.new_vars_with_candidates_2d(w, h, &[0, 1]);
for y in 0..h {
sys.add_constraint(Nonogram::new(&vars[y], &rows[y]));
@ -192,8 +196,9 @@ fn make_nonogram(rows: &[Vec<usize>], cols: &[Vec<usize>])
for x in 0..w {
sys.add_constraint(Nonogram::new(
&vars.iter().map(|row| row[x]).collect(),
&cols[x]));
&vars.iter().map(|row| row[x]).collect(),
&cols[x],
));
}
(sys, vars)
@ -221,73 +226,71 @@ fn nonogram_wikipedia() {
let puzzle_rows = [
vec![3],
vec![5],
vec![3,1],
vec![2,1],
vec![3,3,4],
vec![2,2,7],
vec![6,1,1],
vec![4,2,2],
vec![1,1],
vec![3,1],
vec![3, 1],
vec![2, 1],
vec![3, 3, 4],
vec![2, 2, 7],
vec![6, 1, 1],
vec![4, 2, 2],
vec![1, 1],
vec![3, 1],
vec![6],
vec![2,7],
vec![6,3,1],
vec![1,2,2,1,1],
vec![4,1,1,3],
vec![4,2,2],
vec![3,3,1],
vec![3,3],
vec![2, 7],
vec![6, 3, 1],
vec![1, 2, 2, 1, 1],
vec![4, 1, 1, 3],
vec![4, 2, 2],
vec![3, 3, 1],
vec![3, 3],
vec![3],
vec![2,1],
vec![2, 1],
];
let puzzle_cols = [
vec![2],
vec![1,2],
vec![2,3],
vec![2,3],
vec![3,1,1],
vec![2,1,1],
vec![1,1,1,2,2],
vec![1,1,3,1,3],
vec![2,6,4],
vec![3,3,9,1],
vec![5,3,2],
vec![3,1,2,2],
vec![2,1,7],
vec![3,3,2],
vec![2,4],
vec![2,1,2],
vec![2,2,1],
vec![2,2],
vec![1, 2],
vec![2, 3],
vec![2, 3],
vec![3, 1, 1],
vec![2, 1, 1],
vec![1, 1, 1, 2, 2],
vec![1, 1, 3, 1, 3],
vec![2, 6, 4],
vec![3, 3, 9, 1],
vec![5, 3, 2],
vec![3, 1, 2, 2],
vec![2, 1, 7],
vec![3, 3, 2],
vec![2, 4],
vec![2, 1, 2],
vec![2, 2, 1],
vec![2, 2],
vec![1],
vec![1],
];
let expected = [
[ 0,0,0,0,0, 0,0,0,0,0, 1,1,1,0,0, 0,0,0,0,0 ],
[ 0,0,0,0,0, 0,0,0,0,1, 1,1,1,1,0, 0,0,0,0,0 ],
[ 0,0,0,0,0, 0,0,0,0,1, 1,1,0,1,0, 0,0,0,0,0 ],
[ 0,0,0,0,0, 0,0,0,0,1, 1,0,0,1,0, 0,0,0,0,0 ],
[ 0,0,0,0,0, 0,1,1,1,0, 1,1,1,0,1, 1,1,1,0,0 ],
[ 0,0,0,0,1, 1,0,0,1,1, 0,0,0,1,1, 1,1,1,1,1 ],
[ 0,0,1,1,1, 1,1,1,0,1, 0,0,0,1,0, 0,0,0,0,0 ],
[ 0,1,1,1,1, 0,0,0,1,1, 0,0,1,1,0, 0,0,0,0,0 ],
[ 0,0,0,0,0, 0,0,0,1,0, 0,0,1,0,0, 0,0,0,0,0 ],
[ 0,0,0,0,0, 0,0,1,1,1, 0,0,1,0,0, 0,0,0,0,0 ],
[ 0,0,0,0,0, 0,0,1,1,1, 1,1,1,0,0, 0,0,0,0,0 ],
[ 0,1,1,0,0, 0,1,1,1,1, 1,1,1,0,0, 0,0,0,0,0 ],
[ 1,1,1,1,1, 1,0,0,1,1, 1,0,1,0,0, 0,0,0,0,0 ],
[ 1,0,1,1,0, 0,1,1,0,1, 0,0,1,0,0, 0,0,0,0,0 ],
[ 0,0,0,1,1, 1,1,0,0,1, 0,1,0,0,1, 1,1,0,0,0 ],
[ 0,0,0,0,0, 0,0,0,1,1, 1,1,0,1,1, 0,1,1,0,0 ],
[ 0,0,0,0,0, 0,0,0,1,1, 1,0,0,1,1, 1,0,1,0,0 ],
[ 0,0,0,0,0, 0,0,1,1,1, 0,0,0,0,1, 1,1,0,0,0 ],
[ 0,0,0,0,0, 0,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0 ],
[ 0,0,0,0,0, 0,1,1,0,1, 0,0,0,0,0, 0,0,0,0,0 ] ];
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
let (mut sys, vars) = make_nonogram(&puzzle_rows, &puzzle_cols);
let dict = sys.solve_unique().expect("solution");

View File

@ -4,21 +4,24 @@
extern crate puzzle_solver;
use std::rc::Rc;
use puzzle_solver::*;
use std::rc::Rc;
struct NoDiagonal {
vars: Vec<VarToken>,
}
impl Constraint for NoDiagonal {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item=&'a VarToken> + 'a> {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item = &'a VarToken> + 'a> {
Box::new(self.vars.iter())
}
fn on_assigned(&self, search: &mut PuzzleSearch, var: VarToken, val: Val)
-> PsResult<()> {
let y1 = self.vars.iter().position(|&v| v == var).expect("unreachable");
fn on_assigned(&self, search: &mut PuzzleSearch, var: VarToken, val: Val) -> PsResult<()> {
let y1 = self
.vars
.iter()
.position(|&v| v == var)
.expect("unreachable");
for (y2, &var2) in self.vars.iter().enumerate() {
if !search.is_assigned(var2) {
let x1 = val;
@ -31,8 +34,7 @@ impl Constraint for NoDiagonal {
Ok(())
}
fn substitute(&self, _from: VarToken, _to: VarToken)
-> PsResult<Rc<dyn Constraint>> {
fn substitute(&self, _from: VarToken, _to: VarToken) -> PsResult<Rc<dyn Constraint>> {
unimplemented!();
}
}
@ -43,7 +45,7 @@ fn make_queens(n: usize) -> (Puzzle, Vec<VarToken>) {
let vars = sys.new_vars_with_candidates_1d(n, &pos);
sys.all_different(&vars);
sys.add_constraint(NoDiagonal{ vars: vars.clone() });
sys.add_constraint(NoDiagonal { vars: vars.clone() });
(sys, vars)
}

View File

@ -5,7 +5,7 @@
extern crate puzzle_solver;
use puzzle_solver::{Puzzle,Solution,Val,VarToken};
use puzzle_solver::{Puzzle, Solution, Val, VarToken};
const SQRT_SIZE: usize = 3;
const SIZE: usize = 9;
@ -15,7 +15,7 @@ type SudokuVars = Vec<Vec<VarToken>>;
type SamuraiVars = (SudokuVars, SudokuVars, SudokuVars, SudokuVars, SudokuVars);
fn make_sudoku(sys: &mut Puzzle) -> SudokuVars {
let vars = sys.new_vars_with_candidates_2d(SIZE, SIZE, &[1,2,3,4,5,6,7,8,9]);
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]);
@ -28,15 +28,18 @@ fn make_sudoku(sys: &mut Puzzle) -> SudokuVars {
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)]));
sys.all_different((0..SIZE).map(|n| &vars[y0 + (n / SQRT_SIZE)][x0 + (n % SQRT_SIZE)]));
}
vars
}
fn make_samurai_sudoku(board: &Board) -> (Puzzle, SamuraiVars) {
let set = |sys: &mut Puzzle, var, val| if val != 0 { sys.set_value(var, val) };
let set = |sys: &mut Puzzle, var, val| {
if val != 0 {
sys.set_value(var, val)
}
};
let mut sys = Puzzle::new();
let tl = make_sudoku(&mut sys);
@ -47,10 +50,22 @@ fn make_samurai_sudoku(board: &Board) -> (Puzzle, SamuraiVars) {
for y in 0..SQRT_SIZE {
for x in 0..SQRT_SIZE {
sys.unify(mid[0 * SQRT_SIZE + y][0 * SQRT_SIZE + x], tl[2 * SQRT_SIZE + y][2 * SQRT_SIZE + x]);
sys.unify(mid[0 * SQRT_SIZE + y][2 * SQRT_SIZE + x], tr[2 * SQRT_SIZE + y][0 * SQRT_SIZE + x]);
sys.unify(mid[2 * SQRT_SIZE + y][0 * SQRT_SIZE + x], bl[0 * SQRT_SIZE + y][2 * SQRT_SIZE + x]);
sys.unify(mid[2 * SQRT_SIZE + y][2 * SQRT_SIZE + x], br[0 * SQRT_SIZE + y][0 * SQRT_SIZE + x]);
sys.unify(
mid[0 * SQRT_SIZE + y][0 * SQRT_SIZE + x],
tl[2 * SQRT_SIZE + y][2 * SQRT_SIZE + x],
);
sys.unify(
mid[0 * SQRT_SIZE + y][2 * SQRT_SIZE + x],
tr[2 * SQRT_SIZE + y][0 * SQRT_SIZE + x],
);
sys.unify(
mid[2 * SQRT_SIZE + y][0 * SQRT_SIZE + x],
bl[0 * SQRT_SIZE + y][2 * SQRT_SIZE + x],
);
sys.unify(
mid[2 * SQRT_SIZE + y][2 * SQRT_SIZE + x],
br[0 * SQRT_SIZE + y][0 * SQRT_SIZE + x],
);
}
}
@ -59,8 +74,16 @@ fn make_samurai_sudoku(board: &Board) -> (Puzzle, SamuraiVars) {
set(&mut sys, tl[y][x], board[y][x]);
set(&mut sys, tr[y][x], board[y][SIZE + SQRT_SIZE + x]);
set(&mut sys, bl[y][x], board[SIZE + SQRT_SIZE + y][x]);
set(&mut sys, br[y][x], board[SIZE + SQRT_SIZE + y][SIZE + SQRT_SIZE + x]);
set(&mut sys, mid[y][x], board[2 * SQRT_SIZE + y][2 * SQRT_SIZE + x]);
set(
&mut sys,
br[y][x],
board[SIZE + SQRT_SIZE + y][SIZE + SQRT_SIZE + x],
);
set(
&mut sys,
mid[y][x],
board[2 * SQRT_SIZE + y][2 * SQRT_SIZE + x],
);
}
}
@ -70,7 +93,11 @@ fn make_samurai_sudoku(board: &Board) -> (Puzzle, SamuraiVars) {
fn print_samurai_sudoku(dict: &Solution, vars: &SamuraiVars) {
let &(ref tl, ref tr, ref bl, ref br, ref mid) = vars;
let pr3 = |a: &[VarToken], j| print!(" {}{}{}", dict[a[j]], dict[a[j + 1]], dict[a[j + 2]]);
let pr9 = |a| { pr3(a, 0); pr3(a, 3); pr3(a, 6); };
let pr9 = |a| {
pr3(a, 0);
pr3(a, 3);
pr3(a, 6);
};
let gap = || print!(" ");
for i in 0..SIZE {
@ -110,8 +137,14 @@ fn verify_samurai_sudoku(dict: &Solution, vars: &SamuraiVars, expected: &Board)
assert_eq!(dict[tl[i][j]], expected[i][j]);
assert_eq!(dict[tr[i][j]], expected[i][SIZE + SQRT_SIZE + j]);
assert_eq!(dict[bl[i][j]], expected[SIZE + SQRT_SIZE + i][j]);
assert_eq!(dict[br[i][j]], expected[SIZE + SQRT_SIZE + i][SIZE + SQRT_SIZE + j]);
assert_eq!(dict[mid[i][j]], expected[2 * SQRT_SIZE + i][2 * SQRT_SIZE + j]);
assert_eq!(
dict[br[i][j]],
expected[SIZE + SQRT_SIZE + i][SIZE + SQRT_SIZE + j]
);
assert_eq!(
dict[mid[i][j]],
expected[2 * SQRT_SIZE + i][2 * SQRT_SIZE + j]
);
}
}
}
@ -119,62 +152,136 @@ fn verify_samurai_sudoku(dict: &Solution, vars: &SamuraiVars, expected: &Board)
#[test]
fn samuraisudoku_easy() {
let puzzle = [
[ 0,0,3, 0,0,0, 2,0,0, X,X,X, 0,0,6, 0,0,0, 2,0,0 ],
[ 0,2,0, 4,0,8, 0,3,0, X,X,X, 0,3,0, 4,0,2, 0,8,0 ],
[ 8,0,0, 0,9,0, 0,0,4, X,X,X, 8,0,0, 0,1,0, 0,0,4 ],
[ 0,5,0, 6,0,1, 0,2,0, X,X,X, 0,2,0, 1,0,7, 0,9,0 ],
[ 0,0,8, 0,0,0, 6,0,0, X,X,X, 0,0,9, 0,0,0, 8,0,0 ],
[ 0,7,0, 8,0,4, 0,1,0, X,X,X, 0,8,0, 5,0,9, 0,4,0 ],
[ 1,0,0, 0,7,0, 0,0,0, 0,0,0, 0,0,0, 0,7,0, 0,0,5 ],
[ 0,4,0, 1,0,2, 0,0,0, 0,0,0, 0,0,0, 2,0,8, 0,7,0 ],
[ 0,0,9, 0,0,0, 0,0,0, 0,6,0, 0,0,0, 0,0,0, 1,0,0 ],
[ X,X,X, X,X,X, 0,0,0, 5,0,1, 0,0,0, X,X,X, X,X,X ],
[ X,X,X, X,X,X, 0,0,9, 0,0,0, 6,0,0, X,X,X, X,X,X ],
[ X,X,X, X,X,X, 0,0,0, 3,0,6, 0,0,0, X,X,X, X,X,X ],
[ 0,0,8, 0,0,0, 0,0,0, 0,7,0, 0,0,0, 0,0,0, 4,0,0 ],
[ 0,4,0, 5,0,1, 0,0,0, 0,0,0, 0,0,0, 9,0,5, 0,7,0 ],
[ 6,0,0, 0,2,0, 0,0,0, 0,0,0, 0,0,0, 0,6,0, 0,0,9 ],
[ 0,9,0, 1,0,3, 0,7,0, X,X,X, 0,7,0, 5,0,1, 0,9,0 ],
[ 0,0,5, 0,0,0, 1,0,0, X,X,X, 0,0,3, 0,0,0, 6,0,0 ],
[ 0,1,0, 6,0,8, 0,9,0, X,X,X, 0,2,0, 8,0,6, 0,1,0 ],
[ 5,0,0, 0,7,0, 0,0,6, X,X,X, 7,0,0, 0,2,0, 0,0,5 ],
[ 0,2,0, 3,0,5, 0,1,0, X,X,X, 0,9,0, 6,0,4, 0,3,0 ],
[ 0,0,6, 0,0,0, 2,0,0, X,X,X, 0,0,4, 0,0,0, 1,0,0 ] ];
[
0, 0, 3, 0, 0, 0, 2, 0, 0, X, X, X, 0, 0, 6, 0, 0, 0, 2, 0, 0,
],
[
0, 2, 0, 4, 0, 8, 0, 3, 0, X, X, X, 0, 3, 0, 4, 0, 2, 0, 8, 0,
],
[
8, 0, 0, 0, 9, 0, 0, 0, 4, X, X, X, 8, 0, 0, 0, 1, 0, 0, 0, 4,
],
[
0, 5, 0, 6, 0, 1, 0, 2, 0, X, X, X, 0, 2, 0, 1, 0, 7, 0, 9, 0,
],
[
0, 0, 8, 0, 0, 0, 6, 0, 0, X, X, X, 0, 0, 9, 0, 0, 0, 8, 0, 0,
],
[
0, 7, 0, 8, 0, 4, 0, 1, 0, X, X, X, 0, 8, 0, 5, 0, 9, 0, 4, 0,
],
[
1, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 5,
],
[
0, 4, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 8, 0, 7, 0,
],
[
0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
],
[
X, X, X, X, X, X, 0, 0, 0, 5, 0, 1, 0, 0, 0, X, X, X, X, X, X,
],
[
X, X, X, X, X, X, 0, 0, 9, 0, 0, 0, 6, 0, 0, X, X, X, X, X, X,
],
[
X, X, X, X, X, X, 0, 0, 0, 3, 0, 6, 0, 0, 0, X, X, X, X, X, X,
],
[
0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0,
],
[
0, 4, 0, 5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 5, 0, 7, 0,
],
[
6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 9,
],
[
0, 9, 0, 1, 0, 3, 0, 7, 0, X, X, X, 0, 7, 0, 5, 0, 1, 0, 9, 0,
],
[
0, 0, 5, 0, 0, 0, 1, 0, 0, X, X, X, 0, 0, 3, 0, 0, 0, 6, 0, 0,
],
[
0, 1, 0, 6, 0, 8, 0, 9, 0, X, X, X, 0, 2, 0, 8, 0, 6, 0, 1, 0,
],
[
5, 0, 0, 0, 7, 0, 0, 0, 6, X, X, X, 7, 0, 0, 0, 2, 0, 0, 0, 5,
],
[
0, 2, 0, 3, 0, 5, 0, 1, 0, X, X, X, 0, 9, 0, 6, 0, 4, 0, 3, 0,
],
[
0, 0, 6, 0, 0, 0, 2, 0, 0, X, X, X, 0, 0, 4, 0, 0, 0, 1, 0, 0,
],
];
let expected = [
[ 4,9,3, 7,1,5, 2,6,8, X,X,X, 9,4,6, 8,3,5, 2,1,7 ],
[ 5,2,7, 4,6,8, 9,3,1, X,X,X, 1,3,7, 4,9,2, 5,8,6 ],
[ 8,6,1, 2,9,3, 5,7,4, X,X,X, 8,5,2, 7,1,6, 9,3,4 ],
[ 9,5,4, 6,3,1, 8,2,7, X,X,X, 5,2,4, 1,8,7, 6,9,3 ],
[ 3,1,8, 9,2,7, 6,4,5, X,X,X, 7,1,9, 6,4,3, 8,5,2 ],
[ 2,7,6, 8,5,4, 3,1,9, X,X,X, 6,8,3, 5,2,9, 7,4,1 ],
[ 1,8,2, 3,7,9, 4,5,6, 7,1,3, 2,9,8, 3,7,1, 4,6,5 ],
[ 6,4,5, 1,8,2, 7,9,3, 8,5,2, 4,6,1, 2,5,8, 3,7,9 ],
[ 7,3,9, 5,4,6, 1,8,2, 9,6,4, 3,7,5, 9,6,4, 1,2,8 ],
[ X,X,X, X,X,X, 6,4,8, 5,9,1, 7,2,3, X,X,X, X,X,X ],
[ X,X,X, X,X,X, 3,1,9, 2,8,7, 6,5,4, X,X,X, X,X,X ],
[ X,X,X, X,X,X, 2,7,5, 3,4,6, 1,8,9, X,X,X, X,X,X ],
[ 1,7,8, 9,3,6, 5,2,4, 1,7,8, 9,3,6, 2,8,7, 4,5,1 ],
[ 2,4,3, 5,8,1, 9,6,7, 4,3,5, 8,1,2, 9,4,5, 3,7,6 ],
[ 6,5,9, 4,2,7, 8,3,1, 6,2,9, 5,4,7, 1,6,3, 8,2,9 ],
[ 8,9,2, 1,4,3, 6,7,5, X,X,X, 6,7,8, 5,3,1, 2,9,4 ],
[ 4,6,5, 7,9,2, 1,8,3, X,X,X, 1,5,3, 4,9,2, 6,8,7 ],
[ 3,1,7, 6,5,8, 4,9,2, X,X,X, 4,2,9, 8,7,6, 5,1,3 ],
[ 5,8,1, 2,7,9, 3,4,6, X,X,X, 7,6,1, 3,2,8, 9,4,5 ],
[ 9,2,4, 3,6,5, 7,1,8, X,X,X, 2,9,5, 6,1,4, 7,3,8 ],
[ 7,3,6, 8,1,4, 2,5,9, X,X,X, 3,8,4, 7,5,9, 1,6,2 ] ];
[
4, 9, 3, 7, 1, 5, 2, 6, 8, X, X, X, 9, 4, 6, 8, 3, 5, 2, 1, 7,
],
[
5, 2, 7, 4, 6, 8, 9, 3, 1, X, X, X, 1, 3, 7, 4, 9, 2, 5, 8, 6,
],
[
8, 6, 1, 2, 9, 3, 5, 7, 4, X, X, X, 8, 5, 2, 7, 1, 6, 9, 3, 4,
],
[
9, 5, 4, 6, 3, 1, 8, 2, 7, X, X, X, 5, 2, 4, 1, 8, 7, 6, 9, 3,
],
[
3, 1, 8, 9, 2, 7, 6, 4, 5, X, X, X, 7, 1, 9, 6, 4, 3, 8, 5, 2,
],
[
2, 7, 6, 8, 5, 4, 3, 1, 9, X, X, X, 6, 8, 3, 5, 2, 9, 7, 4, 1,
],
[
1, 8, 2, 3, 7, 9, 4, 5, 6, 7, 1, 3, 2, 9, 8, 3, 7, 1, 4, 6, 5,
],
[
6, 4, 5, 1, 8, 2, 7, 9, 3, 8, 5, 2, 4, 6, 1, 2, 5, 8, 3, 7, 9,
],
[
7, 3, 9, 5, 4, 6, 1, 8, 2, 9, 6, 4, 3, 7, 5, 9, 6, 4, 1, 2, 8,
],
[
X, X, X, X, X, X, 6, 4, 8, 5, 9, 1, 7, 2, 3, X, X, X, X, X, X,
],
[
X, X, X, X, X, X, 3, 1, 9, 2, 8, 7, 6, 5, 4, X, X, X, X, X, X,
],
[
X, X, X, X, X, X, 2, 7, 5, 3, 4, 6, 1, 8, 9, X, X, X, X, X, X,
],
[
1, 7, 8, 9, 3, 6, 5, 2, 4, 1, 7, 8, 9, 3, 6, 2, 8, 7, 4, 5, 1,
],
[
2, 4, 3, 5, 8, 1, 9, 6, 7, 4, 3, 5, 8, 1, 2, 9, 4, 5, 3, 7, 6,
],
[
6, 5, 9, 4, 2, 7, 8, 3, 1, 6, 2, 9, 5, 4, 7, 1, 6, 3, 8, 2, 9,
],
[
8, 9, 2, 1, 4, 3, 6, 7, 5, X, X, X, 6, 7, 8, 5, 3, 1, 2, 9, 4,
],
[
4, 6, 5, 7, 9, 2, 1, 8, 3, X, X, X, 1, 5, 3, 4, 9, 2, 6, 8, 7,
],
[
3, 1, 7, 6, 5, 8, 4, 9, 2, X, X, X, 4, 2, 9, 8, 7, 6, 5, 1, 3,
],
[
5, 8, 1, 2, 7, 9, 3, 4, 6, X, X, X, 7, 6, 1, 3, 2, 8, 9, 4, 5,
],
[
9, 2, 4, 3, 6, 5, 7, 1, 8, X, X, X, 2, 9, 5, 6, 1, 4, 7, 3, 8,
],
[
7, 3, 6, 8, 1, 4, 2, 5, 9, X, X, X, 3, 8, 4, 7, 5, 9, 1, 6, 2,
],
];
let (mut sys, vars) = make_samurai_sudoku(&puzzle);
let dict = sys.solve_any().expect("solution");

View File

@ -4,11 +4,11 @@
extern crate puzzle_solver;
use puzzle_solver::{Puzzle,Solution,VarToken};
use puzzle_solver::{Puzzle, Solution, VarToken};
fn make_send_more_money() -> (Puzzle, Vec<VarToken>) {
let mut sys = Puzzle::new();
let vars = sys.new_vars_with_candidates_1d(8, &[0,1,2,3,4,5,6,7,8,9]);
let vars = sys.new_vars_with_candidates_1d(8, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
let (s, e, n, d) = (vars[0], vars[1], vars[2], vars[3]);
let (m, o, r, y) = (vars[4], vars[5], vars[6], vars[7]);
@ -32,7 +32,10 @@ fn print_send_more_money(dict: &Solution, vars: &Vec<VarToken>) {
println!(" {} {} {} {}", dict[s], dict[e], dict[n], dict[d]);
println!(" + {} {} {} {}", dict[m], dict[o], dict[r], dict[e]);
println!("----------");
println!(" {} {} {} {} {}", dict[m], dict[o], dict[n], dict[e], dict[y]);
println!(
" {} {} {} {} {}",
dict[m], dict[o], dict[n], dict[e], dict[y]
);
}
fn verify_send_more_money(dict: &Solution, vars: &Vec<VarToken>) {
@ -51,7 +54,7 @@ fn verify_send_more_money(dict: &Solution, vars: &Vec<VarToken>) {
#[test]
fn sendmoremoney_carry() {
let carry = [0,1];
let carry = [0, 1];
let (mut sys, vars) = make_send_more_money();
let (s, e, n, d) = (vars[0], vars[1], vars[2], vars[3]);
@ -61,10 +64,10 @@ fn sendmoremoney_carry() {
let c3 = sys.new_var_with_candidates(&carry);
sys.intersect_candidates(m, &carry); // c4 == m.
sys.equals( d + e, 10 * c1 + y);
sys.equals(d + e, 10 * c1 + y);
sys.equals(c1 + n + r, 10 * c2 + e);
sys.equals(c2 + e + o, 10 * c3 + n);
sys.equals(c3 + s + m, 10 * m + o);
sys.equals(c3 + s + m, 10 * m + o);
let dict = sys.solve_unique().expect("solution");
print_send_more_money(&dict, &vars);

View File

@ -4,7 +4,7 @@
extern crate puzzle_solver;
use puzzle_solver::{Puzzle,Solution,Val,VarToken};
use puzzle_solver::{Puzzle, Solution, Val, VarToken};
const SQRT_SIZE: usize = 3;
const SIZE: usize = 9;
@ -12,7 +12,7 @@ type Board = [[Val; SIZE]; SIZE];
fn make_sudoku(board: &Board) -> (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]);
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]);
@ -25,8 +25,7 @@ fn make_sudoku(board: &Board) -> (Puzzle, Vec<Vec<VarToken>>) {
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)]));
sys.all_different((0..SIZE).map(|n| &vars[y0 + (n / SQRT_SIZE)][x0 + (n % SQRT_SIZE)]));
}
for y in 0..SIZE {
@ -48,9 +47,11 @@ fn print_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
}
for x in 0..SIZE {
print!("{}{}",
if x % SQRT_SIZE == 0 { " " } else { "" },
dict[vars[y][x]]);
print!(
"{}{}",
if x % SQRT_SIZE == 0 { " " } else { "" },
dict[vars[y][x]]
);
}
println!();
}
@ -67,30 +68,28 @@ fn verify_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>, expected: &Board) {
#[test]
fn sudoku_hardest() {
let puzzle = [
[ 8,0,0, 0,0,0, 0,0,0 ],
[ 0,0,3, 6,0,0, 0,0,0 ],
[ 0,7,0, 0,9,0, 2,0,0 ],
[ 0,5,0, 0,0,7, 0,0,0 ],
[ 0,0,0, 0,4,5, 7,0,0 ],
[ 0,0,0, 1,0,0, 0,3,0 ],
[ 0,0,1, 0,0,0, 0,6,8 ],
[ 0,0,8, 5,0,0, 0,1,0 ],
[ 0,9,0, 0,0,0, 4,0,0 ] ];
[8, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 3, 6, 0, 0, 0, 0, 0],
[0, 7, 0, 0, 9, 0, 2, 0, 0],
[0, 5, 0, 0, 0, 7, 0, 0, 0],
[0, 0, 0, 0, 4, 5, 7, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 3, 0],
[0, 0, 1, 0, 0, 0, 0, 6, 8],
[0, 0, 8, 5, 0, 0, 0, 1, 0],
[0, 9, 0, 0, 0, 0, 4, 0, 0],
];
let expected = [
[ 8,1,2, 7,5,3, 6,4,9 ],
[ 9,4,3, 6,8,2, 1,7,5 ],
[ 6,7,5, 4,9,1, 2,8,3 ],
[ 1,5,4, 2,3,7, 8,9,6 ],
[ 3,6,9, 8,4,5, 7,2,1 ],
[ 2,8,7, 1,6,9, 5,3,4 ],
[ 5,2,1, 9,7,4, 3,6,8 ],
[ 4,3,8, 5,2,6, 9,1,7 ],
[ 7,9,6, 3,1,8, 4,5,2 ] ];
[8, 1, 2, 7, 5, 3, 6, 4, 9],
[9, 4, 3, 6, 8, 2, 1, 7, 5],
[6, 7, 5, 4, 9, 1, 2, 8, 3],
[1, 5, 4, 2, 3, 7, 8, 9, 6],
[3, 6, 9, 8, 4, 5, 7, 2, 1],
[2, 8, 7, 1, 6, 9, 5, 3, 4],
[5, 2, 1, 9, 7, 4, 3, 6, 8],
[4, 3, 8, 5, 2, 6, 9, 1, 7],
[7, 9, 6, 3, 1, 8, 4, 5, 2],
];
let (mut sys, vars) = make_sudoku(&puzzle);
let solution = sys.solve_any().expect("solution");
@ -102,30 +101,28 @@ fn sudoku_hardest() {
#[test]
fn sudoku_wikipedia() {
let puzzle = [
[ 5,3,0, 0,7,0, 0,0,0 ],
[ 6,0,0, 1,9,5, 0,0,0 ],
[ 0,9,8, 0,0,0, 0,6,0 ],
[ 8,0,0, 0,6,0, 0,0,3 ],
[ 4,0,0, 8,0,3, 0,0,1 ],
[ 7,0,0, 0,2,0, 0,0,6 ],
[ 0,6,0, 0,0,0, 2,8,0 ],
[ 0,0,0, 4,1,9, 0,0,5 ],
[ 0,0,0, 0,8,0, 0,7,9 ] ];
[5, 3, 0, 0, 7, 0, 0, 0, 0],
[6, 0, 0, 1, 9, 5, 0, 0, 0],
[0, 9, 8, 0, 0, 0, 0, 6, 0],
[8, 0, 0, 0, 6, 0, 0, 0, 3],
[4, 0, 0, 8, 0, 3, 0, 0, 1],
[7, 0, 0, 0, 2, 0, 0, 0, 6],
[0, 6, 0, 0, 0, 0, 2, 8, 0],
[0, 0, 0, 4, 1, 9, 0, 0, 5],
[0, 0, 0, 0, 8, 0, 0, 7, 9],
];
let expected = [
[ 5,3,4, 6,7,8, 9,1,2 ],
[ 6,7,2, 1,9,5, 3,4,8 ],
[ 1,9,8, 3,4,2, 5,6,7 ],
[ 8,5,9, 7,6,1, 4,2,3 ],
[ 4,2,6, 8,5,3, 7,9,1 ],
[ 7,1,3, 9,2,4, 8,5,6 ],
[ 9,6,1, 5,3,7, 2,8,4 ],
[ 2,8,7, 4,1,9, 6,3,5 ],
[ 3,4,5, 2,8,6, 1,7,9 ] ];
[5, 3, 4, 6, 7, 8, 9, 1, 2],
[6, 7, 2, 1, 9, 5, 3, 4, 8],
[1, 9, 8, 3, 4, 2, 5, 6, 7],
[8, 5, 9, 7, 6, 1, 4, 2, 3],
[4, 2, 6, 8, 5, 3, 7, 9, 1],
[7, 1, 3, 9, 2, 4, 8, 5, 6],
[9, 6, 1, 5, 3, 7, 2, 8, 4],
[2, 8, 7, 4, 1, 9, 6, 3, 5],
[3, 4, 5, 2, 8, 6, 1, 7, 9],
];
let (mut sys, vars) = make_sudoku(&puzzle);
let solution = sys.solve_any().expect("solution");

View File

@ -5,15 +5,14 @@
extern crate puzzle_solver;
use puzzle_solver::{Puzzle,Solution,Val,VarToken};
use puzzle_solver::{Puzzle, Solution, Val, VarToken};
const SIZE: usize = 3;
type Board = [[Val; SIZE]; SIZE];
fn make_sujiko(board: &Board, tl: Val, tr: Val, bl: Val, br: Val)
-> (Puzzle, Vec<Vec<VarToken>>) {
fn make_sujiko(board: &Board, tl: Val, tr: Val, bl: Val, br: Val) -> (Puzzle, Vec<Vec<VarToken>>) {
let mut sys = Puzzle::new();
let vars = sys.new_vars_with_candidates_2d(3, 3, &[1,2,3,4,5,6,7,8,9]);
let vars = sys.new_vars_with_candidates_2d(3, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
sys.all_different(vars.iter().flat_map(|it| it));
@ -22,8 +21,10 @@ fn make_sujiko(board: &Board, tl: Val, tr: Val, bl: Val, br: Val)
sys.equals(bl, vars[1][0] + vars[1][1] + vars[2][0] + vars[2][1]);
sys.equals(br, vars[1][1] + vars[1][2] + vars[2][1] + vars[2][2]);
sys.equals(tl + tr + bl + br - (1..(9 + 1)).sum::<Val>(),
vars[0][1] + vars[1][0] + 3 * vars[1][1] + vars[1][2] + vars[2][1]);
sys.equals(
tl + tr + bl + br - (1..(9 + 1)).sum::<Val>(),
vars[0][1] + vars[1][0] + 3 * vars[1][1] + vars[1][2] + vars[2][1],
);
for y in 0..SIZE {
for x in 0..SIZE {
@ -56,8 +57,8 @@ fn verify_sujiko(dict: &Solution, vars: &Vec<Vec<VarToken>>, expected: &Board) {
#[test]
fn sujiko_simetric() {
let puzzle = [ [ 6,0,9 ], [ 0,0,0 ], [ 5,0,0 ] ];
let expected = [ [ 6,2,9 ], [ 8,1,3 ], [ 5,4,7 ] ];
let puzzle = [[6, 0, 9], [0, 0, 0], [5, 0, 0]];
let expected = [[6, 2, 9], [8, 1, 3], [5, 4, 7]];
let (mut sys, vars) = make_sujiko(&puzzle, 17, 15, 18, 15);
let dict = sys.solve_unique().expect("solution");

View File

@ -4,9 +4,9 @@
extern crate puzzle_solver;
use puzzle_solver::*;
use std::iter;
use std::rc::Rc;
use puzzle_solver::*;
const X: Val = -1;
@ -19,12 +19,11 @@ struct BinaryRepr {
}
impl Constraint for BinaryRepr {
fn vars<'a>(&'a self) -> Box<dyn Iterator<Item=&'a VarToken> + 'a> {
fn vars<'a>(&'a self) -> Box<dyn 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<()> {
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() {
@ -33,7 +32,8 @@ impl Constraint for BinaryRepr {
}
} else if let Some(bitpos) = self.bits.iter().position(|&v| v == var) {
let bit = 1 << bitpos;
let discard = search.get_unassigned(self.value)
let discard = search
.get_unassigned(self.value)
.filter(|c| c & bit != val * bit)
.collect::<Vec<_>>();
@ -45,8 +45,7 @@ impl Constraint for BinaryRepr {
Ok(())
}
fn substitute(&self, _from: VarToken, _to: VarToken)
-> PsResult<Rc<dyn Constraint>> {
fn substitute(&self, _from: VarToken, _to: VarToken) -> PsResult<Rc<dyn Constraint>> {
unimplemented!();
}
}
@ -83,13 +82,16 @@ fn make_takuzu(puzzle: &Vec<Vec<Val>>) -> (Puzzle, Vec<Vec<VarToken>>) {
let col_candidates = make_sums(width);
let mut sys = Puzzle::new();
let vars = sys.new_vars_with_candidates_2d(width, height, &[0,1]);
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.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(),
@ -98,7 +100,10 @@ fn make_takuzu(puzzle: &Vec<Vec<Val>>) -> (Puzzle, Vec<Vec<VarToken>>) {
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.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(),
@ -108,15 +113,18 @@ fn make_takuzu(puzzle: &Vec<Vec<Val>>) -> (Puzzle, Vec<Vec<VarToken>>) {
// 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]);
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);
let disjunction = sys.new_var_with_candidates(&[1, 2]);
sys.equals(
vars[y + 0][x] + vars[y + 1][x] + vars[y + 2][x],
disjunction,
);
}
}
@ -154,12 +162,13 @@ fn verify_takuzu(dict: &Solution, vars: &Vec<Vec<VarToken>>, expected: &[Val]) {
#[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 ] ];
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();
@ -172,18 +181,19 @@ fn takuzu_grid1() {
#[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 ] ];
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,
@ -197,7 +207,8 @@ fn takuzu_grid2() {
0b_110010010110,
0b_010101101010,
0b_101010010101,
0b_101011010100 ];
0b_101011010100,
];
let (mut sys, vars) = make_takuzu(&puzzle);
let dict = sys.solve_unique().expect("solution");
@ -209,18 +220,19 @@ fn takuzu_grid2() {
#[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 ] ];
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,
@ -234,7 +246,8 @@ fn takuzu_grid3() {
0b_010010101101,
0b_011001011001,
0b_100110100110,
0b_010101100110 ];
0b_010101100110,
];
let (mut sys, vars) = make_takuzu(&puzzle);
let dict = sys.solve_unique().expect("solution");
@ -246,18 +259,19 @@ fn takuzu_grid3() {
#[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 ] ];
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");

View File

@ -9,7 +9,7 @@ extern crate puzzle_solver;
use num_rational::Ratio;
use num_traits::ToPrimitive;
use puzzle_solver::{LinExpr,Puzzle,Val};
use puzzle_solver::{LinExpr, Puzzle, Val};
#[test]
fn xkcd_knapsack() {
@ -32,8 +32,12 @@ fn xkcd_knapsack() {
vars.push(var)
}
sys.equals(total, vars.iter().zip(menu.iter()).fold(LinExpr::from(0),
|sum, (&var, &(cost, _))| sum + var * cost));
sys.equals(
total,
vars.iter()
.zip(menu.iter())
.fold(LinExpr::from(0), |sum, (&var, &(cost, _))| sum + var * cost),
);
let solutions = sys.solve_all();
assert_eq!(solutions.len(), 2);

View File

@ -9,32 +9,66 @@ extern crate puzzle_solver;
use puzzle_solver::Puzzle;
#[derive(Clone,Copy,Debug)]
enum Nat { Dane, Englishman, German, Norwegian, Swede }
#[derive(Clone, Copy, Debug)]
enum Nat {
Dane,
Englishman,
German,
Norwegian,
Swede,
}
#[derive(Clone,Copy,Debug)]
enum Col { Blue, Green, Red, White, Yellow }
#[derive(Clone, Copy, Debug)]
enum Col {
Blue,
Green,
Red,
White,
Yellow,
}
#[derive(Clone,Copy,Debug)]
enum Dri { Beer, Coffee, Milk, Tea, Water }
#[derive(Clone, Copy, Debug)]
enum Dri {
Beer,
Coffee,
Milk,
Tea,
Water,
}
#[derive(Clone,Copy,Debug)]
enum Smo { Blend, BlueMaster, Dunhill, PallMall, Prince }
#[derive(Clone, Copy, Debug)]
enum Smo {
Blend,
BlueMaster,
Dunhill,
PallMall,
Prince,
}
#[derive(Clone,Copy,Debug)]
enum Pet { Bird, Cat, Dog, Fish, Horse }
#[derive(Clone, Copy, Debug)]
enum Pet {
Bird,
Cat,
Dog,
Fish,
Horse,
}
#[test]
fn zebra() {
use crate::Nat::*; use crate::Col::*; use crate::Dri::*; use crate::Smo::*; use crate::Pet::*;
use crate::Col::*;
use crate::Dri::*;
use crate::Nat::*;
use crate::Pet::*;
use crate::Smo::*;
// #1: There are five houses.
let mut sys = Puzzle::new();
let nat_var = sys.new_vars_with_candidates_1d(5, &[1,2,3,4,5]);
let col_var = sys.new_vars_with_candidates_1d(5, &[1,2,3,4,5]);
let dri_var = sys.new_vars_with_candidates_1d(5, &[1,2,3,4,5]);
let smo_var = sys.new_vars_with_candidates_1d(5, &[1,2,3,4,5]);
let pet_var = sys.new_vars_with_candidates_1d(5, &[1,2,3,4,5]);
let nat_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
let col_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
let dri_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
let smo_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
let pet_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
let nat = |n| nat_var[n as usize];
let col = |n| col_var[n as usize];
@ -76,11 +110,11 @@ fn zebra() {
sys.equals(nat(Norwegian), 1);
// #11: The man who smokes Blend lives in the house next to the house with cats.
let neighbour11 = sys.new_var_with_candidates(&[-1,1]);
let neighbour11 = sys.new_var_with_candidates(&[-1, 1]);
sys.equals(smo(Blend), pet(Cat) + neighbour11);
// #12: In a house next to the house where they have a horse, they smoke Dunhill.
let neighbour12 = sys.new_var_with_candidates(&[-1,1]);
let neighbour12 = sys.new_var_with_candidates(&[-1, 1]);
sys.equals(pet(Horse), smo(Dunhill) + neighbour12);
// #13: The man who smokes Blue Master drinks beer.
@ -90,23 +124,24 @@ fn zebra() {
sys.equals(nat(German), smo(Prince));
// #15: The Norwegian lives next to the blue house.
let neighbour15 = sys.new_var_with_candidates(&[-1,1]);
let neighbour15 = sys.new_var_with_candidates(&[-1, 1]);
sys.equals(nat(Norwegian), col(Blue) + neighbour15);
// #16: They drink water in a house next to the house where they smoke Blend.
let neighbour16 = sys.new_var_with_candidates(&[-1,1]);
let neighbour16 = sys.new_var_with_candidates(&[-1, 1]);
sys.equals(dri(Water), smo(Blend) + neighbour16);
let dict = sys.solve_any().expect("solution");
let expected = [
(Norwegian, Yellow, Water, Dunhill, Cat),
(Dane, Blue, Tea, Blend, Horse),
(Englishman, Red, Milk, PallMall, Bird),
(German, Green, Coffee, Prince, Fish),
(Swede, White, Beer, BlueMaster, Dog) ];
(Norwegian, Yellow, Water, Dunhill, Cat),
(Dane, Blue, Tea, Blend, Horse),
(Englishman, Red, Milk, PallMall, Bird),
(German, Green, Coffee, Prince, Fish),
(Swede, White, Beer, BlueMaster, Dog),
];
for &(n,c,d,s,p) in expected.iter() {
for &(n, c, d, s, p) in expected.iter() {
assert_eq!(dict[nat(n)], dict[col(c)]);
assert_eq!(dict[nat(n)], dict[dri(d)]);
assert_eq!(dict[nat(n)], dict[smo(s)]);