fmt
This commit is contained in:
parent
74ea9c497f
commit
da22e1dfbb
@ -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(),
|
||||
}
|
||||
@ -34,8 +36,7 @@ impl Constraint for AllDifferent {
|
||||
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,8 +76,7 @@ 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();
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! 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};
|
||||
|
||||
@ -25,9 +25,7 @@ impl Equality {
|
||||
/// vars[0][0] + vars[0][1] + vars[0][2] - 15);
|
||||
/// ```
|
||||
pub fn new(eqn: LinExpr) -> Self {
|
||||
Equality {
|
||||
eqn: eqn,
|
||||
}
|
||||
Equality { eqn: eqn }
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,8 +34,7 @@ impl Constraint for Equality {
|
||||
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,8 +139,7 @@ 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;
|
||||
|
@ -15,8 +15,7 @@ pub trait Constraint {
|
||||
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;
|
||||
|
@ -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,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,9 @@ 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;
|
||||
|
@ -1,11 +1,11 @@
|
||||
//! Linear expressions.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::convert::From;
|
||||
use std::ops::{Add,Mul,Neg,Sub};
|
||||
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 crate::{Coef, LinExpr, VarToken};
|
||||
|
||||
@ -13,13 +13,17 @@ 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() {
|
||||
|
170
src/puzzle.rs
170
src/puzzle.rs
@ -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,10 +8,9 @@ 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)]
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -329,7 +332,10 @@ 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> {
|
||||
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,8 +703,11 @@ impl<'a> PuzzleSearch<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
let next_unassigned = self.vars.iter().enumerate().min_by_key(
|
||||
|&(_, vs)| match vs {
|
||||
let next_unassigned = self
|
||||
.vars
|
||||
.iter()
|
||||
.enumerate()
|
||||
.min_by_key(|&(_, vs)| match vs {
|
||||
&VarState::Unassigned(ref cs) => cs.len(),
|
||||
_ => ::std::usize::MAX,
|
||||
});
|
||||
@ -716,8 +735,9 @@ impl<'a> PuzzleSearch<'a> {
|
||||
}
|
||||
} else {
|
||||
// No unassigned variables remaining.
|
||||
let vars = (0..self.puzzle.num_vars).map(|idx|
|
||||
self[VarToken(idx)]).collect();
|
||||
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);
|
||||
|
@ -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);
|
||||
@ -91,7 +97,8 @@ fn hidato_wikipedia() {
|
||||
[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 ] ];
|
||||
[NA, NA, NA, NA, NA, NA, 5, 0],
|
||||
];
|
||||
|
||||
let expected = [
|
||||
[32, 33, 35, 36, 37, NA, NA, NA],
|
||||
@ -101,7 +108,8 @@ fn hidato_wikipedia() {
|
||||
[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 ] ];
|
||||
[NA, NA, NA, NA, NA, NA, 5, 4],
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_hidato(&puzzle);
|
||||
let dict = sys.solve_any().expect("solution");
|
||||
|
181
tests/kakuro.rs
181
tests/kakuro.rs
@ -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) {
|
||||
@ -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,21 +84,150 @@ 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 = [
|
||||
@ -95,7 +237,8 @@ fn kakuro_wikipedia() {
|
||||
[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 ] ];
|
||||
[3, 1, 2, X, X, 2, 1],
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_kakuro(&puzzle);
|
||||
let dict = sys.solve_any().expect("solution");
|
||||
|
@ -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!("{}{}",
|
||||
print!(
|
||||
"{}{}",
|
||||
if x % SQRT_SIZE == 0 { " " } else { "" },
|
||||
dict[vars[y][x]]);
|
||||
dict[vars[y][x]]
|
||||
);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
@ -98,14 +104,13 @@ fn killersudoku_wikipedia() {
|
||||
[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 ] ];
|
||||
[4, 3, 7, 1, 6, 5, 2, 8, 9],
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_killer_sudoku(&puzzle);
|
||||
let dict = sys.solve_any().expect("solution");
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
@ -45,11 +45,14 @@ impl Nonogram {
|
||||
}
|
||||
}
|
||||
|
||||
fn autofill(&self,
|
||||
trial: &mut Vec<Val>, pos: usize, rule_idx: usize,
|
||||
fn autofill(
|
||||
&self,
|
||||
trial: &mut Vec<Val>,
|
||||
pos: usize,
|
||||
rule_idx: usize,
|
||||
accum: &mut Vec<Val>,
|
||||
cache: &mut HashMap<(usize, usize), AutoFillResult>)
|
||||
-> AutoFillResult {
|
||||
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;
|
||||
@ -102,7 +106,8 @@ impl Nonogram {
|
||||
// Try filled.
|
||||
if trial[pos] != FLAG_OFF
|
||||
&& rule_idx < self.rule.len()
|
||||
&& pos + self.rule[rule_idx] <= trial.len() {
|
||||
&& pos + self.rule[rule_idx] <= trial.len()
|
||||
{
|
||||
let mut ok = true;
|
||||
let mut pos = pos;
|
||||
|
||||
@ -159,29 +164,28 @@ 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]);
|
||||
@ -193,7 +197,8 @@ 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]));
|
||||
&cols[x],
|
||||
));
|
||||
}
|
||||
|
||||
(sys, vars)
|
||||
@ -270,24 +275,22 @@ fn nonogram_wikipedia() {
|
||||
[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, 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");
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
extern crate puzzle_solver;
|
||||
|
||||
use std::rc::Rc;
|
||||
use puzzle_solver::*;
|
||||
use std::rc::Rc;
|
||||
|
||||
struct NoDiagonal {
|
||||
vars: Vec<VarToken>,
|
||||
@ -16,9 +16,12 @@ impl Constraint for NoDiagonal {
|
||||
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!();
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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>) {
|
||||
|
@ -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!("{}{}",
|
||||
print!(
|
||||
"{}{}",
|
||||
if x % SQRT_SIZE == 0 { " " } else { "" },
|
||||
dict[vars[y][x]]);
|
||||
dict[vars[y][x]]
|
||||
);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
@ -70,27 +71,25 @@ fn sudoku_hardest() {
|
||||
[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 ] ];
|
||||
[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 ] ];
|
||||
[7, 9, 6, 3, 1, 8, 4, 5, 2],
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_sudoku(&puzzle);
|
||||
let solution = sys.solve_any().expect("solution");
|
||||
@ -105,27 +104,25 @@ fn sudoku_wikipedia() {
|
||||
[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 ] ];
|
||||
[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 ] ];
|
||||
[3, 4, 5, 2, 8, 6, 1, 7, 9],
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_sudoku(&puzzle);
|
||||
let solution = sys.solve_any().expect("solution");
|
||||
|
@ -10,8 +10,7 @@ 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]);
|
||||
|
||||
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
@ -23,8 +23,7 @@ impl Constraint for BinaryRepr {
|
||||
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!();
|
||||
}
|
||||
}
|
||||
@ -89,7 +88,10 @@ fn make_takuzu(puzzle: &Vec<Vec<Val>>) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
|
||||
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(),
|
||||
@ -116,7 +121,10 @@ fn make_takuzu(puzzle: &Vec<Vec<Val>>) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
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.equals(
|
||||
vars[y + 0][x] + vars[y + 1][x] + vars[y + 2][x],
|
||||
disjunction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +167,8 @@ fn takuzu_grid1() {
|
||||
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, X, X, X, X, X],
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_takuzu(&puzzle);
|
||||
let solutions = sys.solve_all();
|
||||
@ -183,7 +192,8 @@ fn takuzu_grid2() {
|
||||
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![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");
|
||||
@ -220,7 +231,8 @@ fn takuzu_grid3() {
|
||||
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, 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");
|
||||
@ -257,7 +270,8 @@ fn takuzu_grid4() {
|
||||
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, 1, X, X, X, X, 1, X, X, 0, 0],
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_takuzu(&puzzle);
|
||||
let dict = &sys.solve_any().expect("solution");
|
||||
|
@ -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);
|
||||
|
@ -10,23 +10,57 @@ extern crate puzzle_solver;
|
||||
use puzzle_solver::Puzzle;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Nat { Dane, Englishman, German, Norwegian, Swede }
|
||||
enum Nat {
|
||||
Dane,
|
||||
Englishman,
|
||||
German,
|
||||
Norwegian,
|
||||
Swede,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Col { Blue, Green, Red, White, Yellow }
|
||||
enum Col {
|
||||
Blue,
|
||||
Green,
|
||||
Red,
|
||||
White,
|
||||
Yellow,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Dri { Beer, Coffee, Milk, Tea, Water }
|
||||
enum Dri {
|
||||
Beer,
|
||||
Coffee,
|
||||
Milk,
|
||||
Tea,
|
||||
Water,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Smo { Blend, BlueMaster, Dunhill, PallMall, Prince }
|
||||
enum Smo {
|
||||
Blend,
|
||||
BlueMaster,
|
||||
Dunhill,
|
||||
PallMall,
|
||||
Prince,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Pet { Bird, Cat, Dog, Fish, Horse }
|
||||
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();
|
||||
@ -104,7 +138,8 @@ fn zebra() {
|
||||
(Dane, Blue, Tea, Blend, Horse),
|
||||
(Englishman, Red, Milk, PallMall, Bird),
|
||||
(German, Green, Coffee, Prince, Fish),
|
||||
(Swede, White, Beer, BlueMaster, Dog) ];
|
||||
(Swede, White, Beer, BlueMaster, Dog),
|
||||
];
|
||||
|
||||
for &(n, c, d, s, p) in expected.iter() {
|
||||
assert_eq!(dict[nat(n)], dict[col(c)]);
|
||||
|
Loading…
Reference in New Issue
Block a user