diff --git a/src/constraint/alldifferent.rs b/src/constraint/alldifferent.rs index 744b239..c69a10c 100644 --- a/src/constraint/alldifferent.rs +++ b/src/constraint/alldifferent.rs @@ -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, @@ -22,7 +22,9 @@ impl AllDifferent { /// puzzle_solver::constraint::AllDifferent::new(&vars); /// ``` pub fn new<'a, I>(vars: I) -> Self - where I: IntoIterator { + where + I: IntoIterator, + { AllDifferent { vars: vars.into_iter().cloned().collect(), } @@ -30,12 +32,11 @@ impl AllDifferent { } impl Constraint for AllDifferent { - fn vars<'a>(&'a self) -> Box + 'a> { + fn vars<'a>(&'a self) -> Box + '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> { + fn substitute(&self, from: VarToken, to: VarToken) -> PsResult> { 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::>(), &[2,3]); - assert_eq!(search.get_unassigned(v2).collect::>(), &[2,3]); + assert_eq!(search.get_unassigned(v1).collect::>(), &[2, 3]); + assert_eq!(search.get_unassigned(v2).collect::>(), &[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::>(), &[1,2]); - assert_eq!(search.get_unassigned(v1).collect::>(), &[1,2]); + assert_eq!(search.get_unassigned(v0).collect::>(), &[1, 2]); + assert_eq!(search.get_unassigned(v1).collect::>(), &[1, 2]); assert_eq!(search[v2], 3); } } diff --git a/src/constraint/equality.rs b/src/constraint/equality.rs index 94e512d..6b4e409 100644 --- a/src/constraint/equality.rs +++ b/src/constraint/equality.rs @@ -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 + 'a> { + fn vars<'a>(&'a self) -> Box + '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> { + fn substitute(&self, from: VarToken, to: VarToken) -> PsResult> { 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::>(), &[1,2]); - assert_eq!(search.get_unassigned(v1).collect::>(), &[3,4]); + assert_eq!(search.get_unassigned(v0).collect::>(), &[1, 2]); + assert_eq!(search.get_unassigned(v1).collect::>(), &[3, 4]); } } diff --git a/src/constraint/mod.rs b/src/constraint/mod.rs index a6eb40e..74481f0 100644 --- a/src/constraint/mod.rs +++ b/src/constraint/mod.rs @@ -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 + 'a>; + fn vars<'a>(&'a self) -> Box + '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>; + fn substitute(&self, from: VarToken, to: VarToken) -> PsResult>; } pub use self::alldifferent::AllDifferent; diff --git a/src/constraint/unify.rs b/src/constraint/unify.rs index 4c5661f..c05e6f3 100644 --- a/src/constraint/unify.rs +++ b/src/constraint/unify.rs @@ -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 + 'a> { + fn vars<'a>(&'a self) -> Box + '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> { + fn substitute(&self, from: VarToken, to: VarToken) -> PsResult> { 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); diff --git a/src/lib.rs b/src/lib.rs index 863e668..db24ce4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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). diff --git a/src/linexpr.rs b/src/linexpr.rs index 4fcbd05..5334e9b 100644 --- a/src/linexpr.rs +++ b/src/linexpr.rs @@ -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() { diff --git a/src/puzzle.rs b/src/puzzle.rs index e8f7a6b..996466d 100644 --- a/src/puzzle.rs +++ b/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,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>), // A variable with a list of candidates. + None, // A variable with no candidates. + Value(Val), // A variable set to its initial value. + Set(Rc>), // 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 + 'a> { + fn iter<'a>(&'a self) -> Box + '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 { + pub fn new_vars_with_candidates_1d(&mut self, n: usize, candidates: &[Val]) -> Vec { 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> { + pub fn new_vars_with_candidates_2d( + self: &mut Puzzle, + width: usize, + height: usize, + candidates: &[Val], + ) -> Vec> { 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(&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 { + where + I: IntoIterator, + { 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(&mut self, lhs: L, rhs: R) - where L: Into, R: Into { + pub fn equals(&mut self, lhs: L, rhs: R) + where + L: Into, + R: Into, + { 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>, num_vars: usize) - -> Vec { + fn init_wake(constraints: &Vec>, num_vars: usize) -> Vec { 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 + 'a> { + pub fn get_unassigned(&'a self, var: VarToken) -> Box + '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); diff --git a/tests/hidato.rs b/tests/hidato.rs index 7f98b94..1860d23 100644 --- a/tests/hidato.rs +++ b/tests/hidato.rs @@ -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) { 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, 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"); diff --git a/tests/kakuro.rs b/tests/kakuro.rs index 84cec7f..2c9e915 100644 --- a/tests/kakuro.rs +++ b/tests/kakuro.rs @@ -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>>; 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"); diff --git a/tests/killersudoku.rs b/tests/killersudoku.rs index a897bd0..e8f182e 100644 --- a/tests/killersudoku.rs +++ b/tests/killersudoku.rs @@ -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)]) -> (Puzzle, Vec>) { 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)]) -> (Puzzle, Vec 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>) { } 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>, 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"); diff --git a/tests/magicsquare.rs b/tests/magicsquare.rs index 096f582..97bf440 100644 --- a/tests/magicsquare.rs +++ b/tests/magicsquare.rs @@ -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>, VarToken) { let mut sys = Puzzle::new(); @@ -19,16 +19,28 @@ fn make_magic_square(n: usize) -> (Puzzle, Vec>, 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. diff --git a/tests/nonogram.rs b/tests/nonogram.rs index 5987fd7..224f84c 100644 --- a/tests/nonogram.rs +++ b/tests/nonogram.rs @@ -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, } -#[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, pos: usize, rule_idx: usize, - accum: &mut Vec, - cache: &mut HashMap<(usize, usize), AutoFillResult>) - -> AutoFillResult { + fn autofill( + &self, + trial: &mut Vec, + pos: usize, + rule_idx: usize, + accum: &mut Vec, + 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::() - + (self.rule.len() - rule_idx - 1); + let required_space = + self.rule[rule_idx..].iter().sum::() + (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 + 'a> { + fn vars<'a>(&'a self) -> Box + '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> { + fn substitute(&self, _search: VarToken, _replace: VarToken) -> PsResult> { unimplemented!(); } } /*--------------------------------------------------------------*/ -fn make_nonogram(rows: &[Vec], cols: &[Vec]) - -> (Puzzle, Vec>) { +fn make_nonogram(rows: &[Vec], cols: &[Vec]) -> (Puzzle, Vec>) { 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], cols: &[Vec]) 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"); diff --git a/tests/queens.rs b/tests/queens.rs index 9a5b2dd..bedd489 100644 --- a/tests/queens.rs +++ b/tests/queens.rs @@ -4,21 +4,24 @@ extern crate puzzle_solver; -use std::rc::Rc; use puzzle_solver::*; +use std::rc::Rc; struct NoDiagonal { vars: Vec, } impl Constraint for NoDiagonal { - fn vars<'a>(&'a self) -> Box + 'a> { + fn vars<'a>(&'a self) -> Box + '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> { + fn substitute(&self, _from: VarToken, _to: VarToken) -> PsResult> { unimplemented!(); } } @@ -43,7 +45,7 @@ fn make_queens(n: usize) -> (Puzzle, Vec) { 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) } diff --git a/tests/samuraisudoku.rs b/tests/samuraisudoku.rs index 2380fea..a8a9879 100644 --- a/tests/samuraisudoku.rs +++ b/tests/samuraisudoku.rs @@ -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>; 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"); diff --git a/tests/sendmoremoney.rs b/tests/sendmoremoney.rs index b7b02b7..b5eeddb 100644 --- a/tests/sendmoremoney.rs +++ b/tests/sendmoremoney.rs @@ -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) { 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) { 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) { @@ -51,7 +54,7 @@ fn verify_send_more_money(dict: &Solution, vars: &Vec) { #[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); diff --git a/tests/sudoku.rs b/tests/sudoku.rs index b2b59d7..01986d6 100644 --- a/tests/sudoku.rs +++ b/tests/sudoku.rs @@ -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>) { 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>) { 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>) { } 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>, 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"); diff --git a/tests/sujiko.rs b/tests/sujiko.rs index 657cc86..a00e6be 100644 --- a/tests/sujiko.rs +++ b/tests/sujiko.rs @@ -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>) { +fn make_sujiko(board: &Board, tl: Val, tr: Val, bl: Val, br: Val) -> (Puzzle, Vec>) { 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::(), - 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::(), + 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>, 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"); diff --git a/tests/takuzu.rs b/tests/takuzu.rs index 8359bbb..97c6f68 100644 --- a/tests/takuzu.rs +++ b/tests/takuzu.rs @@ -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 + 'a> { + fn vars<'a>(&'a self) -> Box + '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::>(); @@ -45,8 +45,7 @@ impl Constraint for BinaryRepr { Ok(()) } - fn substitute(&self, _from: VarToken, _to: VarToken) - -> PsResult> { + fn substitute(&self, _from: VarToken, _to: VarToken) -> PsResult> { unimplemented!(); } } @@ -83,13 +82,16 @@ fn make_takuzu(puzzle: &Vec>) -> (Puzzle, Vec>) { 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>) -> (Puzzle, Vec>) { 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>) -> (Puzzle, Vec>) { // 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>, 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"); diff --git a/tests/xkcd.rs b/tests/xkcd.rs index e4bb547..851a0e1 100644 --- a/tests/xkcd.rs +++ b/tests/xkcd.rs @@ -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); diff --git a/tests/zebra.rs b/tests/zebra.rs index 6eaf41a..300cb84 100644 --- a/tests/zebra.rs +++ b/tests/zebra.rs @@ -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)]);