fix warnings
This commit is contained in:
parent
da22e1dfbb
commit
631d25cc3c
@ -12,10 +12,15 @@ keywords = ["constraint", "finite", "domain", "puzzle", "sudoku"]
|
||||
categories = ["science"]
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "run"
|
||||
path = "./bin/run.rs"
|
||||
|
||||
[dependencies]
|
||||
bit-set = "0.4"
|
||||
num-rational = { version = "0.1", default-features = false }
|
||||
num-traits = "0.1"
|
||||
thiserror = "1.0.30"
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "wangds/puzzle-solver" }
|
||||
|
@ -3,7 +3,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{Constraint, PsResult, PuzzleSearch, Val, VarToken};
|
||||
use crate::{Constraint, Error, PsResult, PuzzleSearch, Val, VarToken};
|
||||
|
||||
pub struct AllDifferent {
|
||||
vars: Vec<VarToken>,
|
||||
@ -38,7 +38,7 @@ impl Constraint for AllDifferent {
|
||||
|
||||
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));
|
||||
search.remove_candidate(var2, val)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -50,25 +50,30 @@ impl Constraint for AllDifferent {
|
||||
let mut all_candidates = HashMap::new();
|
||||
|
||||
for &var in self.vars.iter().filter(|&var| !search.is_assigned(*var)) {
|
||||
num_unassigned = num_unassigned + 1;
|
||||
num_unassigned += 1;
|
||||
|
||||
for val in search.get_unassigned(var) {
|
||||
if all_candidates.contains_key(&val) {
|
||||
all_candidates.insert(val, None);
|
||||
} else {
|
||||
all_candidates.insert(val, Some(var));
|
||||
match all_candidates.entry(val) {
|
||||
std::collections::hash_map::Entry::Occupied(mut e) => {
|
||||
e.insert(None);
|
||||
}
|
||||
std::collections::hash_map::Entry::Vacant(e) => {
|
||||
e.insert(Some(var));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if num_unassigned > all_candidates.len() {
|
||||
// More unassigned variables than candidates, contradiction.
|
||||
return Err(());
|
||||
} else if num_unassigned == all_candidates.len() {
|
||||
return Err(Error::Default);
|
||||
}
|
||||
|
||||
if num_unassigned == all_candidates.len() {
|
||||
// As many as variables as candidates.
|
||||
for (&val, &opt) in all_candidates.iter() {
|
||||
if let Some(var) = opt {
|
||||
r#try!(search.set_candidate(var, val));
|
||||
search.set_candidate(var, val)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,7 +90,7 @@ impl Constraint for AllDifferent {
|
||||
}
|
||||
}
|
||||
|
||||
Err(())
|
||||
Err(Error::Default)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use num_rational::Ratio;
|
||||
use num_traits::Zero;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{Constraint, LinExpr, PsResult, PuzzleSearch, Val, VarToken};
|
||||
use crate::{Constraint, Error, LinExpr, PsResult, PuzzleSearch, Val, VarToken};
|
||||
|
||||
pub struct Equality {
|
||||
// The equation: 0 = constant + coef1 * var1 + coef2 * var2 + ...
|
||||
@ -25,7 +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 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ impl Constraint for Equality {
|
||||
|
||||
for (&var, &coef) in self.eqn.coef.iter() {
|
||||
if let Some(val) = search.get_assigned(var) {
|
||||
sum = sum + coef * Ratio::from_integer(val);
|
||||
sum += coef * Ratio::from_integer(val);
|
||||
} else {
|
||||
// If we find more than one unassigned variable,
|
||||
// cannot assign any other variables.
|
||||
@ -57,14 +57,12 @@ impl Constraint for Equality {
|
||||
// sum + coef * var = 0.
|
||||
let val = -sum / coef;
|
||||
if val.is_integer() {
|
||||
r#try!(search.set_candidate(var, val.to_integer()));
|
||||
search.set_candidate(var, val.to_integer())?;
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
} else {
|
||||
if !sum.is_zero() {
|
||||
return Err(());
|
||||
return Err(Error::Default);
|
||||
}
|
||||
} else if !sum.is_zero() {
|
||||
return Err(Error::Default);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -75,13 +73,13 @@ impl Constraint for Equality {
|
||||
let mut sum_max = self.eqn.constant;
|
||||
|
||||
for (&var, &coef) in self.eqn.coef.iter() {
|
||||
let (min_val, max_val) = r#try!(search.get_min_max(var));
|
||||
let (min_val, max_val) = search.get_min_max(var)?;
|
||||
if coef > Ratio::zero() {
|
||||
sum_min = sum_min + coef * Ratio::from_integer(min_val);
|
||||
sum_max = sum_max + coef * Ratio::from_integer(max_val);
|
||||
sum_min += coef * Ratio::from_integer(min_val);
|
||||
sum_max += coef * Ratio::from_integer(max_val);
|
||||
} else {
|
||||
sum_min = sum_min + coef * Ratio::from_integer(max_val);
|
||||
sum_max = sum_max + coef * Ratio::from_integer(min_val);
|
||||
sum_min += coef * Ratio::from_integer(max_val);
|
||||
sum_max += coef * Ratio::from_integer(min_val);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,9 +89,10 @@ impl Constraint for Equality {
|
||||
let mut iters = self.eqn.coef.len();
|
||||
let mut iter = self.eqn.coef.iter().cycle();
|
||||
while iters > 0 {
|
||||
iters = iters - 1;
|
||||
iters -= 1;
|
||||
|
||||
if !(sum_min <= Ratio::zero() && Ratio::zero() <= sum_max) {
|
||||
return Err(());
|
||||
return Err(Error::Default);
|
||||
}
|
||||
|
||||
let (&var, &coef) = iter.next().expect("cycle");
|
||||
@ -101,7 +100,7 @@ impl Constraint for Equality {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (min_val, max_val) = r#try!(search.get_min_max(var));
|
||||
let (min_val, max_val) = search.get_min_max(var)?;
|
||||
let (min_bnd, max_bnd);
|
||||
|
||||
if coef > Ratio::zero() {
|
||||
@ -121,15 +120,14 @@ impl Constraint for Equality {
|
||||
}
|
||||
|
||||
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) = 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);
|
||||
sum_max = sum_max + coef * Ratio::from_integer(new_max - max_val);
|
||||
sum_min += coef * Ratio::from_integer(new_min - min_val);
|
||||
sum_max += coef * Ratio::from_integer(new_max - max_val);
|
||||
} else {
|
||||
sum_min = sum_min + coef * Ratio::from_integer(new_max - max_val);
|
||||
sum_max = sum_max + coef * Ratio::from_integer(new_min - min_val);
|
||||
sum_min += coef * Ratio::from_integer(new_max - max_val);
|
||||
sum_max += coef * Ratio::from_integer(new_min - min_val);
|
||||
}
|
||||
|
||||
iters = self.eqn.coef.len();
|
||||
@ -145,7 +143,7 @@ impl Constraint for Equality {
|
||||
eqn = eqn + coef * to;
|
||||
}
|
||||
|
||||
Ok(Rc::new(Equality { eqn: eqn }))
|
||||
Ok(Rc::new(Equality { eqn }))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,8 @@ impl Unify {
|
||||
/// ```
|
||||
pub fn new(var1: VarToken, var2: VarToken) -> Self {
|
||||
Unify {
|
||||
var1: var1,
|
||||
var2: var2,
|
||||
var1,
|
||||
var2,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,8 +53,8 @@ impl Constraint for Unify {
|
||||
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,
|
||||
var1,
|
||||
var2,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
13
src/error.rs
Normal file
13
src/error.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Default")]
|
||||
Default,
|
||||
}
|
||||
|
||||
impl From<()> for Error {
|
||||
fn from(_: ()) -> Self {
|
||||
Self::Default
|
||||
}
|
||||
}
|
22
src/lib.rs
22
src/lib.rs
@ -1,17 +1,20 @@
|
||||
//! This crate searches for the solutions to logic puzzles.
|
||||
//! The puzzle rules are expressed as constraints.
|
||||
|
||||
extern crate bit_set;
|
||||
extern crate num_rational;
|
||||
extern crate num_traits;
|
||||
pub mod constraint;
|
||||
|
||||
mod error;
|
||||
mod linexpr;
|
||||
mod puzzle;
|
||||
|
||||
use num_rational::Rational32;
|
||||
use std::collections::HashMap;
|
||||
use std::ops;
|
||||
|
||||
pub use crate::constraint::Constraint;
|
||||
pub use crate::puzzle::Puzzle;
|
||||
pub use crate::puzzle::PuzzleSearch;
|
||||
pub use constraint::Constraint;
|
||||
pub use error::Error;
|
||||
pub use puzzle::Puzzle;
|
||||
pub use puzzle::PuzzleSearch;
|
||||
|
||||
/// A puzzle variable token.
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
@ -39,7 +42,7 @@ pub struct LinExpr {
|
||||
}
|
||||
|
||||
/// A result during a puzzle solution search (Err = contradiction).
|
||||
pub type PsResult<T> = Result<T, ()>;
|
||||
pub type PsResult<T> = Result<T, Error>;
|
||||
|
||||
/// A dictionary mapping puzzle variables to the solution value.
|
||||
#[derive(Debug)]
|
||||
@ -47,11 +50,6 @@ pub struct Solution {
|
||||
vars: Vec<Val>,
|
||||
}
|
||||
|
||||
pub mod constraint;
|
||||
|
||||
mod linexpr;
|
||||
mod puzzle;
|
||||
|
||||
impl ops::Index<VarToken> for Solution {
|
||||
type Output = Val;
|
||||
fn index(&self, var: VarToken) -> &Val {
|
||||
|
@ -73,7 +73,7 @@ impl From<VarToken> for LinExpr {
|
||||
|
||||
LinExpr {
|
||||
constant: Ratio::zero(),
|
||||
coef: coef,
|
||||
coef,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,7 +141,7 @@ impl Neg for LinExpr {
|
||||
impl<T: IntoCoef> Add<T> for LinExpr {
|
||||
type Output = LinExpr;
|
||||
fn add(mut self, rhs: T) -> Self::Output {
|
||||
self.constant = self.constant + rhs.into_coef();
|
||||
self.constant += rhs.into_coef();
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -163,9 +163,9 @@ impl<T: IntoCoef> Mul<T> for LinExpr {
|
||||
} else {
|
||||
let rhs = rhs.into_coef();
|
||||
if rhs != Ratio::one() {
|
||||
self.constant = self.constant * rhs;
|
||||
self.constant *= rhs;
|
||||
for coef in self.coef.values_mut() {
|
||||
*coef = *coef * rhs;
|
||||
*coef *= rhs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,7 +200,7 @@ impl_subtract_op!(VarToken - LinExpr);
|
||||
impl Add for LinExpr {
|
||||
type Output = LinExpr;
|
||||
fn add(mut self, mut rhs: LinExpr) -> Self::Output {
|
||||
self.constant = self.constant + rhs.constant;
|
||||
self.constant += rhs.constant;
|
||||
|
||||
for (x2, a2) in rhs.coef.drain() {
|
||||
match self.coef.entry(x2) {
|
||||
@ -232,6 +232,7 @@ mod tests {
|
||||
use num_rational::Ratio;
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::identity_op, clippy::eq_op)]
|
||||
fn test_ops() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let x = puzzle.new_var();
|
||||
@ -289,6 +290,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::erasing_op, clippy::eq_op)]
|
||||
fn test_coef_zero() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let x = puzzle.new_var();
|
||||
|
228
src/puzzle.rs
228
src/puzzle.rs
@ -10,6 +10,7 @@ use std::ops;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::constraint;
|
||||
use crate::Error;
|
||||
use crate::{Constraint, LinExpr, PsResult, Solution, Val, VarToken};
|
||||
|
||||
/// A collection of candidates.
|
||||
@ -71,18 +72,18 @@ impl Candidates {
|
||||
/// Count the number of candidates for a variable.
|
||||
fn len(&self) -> usize {
|
||||
match self {
|
||||
&Candidates::None => 0,
|
||||
&Candidates::Value(_) => 1,
|
||||
&Candidates::Set(ref rc) => rc.len(),
|
||||
Candidates::None => 0,
|
||||
Candidates::Value(_) => 1,
|
||||
Candidates::Set(ref rc) => rc.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an iterator over all of the candidates of a variable.
|
||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = Val> + 'a> {
|
||||
match self {
|
||||
&Candidates::None => Box::new(iter::empty()),
|
||||
&Candidates::Value(val) => Box::new(iter::once(val)),
|
||||
&Candidates::Set(ref rc) => Box::new(rc.iter().cloned()),
|
||||
Candidates::None => Box::new(iter::empty()),
|
||||
Candidates::Value(val) => Box::new(iter::once(*val)),
|
||||
Candidates::Set(ref rc) => Box::new(rc.iter().cloned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,7 +118,7 @@ impl Puzzle {
|
||||
/// ```
|
||||
pub fn new_var(&mut self) -> VarToken {
|
||||
let var = VarToken(self.num_vars);
|
||||
self.num_vars = self.num_vars + 1;
|
||||
self.num_vars += 1;
|
||||
self.candidates.push(Candidates::None);
|
||||
var
|
||||
}
|
||||
@ -217,14 +218,14 @@ impl Puzzle {
|
||||
pub fn insert_candidates(&mut self, var: VarToken, candidates: &[Val]) {
|
||||
let VarToken(idx) = var;
|
||||
|
||||
match &self.candidates[idx] {
|
||||
&Candidates::Value(_) => panic!("attempt to set fixed variable"),
|
||||
match self.candidates[idx] {
|
||||
Candidates::Value(_) => panic!("attempt to set fixed variable"),
|
||||
|
||||
&Candidates::None => {
|
||||
Candidates::None => {
|
||||
self.candidates[idx] = Candidates::Set(Rc::new(BTreeSet::new()));
|
||||
}
|
||||
|
||||
&Candidates::Set(_) => (),
|
||||
Candidates::Set(_) => (),
|
||||
}
|
||||
|
||||
if let Candidates::Set(ref mut rc) = self.candidates[idx] {
|
||||
@ -454,6 +455,12 @@ impl Puzzle {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Puzzle {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
impl PuzzleConstraints {
|
||||
@ -462,7 +469,7 @@ impl PuzzleConstraints {
|
||||
let wake = Self::init_wake(&puzzle.constraints, puzzle.num_vars);
|
||||
PuzzleConstraints {
|
||||
constraints: puzzle.constraints.clone(),
|
||||
wake: wake,
|
||||
wake,
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,22 +480,22 @@ impl PuzzleConstraints {
|
||||
let mut new_constraints = self.constraints.clone();
|
||||
|
||||
for cidx in self.wake[idx].iter() {
|
||||
let rc = r#try!(self.constraints[cidx].substitute(from, to));
|
||||
let rc = self.constraints[cidx].substitute(from, to)?;
|
||||
new_constraints[cidx] = rc;
|
||||
}
|
||||
|
||||
let wake = Self::init_wake(&new_constraints, self.wake.len());
|
||||
Ok(PuzzleConstraints {
|
||||
constraints: new_constraints,
|
||||
wake: wake,
|
||||
wake,
|
||||
})
|
||||
}
|
||||
|
||||
/// Determine which variables wake up which constraints.
|
||||
fn init_wake(constraints: &Vec<Rc<dyn Constraint>>, num_vars: usize) -> Vec<BitSet> {
|
||||
fn init_wake(constraints: &[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() {
|
||||
for (cidx, constraint) in constraints.iter().enumerate() {
|
||||
for &VarToken(idx) in constraint.vars() {
|
||||
wake[idx].insert(cidx);
|
||||
}
|
||||
}
|
||||
@ -515,10 +522,10 @@ impl<'a> PuzzleSearch<'a> {
|
||||
}
|
||||
|
||||
PuzzleSearch {
|
||||
puzzle: puzzle,
|
||||
puzzle,
|
||||
constraints: Rc::new(constraints),
|
||||
vars: vars,
|
||||
wake: wake,
|
||||
vars,
|
||||
wake,
|
||||
}
|
||||
}
|
||||
|
||||
@ -526,9 +533,9 @@ impl<'a> PuzzleSearch<'a> {
|
||||
pub fn is_assigned(&self, var: VarToken) -> bool {
|
||||
let VarToken(idx) = var;
|
||||
match &self.vars[idx] {
|
||||
&VarState::Assigned(_) => true,
|
||||
&VarState::Unassigned(_) => false,
|
||||
&VarState::Unified(other) => self.is_assigned(other),
|
||||
VarState::Assigned(_) => true,
|
||||
VarState::Unassigned(_) => false,
|
||||
VarState::Unified(other) => self.is_assigned(*other),
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,9 +546,9 @@ impl<'a> PuzzleSearch<'a> {
|
||||
pub fn get_assigned(&self, var: VarToken) -> Option<Val> {
|
||||
let VarToken(idx) = var;
|
||||
match &self.vars[idx] {
|
||||
&VarState::Assigned(val) => Some(val),
|
||||
&VarState::Unassigned(_) => None,
|
||||
&VarState::Unified(other) => self.get_assigned(other),
|
||||
VarState::Assigned(val) => Some(*val),
|
||||
VarState::Unassigned(_) => None,
|
||||
VarState::Unified(other) => self.get_assigned(*other),
|
||||
}
|
||||
}
|
||||
|
||||
@ -549,9 +556,9 @@ impl<'a> PuzzleSearch<'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()),
|
||||
&VarState::Unassigned(ref cs) => cs.iter(),
|
||||
&VarState::Unified(other) => self.get_unassigned(other),
|
||||
VarState::Assigned(_) => Box::new(iter::empty()),
|
||||
VarState::Unassigned(ref cs) => cs.iter(),
|
||||
VarState::Unified(other) => self.get_unassigned(*other),
|
||||
}
|
||||
}
|
||||
|
||||
@ -559,20 +566,20 @@ impl<'a> PuzzleSearch<'a> {
|
||||
pub fn get_min_max(&self, var: VarToken) -> PsResult<(Val, Val)> {
|
||||
let VarToken(idx) = var;
|
||||
match &self.vars[idx] {
|
||||
&VarState::Assigned(val) => Ok((val, val)),
|
||||
&VarState::Unassigned(ref cs) => match cs {
|
||||
&Candidates::None => Err(()),
|
||||
&Candidates::Value(val) => Ok((val, val)),
|
||||
&Candidates::Set(ref rc) => rc
|
||||
VarState::Assigned(val) => Ok((*val, *val)),
|
||||
VarState::Unassigned(ref cs) => match cs {
|
||||
Candidates::None => Err(Error::Default),
|
||||
Candidates::Value(val) => Ok((*val, *val)),
|
||||
Candidates::Set(ref rc) => rc
|
||||
.iter()
|
||||
.cloned()
|
||||
.min()
|
||||
.into_iter()
|
||||
.zip(rc.iter().cloned().max())
|
||||
.next()
|
||||
.ok_or(()),
|
||||
.ok_or(Error::Default),
|
||||
},
|
||||
&VarState::Unified(other) => self.get_min_max(other),
|
||||
VarState::Unified(other) => self.get_min_max(*other),
|
||||
}
|
||||
}
|
||||
|
||||
@ -581,26 +588,26 @@ impl<'a> PuzzleSearch<'a> {
|
||||
let VarToken(idx) = var;
|
||||
|
||||
match &self.vars[idx] {
|
||||
&VarState::Assigned(v) => return bool_to_result(v == val),
|
||||
&VarState::Unassigned(ref cs) => match cs {
|
||||
&Candidates::None => return Err(()),
|
||||
&Candidates::Value(v) => return bool_to_result(v == val),
|
||||
&Candidates::Set(_) => (),
|
||||
VarState::Assigned(v) => return bool_to_result(*v == val),
|
||||
VarState::Unassigned(ref cs) => match cs {
|
||||
Candidates::None => return Err(Error::Default),
|
||||
Candidates::Value(v) => return bool_to_result(*v == val),
|
||||
Candidates::Set(_) => (),
|
||||
},
|
||||
&VarState::Unified(_) => (),
|
||||
VarState::Unified(_) => (),
|
||||
}
|
||||
|
||||
if let &VarState::Unified(other) = &self.vars[idx] {
|
||||
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 VarState::Unassigned(Candidates::Set(ref mut rc)) = self.vars[idx] {
|
||||
if rc.contains(&val) {
|
||||
let mut set = Rc::make_mut(rc);
|
||||
let set = Rc::make_mut(rc);
|
||||
set.clear();
|
||||
set.insert(val);
|
||||
self.wake.union_with(&self.constraints.wake[idx]);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
Err(Error::Default)
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
@ -612,20 +619,20 @@ impl<'a> PuzzleSearch<'a> {
|
||||
let VarToken(idx) = var;
|
||||
|
||||
match &self.vars[idx] {
|
||||
&VarState::Assigned(v) => return bool_to_result(v != val),
|
||||
&VarState::Unassigned(ref cs) => match cs {
|
||||
&Candidates::None => return Err(()),
|
||||
&Candidates::Value(v) => return bool_to_result(v != val),
|
||||
&Candidates::Set(_) => (),
|
||||
VarState::Assigned(v) => return bool_to_result(*v != val),
|
||||
VarState::Unassigned(ref cs) => match cs {
|
||||
Candidates::None => return Err(Error::Default),
|
||||
Candidates::Value(v) => return bool_to_result(*v != val),
|
||||
Candidates::Set(_) => (),
|
||||
},
|
||||
&VarState::Unified(_) => (),
|
||||
VarState::Unified(_) => (),
|
||||
}
|
||||
|
||||
if let &VarState::Unified(other) = &self.vars[idx] {
|
||||
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 VarState::Unassigned(Candidates::Set(ref mut rc)) = self.vars[idx] {
|
||||
if rc.contains(&val) {
|
||||
let mut set = Rc::make_mut(rc);
|
||||
let set = Rc::make_mut(rc);
|
||||
set.remove(&val);
|
||||
self.wake.union_with(&self.constraints.wake[idx]);
|
||||
}
|
||||
@ -644,51 +651,49 @@ impl<'a> PuzzleSearch<'a> {
|
||||
) -> PsResult<(Val, Val)> {
|
||||
let VarToken(idx) = var;
|
||||
|
||||
match &self.vars[idx] {
|
||||
&VarState::Assigned(v) => {
|
||||
match self.vars[idx] {
|
||||
VarState::Assigned(v) => {
|
||||
if min <= v && v <= max {
|
||||
return Ok((v, v));
|
||||
} else {
|
||||
return Err(());
|
||||
return Err(Error::Default);
|
||||
}
|
||||
}
|
||||
&VarState::Unassigned(ref cs) => match cs {
|
||||
&Candidates::None => return Err(()),
|
||||
&Candidates::Value(v) => {
|
||||
if min <= v && v <= max {
|
||||
return Ok((v, v));
|
||||
VarState::Unassigned(ref cs) => match cs {
|
||||
Candidates::None => return Err(Error::Default),
|
||||
Candidates::Value(v) => {
|
||||
if min <= *v && *v <= max {
|
||||
return Ok((*v, *v));
|
||||
} else {
|
||||
return Err(());
|
||||
return Err(Error::Default);
|
||||
}
|
||||
}
|
||||
&Candidates::Set(_) => (),
|
||||
Candidates::Set(_) => (),
|
||||
},
|
||||
&VarState::Unified(_) => (),
|
||||
VarState::Unified(_) => (),
|
||||
}
|
||||
|
||||
if let &VarState::Unified(other) = &self.vars[idx] {
|
||||
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 VarState::Unassigned(Candidates::Set(ref mut rc)) = 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()
|
||||
.filter(|&val| min <= *val && *val <= max)
|
||||
.cloned()
|
||||
.collect();
|
||||
self.wake.union_with(&self.constraints.wake[idx]);
|
||||
}
|
||||
let set = Rc::make_mut(rc);
|
||||
*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()
|
||||
.ok_or(())
|
||||
.ok_or(Error::Default)
|
||||
} else {
|
||||
Ok((curr_min, curr_max))
|
||||
}
|
||||
@ -708,7 +713,7 @@ impl<'a> PuzzleSearch<'a> {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.min_by_key(|&(_, vs)| match vs {
|
||||
&VarState::Unassigned(ref cs) => cs.len(),
|
||||
VarState::Unassigned(ref cs) => cs.len(),
|
||||
_ => ::std::usize::MAX,
|
||||
});
|
||||
|
||||
@ -738,7 +743,7 @@ impl<'a> PuzzleSearch<'a> {
|
||||
let vars = (0..self.puzzle.num_vars)
|
||||
.map(|idx| self[VarToken(idx)])
|
||||
.collect();
|
||||
solutions.push(Solution { vars: vars });
|
||||
solutions.push(Solution { vars });
|
||||
}
|
||||
}
|
||||
|
||||
@ -751,7 +756,7 @@ impl<'a> PuzzleSearch<'a> {
|
||||
for cidx in 0..self.constraints.constraints.len() {
|
||||
if self.constraints.wake[idx].contains(cidx) {
|
||||
let constraint = self.constraints.constraints[cidx].clone();
|
||||
r#try!(constraint.on_assigned(self, var, val));
|
||||
constraint.on_assigned(self, var, val)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -770,18 +775,18 @@ impl<'a> PuzzleSearch<'a> {
|
||||
let mut idx = 0;
|
||||
let mut last_gimme = cycle - 1;
|
||||
loop {
|
||||
let gimme = match &self.vars[idx] {
|
||||
&VarState::Assigned(_) => None,
|
||||
&VarState::Unassigned(ref cs) => match cs.len() {
|
||||
0 => return Err(()),
|
||||
let gimme = match self.vars[idx] {
|
||||
VarState::Assigned(_) => None,
|
||||
VarState::Unassigned(ref cs) => match cs.len() {
|
||||
0 => return Err(Error::Default),
|
||||
1 => cs.iter().next(),
|
||||
_ => None,
|
||||
},
|
||||
&VarState::Unified(_) => None,
|
||||
VarState::Unified(_) => None,
|
||||
};
|
||||
|
||||
if let Some(val) = gimme {
|
||||
r#try!(self.assign(idx, val));
|
||||
self.assign(idx, val)?;
|
||||
last_gimme = idx;
|
||||
} else if idx == last_gimme {
|
||||
break;
|
||||
@ -795,7 +800,7 @@ impl<'a> PuzzleSearch<'a> {
|
||||
let wake = mem::replace(&mut self.wake, BitSet::new());
|
||||
for cidx in wake.iter() {
|
||||
let constraint = self.constraints.constraints[cidx].clone();
|
||||
r#try!(constraint.on_updated(self));
|
||||
constraint.on_updated(self)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -835,24 +840,22 @@ impl<'a> PuzzleSearch<'a> {
|
||||
let VarToken(replace) = to;
|
||||
|
||||
// Create new constraints to reflect the unification.
|
||||
let new_constraints = r#try!(self.constraints.substitute(from, to));
|
||||
let new_constraints = self.constraints.substitute(from, to)?;
|
||||
self.constraints = Rc::new(new_constraints);
|
||||
self.wake.union_with(&self.constraints.wake[replace]);
|
||||
assert!(self.constraints.wake[search].is_empty());
|
||||
|
||||
// Take intersection of the candidates.
|
||||
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)
|
||||
{
|
||||
*rc2 = Rc::new(rc2.intersection(rc1).cloned().collect());
|
||||
if rc2.is_empty() {
|
||||
return Err(());
|
||||
}
|
||||
if let VarState::Assigned(val) = self.vars[search] {
|
||||
self.set_candidate(to, val)?;
|
||||
} else if let (
|
||||
VarState::Unassigned(Candidates::Set(ref mut rc1)),
|
||||
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(Error::Default);
|
||||
}
|
||||
}
|
||||
|
||||
@ -865,18 +868,19 @@ impl<'a> fmt::Debug for PuzzleSearch<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "PuzzleSearch={{")?;
|
||||
for (idx, var) in self.vars.iter().enumerate() {
|
||||
writeln!(f, "")?;
|
||||
writeln!(f)?;
|
||||
|
||||
match var {
|
||||
&VarState::Assigned(val) => {
|
||||
VarState::Assigned(val) => {
|
||||
write!(f, " var {}: {}", idx, val)?;
|
||||
}
|
||||
&VarState::Unassigned(ref cs) => {
|
||||
VarState::Unassigned(ref cs) => {
|
||||
write!(f, " var {}:", idx)?;
|
||||
for val in cs.iter() {
|
||||
write!(f, " {}", val)?;
|
||||
}
|
||||
}
|
||||
&VarState::Unified(VarToken(other)) => {
|
||||
VarState::Unified(VarToken(other)) => {
|
||||
write!(f, " var {}: var {}", idx, other)?;
|
||||
}
|
||||
}
|
||||
@ -896,10 +900,10 @@ impl<'a> ops::Index<VarToken> for PuzzleSearch<'a> {
|
||||
/// Panics if the variable has not been assigned.
|
||||
fn index(&self, var: VarToken) -> &Val {
|
||||
let VarToken(idx) = var;
|
||||
match &self.vars[idx] {
|
||||
&VarState::Assigned(ref val) => val,
|
||||
&VarState::Unassigned(_) => panic!("unassigned"),
|
||||
&VarState::Unified(other) => &self[other],
|
||||
match self.vars[idx] {
|
||||
VarState::Assigned(ref val) => val,
|
||||
VarState::Unassigned(_) => panic!("unassigned"),
|
||||
VarState::Unified(other) => &self[other],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -908,14 +912,14 @@ fn bool_to_result(cond: bool) -> PsResult<()> {
|
||||
if cond {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
Err(Error::Default)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_two_mut<'a, T>(slice: &'a mut [T], a: usize, b: usize) -> (&'a mut T, &'a mut T) {
|
||||
fn get_two_mut<T>(slice: &mut [T], a: usize, b: usize) -> (&mut T, &mut T) {
|
||||
assert!(a != b);
|
||||
if a < b {
|
||||
let (mut l, mut r) = slice.split_at_mut(b);
|
||||
let (l, r) = slice.split_at_mut(b);
|
||||
(&mut l[a], &mut r[0])
|
||||
} else {
|
||||
let (l, r) = slice.split_at_mut(a);
|
||||
|
@ -20,7 +20,7 @@ fn make_hidato(board: &Board) -> (Puzzle, Vec<VarToken>) {
|
||||
for x in 0..WIDTH {
|
||||
if board[y][x] != NA {
|
||||
pos.push((WIDTH * y + x) as Val);
|
||||
count = count + 1;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,7 +58,7 @@ fn make_hidato(board: &Board) -> (Puzzle, Vec<VarToken>) {
|
||||
(sys, vars)
|
||||
}
|
||||
|
||||
fn print_hidato(dict: &Solution, vars: &Vec<VarToken>) {
|
||||
fn print_hidato(dict: &Solution, vars: &[VarToken]) {
|
||||
let mut board = [[NA; WIDTH]; HEIGHT];
|
||||
|
||||
for (idx, &var) in vars.iter().enumerate() {
|
||||
@ -79,7 +79,7 @@ fn print_hidato(dict: &Solution, vars: &Vec<VarToken>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_hidato(dict: &Solution, vars: &Vec<VarToken>, expected: &Board) {
|
||||
fn verify_hidato(dict: &Solution, vars: &[VarToken], expected: &Board) {
|
||||
for (idx, &var) in vars.iter().enumerate() {
|
||||
let x = (dict[var] as usize) % WIDTH;
|
||||
let y = (dict[var] as usize) / WIDTH;
|
||||
|
@ -41,7 +41,7 @@ fn make_killer_sudoku(board: &[(Val, Vec<Point>)]) -> (Puzzle, Vec<Vec<VarToken>
|
||||
(sys, vars)
|
||||
}
|
||||
|
||||
fn print_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
fn print_sudoku(dict: &Solution, vars: &[Vec<VarToken>]) {
|
||||
for y in 0..SIZE {
|
||||
if y % SQRT_SIZE == 0 {
|
||||
println!();
|
||||
@ -49,7 +49,7 @@ fn print_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
|
||||
for x in 0..SIZE {
|
||||
print!(
|
||||
"{}{}",
|
||||
"{} {}",
|
||||
if x % SQRT_SIZE == 0 { " " } else { "" },
|
||||
dict[vars[y][x]]
|
||||
);
|
||||
@ -58,7 +58,7 @@ fn print_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>, expected: &Board) {
|
||||
fn verify_sudoku(dict: &Solution, vars: &[Vec<VarToken>], expected: &Board) {
|
||||
for y in 0..SIZE {
|
||||
for x in 0..SIZE {
|
||||
assert_eq!(dict[vars[y][x]], expected[y][x]);
|
||||
|
@ -16,7 +16,7 @@ fn make_magic_square(n: usize) -> (Puzzle, Vec<Vec<VarToken>>, VarToken) {
|
||||
let max = digits.iter().rev().take(n).sum();
|
||||
let total = sys.new_var_with_candidates(&(min..max).collect::<Vec<Val>>());
|
||||
|
||||
sys.all_different(vars.iter().flat_map(|it| it));
|
||||
sys.all_different(vars.iter().flatten());
|
||||
|
||||
for y in 0..n {
|
||||
sys.equals(
|
||||
@ -49,7 +49,7 @@ fn make_magic_square(n: usize) -> (Puzzle, Vec<Vec<VarToken>>, VarToken) {
|
||||
(sys, vars, total)
|
||||
}
|
||||
|
||||
fn print_magic_square(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
fn print_magic_square(dict: &Solution, vars: &[Vec<VarToken>]) {
|
||||
for row in vars.iter() {
|
||||
for &var in row.iter() {
|
||||
print!(" {:2}", dict[var]);
|
||||
|
@ -38,10 +38,10 @@ enum AutoFillResult {
|
||||
}
|
||||
|
||||
impl Nonogram {
|
||||
fn new(vars: &Vec<VarToken>, rule: &Vec<usize>) -> Self {
|
||||
fn new(vars: &[VarToken], rule: &[usize]) -> Self {
|
||||
Nonogram {
|
||||
vars: vars.clone(),
|
||||
rule: rule.clone(),
|
||||
vars: vars.to_vec(),
|
||||
rule: rule.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +58,7 @@ impl Nonogram {
|
||||
|
||||
// Base case.
|
||||
if pos == trial.len() || cache.contains_key(&key) {
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
if pos == trial.len() && rule_idx != self.rule.len() {
|
||||
return AutoFillResult::Conflict;
|
||||
} else if let Some(&AutoFillResult::Conflict) = cache.get(&key) {
|
||||
@ -65,7 +66,7 @@ impl Nonogram {
|
||||
}
|
||||
|
||||
for (a, t) in accum[0..pos].iter_mut().zip(trial) {
|
||||
*a = *a | *t;
|
||||
*a |= *t;
|
||||
}
|
||||
|
||||
if accum
|
||||
@ -118,7 +119,7 @@ impl Nonogram {
|
||||
} else if trial[pos] >= FLAG_UNKNOWN {
|
||||
trial[pos] = FLAG_UNKNOWN | FLAG_ON;
|
||||
}
|
||||
pos = pos + 1;
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
if ok && pos < trial.len() {
|
||||
@ -126,7 +127,7 @@ impl Nonogram {
|
||||
ok = false;
|
||||
} else if trial[pos] >= FLAG_UNKNOWN {
|
||||
trial[pos] = FLAG_UNKNOWN | FLAG_OFF;
|
||||
pos = pos + 1;
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +152,7 @@ impl Constraint for Nonogram {
|
||||
|
||||
fn on_updated(&self, search: &mut PuzzleSearch) -> PsResult<()> {
|
||||
let mut trial = vec![0; self.vars.len()];
|
||||
for (mut pos, &var) in trial.iter_mut().zip(&self.vars) {
|
||||
for (pos, &var) in trial.iter_mut().zip(&self.vars) {
|
||||
*pos = match search.get_assigned(var) {
|
||||
Some(0) => FLAG_OFF,
|
||||
Some(1) => FLAG_ON,
|
||||
@ -162,14 +163,14 @@ impl Constraint for Nonogram {
|
||||
let mut accum = trial.clone();
|
||||
let mut cache = HashMap::new();
|
||||
match self.autofill(&mut trial, 0, 0, &mut accum, &mut cache) {
|
||||
AutoFillResult::Conflict => return Err(()),
|
||||
AutoFillResult::Conflict => return Err(Error::Default),
|
||||
AutoFillResult::SearchEnded => (),
|
||||
AutoFillResult::SolutionFound => {
|
||||
for idx in 0..self.vars.len() {
|
||||
for (idx, var) in self.vars.iter().copied().enumerate() {
|
||||
if accum[idx] == FLAG_UNKNOWN | FLAG_OFF {
|
||||
r#try!(search.set_candidate(self.vars[idx], 0));
|
||||
search.set_candidate(var, 0)?;
|
||||
} else if accum[idx] == FLAG_UNKNOWN | FLAG_ON {
|
||||
r#try!(search.set_candidate(self.vars[idx], 1));
|
||||
search.set_candidate(var, 1)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,16 +196,14 @@ fn make_nonogram(rows: &[Vec<usize>], cols: &[Vec<usize>]) -> (Puzzle, Vec<Vec<V
|
||||
}
|
||||
|
||||
for x in 0..w {
|
||||
sys.add_constraint(Nonogram::new(
|
||||
&vars.iter().map(|row| row[x]).collect(),
|
||||
&cols[x],
|
||||
));
|
||||
let tmp: Vec<_> = vars.iter().map(|row| row[x]).collect();
|
||||
sys.add_constraint(Nonogram::new(&tmp, &cols[x]));
|
||||
}
|
||||
|
||||
(sys, vars)
|
||||
}
|
||||
|
||||
fn print_nonogram(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
fn print_nonogram(dict: &Solution, vars: &[Vec<VarToken>]) {
|
||||
for row in vars.iter() {
|
||||
for &var in row.iter() {
|
||||
print!("{}", if dict[var] == 1 { "*" } else { "." });
|
||||
@ -213,7 +212,7 @@ fn print_nonogram(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_nonogram(dict: &Solution, vars: &Vec<Vec<VarToken>>, expected: &Board) {
|
||||
fn verify_nonogram(dict: &Solution, vars: &[Vec<VarToken>], expected: &Board) {
|
||||
for y in 0..HEIGHT {
|
||||
for x in 0..WIDTH {
|
||||
assert_eq!(dict[vars[y][x]], expected[y][x]);
|
||||
|
@ -26,8 +26,8 @@ impl Constraint for NoDiagonal {
|
||||
if !search.is_assigned(var2) {
|
||||
let x1 = val;
|
||||
let dy = (y1 as Val) - (y2 as Val);
|
||||
r#try!(search.remove_candidate(var2, x1 - dy));
|
||||
r#try!(search.remove_candidate(var2, x1 + dy));
|
||||
search.remove_candidate(var2, x1 - dy)?;
|
||||
search.remove_candidate(var2, x1 + dy)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ fn make_queens(n: usize) -> (Puzzle, Vec<VarToken>) {
|
||||
(sys, vars)
|
||||
}
|
||||
|
||||
fn print_queens(dict: &Solution, vars: &Vec<VarToken>) {
|
||||
fn print_queens(dict: &Solution, vars: &[VarToken]) {
|
||||
let n = vars.len() as Val;
|
||||
for &var in vars.iter() {
|
||||
for i in 0..n {
|
||||
|
@ -48,6 +48,7 @@ fn make_samurai_sudoku(board: &Board) -> (Puzzle, SamuraiVars) {
|
||||
let br = make_sudoku(&mut sys);
|
||||
let mid = make_sudoku(&mut sys);
|
||||
|
||||
#[allow(clippy::erasing_op)]
|
||||
for y in 0..SQRT_SIZE {
|
||||
for x in 0..SQRT_SIZE {
|
||||
sys.unify(
|
||||
@ -92,13 +93,13 @@ 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 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 gap = || print!(" ");
|
||||
let gap = || print!(" ");
|
||||
|
||||
for i in 0..SIZE {
|
||||
pr9(&tl[i]);
|
||||
|
@ -25,7 +25,7 @@ fn make_send_more_money() -> (Puzzle, Vec<VarToken>) {
|
||||
(sys, vars)
|
||||
}
|
||||
|
||||
fn print_send_more_money(dict: &Solution, vars: &Vec<VarToken>) {
|
||||
fn print_send_more_money(dict: &Solution, vars: &[VarToken]) {
|
||||
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]);
|
||||
|
||||
@ -38,7 +38,7 @@ fn print_send_more_money(dict: &Solution, vars: &Vec<VarToken>) {
|
||||
);
|
||||
}
|
||||
|
||||
fn verify_send_more_money(dict: &Solution, vars: &Vec<VarToken>) {
|
||||
fn verify_send_more_money(dict: &Solution, vars: &[VarToken]) {
|
||||
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]);
|
||||
|
||||
|
@ -40,7 +40,7 @@ fn make_sudoku(board: &Board) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
(sys, vars)
|
||||
}
|
||||
|
||||
fn print_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
fn print_sudoku(dict: &Solution, vars: &[Vec<VarToken>]) {
|
||||
for y in 0..SIZE {
|
||||
if y % SQRT_SIZE == 0 {
|
||||
println!();
|
||||
@ -48,7 +48,7 @@ fn print_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
|
||||
for x in 0..SIZE {
|
||||
print!(
|
||||
"{}{}",
|
||||
"{} {}",
|
||||
if x % SQRT_SIZE == 0 { " " } else { "" },
|
||||
dict[vars[y][x]]
|
||||
);
|
||||
@ -57,7 +57,7 @@ fn print_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_sudoku(dict: &Solution, vars: &Vec<Vec<VarToken>>, expected: &Board) {
|
||||
fn verify_sudoku(dict: &Solution, vars: &[Vec<VarToken>], expected: &Board) {
|
||||
for y in 0..SIZE {
|
||||
for x in 0..SIZE {
|
||||
assert_eq!(dict[vars[y][x]], expected[y][x]);
|
||||
|
@ -14,7 +14,7 @@ fn make_sujiko(board: &Board, tl: Val, tr: Val, bl: Val, br: Val) -> (Puzzle, Ve
|
||||
let mut sys = Puzzle::new();
|
||||
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));
|
||||
sys.all_different(vars.iter().flatten());
|
||||
|
||||
sys.equals(tl, vars[0][0] + vars[0][1] + vars[1][0] + vars[1][1]);
|
||||
sys.equals(tr, vars[0][1] + vars[0][2] + vars[1][1] + vars[1][2]);
|
||||
@ -38,7 +38,7 @@ fn make_sujiko(board: &Board, tl: Val, tr: Val, bl: Val, br: Val) -> (Puzzle, Ve
|
||||
(sys, vars)
|
||||
}
|
||||
|
||||
fn print_sujiko(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
fn print_sujiko(dict: &Solution, vars: &[Vec<VarToken>]) {
|
||||
for y in 0..SIZE {
|
||||
for x in 0..SIZE {
|
||||
print!(" {}", dict[vars[y][x]]);
|
||||
@ -47,7 +47,7 @@ fn print_sujiko(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_sujiko(dict: &Solution, vars: &Vec<Vec<VarToken>>, expected: &Board) {
|
||||
fn verify_sujiko(dict: &Solution, vars: &[Vec<VarToken>], expected: &Board) {
|
||||
for y in 0..SIZE {
|
||||
for x in 0..SIZE {
|
||||
assert_eq!(dict[vars[y][x]], expected[y][x]);
|
||||
|
@ -27,8 +27,8 @@ impl Constraint for BinaryRepr {
|
||||
if var == self.value {
|
||||
let mut val = val;
|
||||
for &var in self.bits.iter() {
|
||||
r#try!(search.set_candidate(var, val & 1));
|
||||
val = val >> 1;
|
||||
search.set_candidate(var, val & 1)?;
|
||||
val >>= 1;
|
||||
}
|
||||
} else if let Some(bitpos) = self.bits.iter().position(|&v| v == var) {
|
||||
let bit = 1 << bitpos;
|
||||
@ -38,7 +38,7 @@ impl Constraint for BinaryRepr {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for c in discard.into_iter() {
|
||||
r#try!(search.remove_candidate(self.value, c));
|
||||
search.remove_candidate(self.value, c)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ fn make_sums(size: usize) -> Vec<Val> {
|
||||
vec
|
||||
}
|
||||
|
||||
fn make_takuzu(puzzle: &Vec<Vec<Val>>) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
fn make_takuzu(puzzle: &[Vec<Val>]) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
let height = puzzle.len();
|
||||
assert!(height > 0 && height % 2 == 0);
|
||||
let width = puzzle[0].len();
|
||||
@ -142,7 +142,7 @@ fn make_takuzu(puzzle: &Vec<Vec<Val>>) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
(sys, vars)
|
||||
}
|
||||
|
||||
fn print_takuzu(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
fn print_takuzu(dict: &Solution, vars: &[Vec<VarToken>]) {
|
||||
for row in vars.iter() {
|
||||
let sum = row.iter().fold(0, |sum, &var| 2 * sum + dict[var]);
|
||||
for &var in row.iter() {
|
||||
@ -152,7 +152,7 @@ fn print_takuzu(dict: &Solution, vars: &Vec<Vec<VarToken>>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_takuzu(dict: &Solution, vars: &Vec<Vec<VarToken>>, expected: &[Val]) {
|
||||
fn verify_takuzu(dict: &Solution, vars: &[Vec<VarToken>], expected: &[Val]) {
|
||||
for (row, &expected) in vars.iter().zip(expected) {
|
||||
let sum = row.iter().fold(0, |sum, &var| 2 * sum + dict[var]);
|
||||
assert_eq!(sum, expected);
|
||||
@ -196,18 +196,18 @@ fn takuzu_grid2() {
|
||||
];
|
||||
|
||||
let expected = [
|
||||
0b_010101101001,
|
||||
0b_010101001011,
|
||||
0b_101010110100,
|
||||
0b_100100110011,
|
||||
0b_011011001100,
|
||||
0b_010010110011,
|
||||
0b_101100101010,
|
||||
0b_001101001101,
|
||||
0b_110010010110,
|
||||
0b_010101101010,
|
||||
0b_101010010101,
|
||||
0b_101011010100,
|
||||
0b_0101_0110_1001,
|
||||
0b_0101_0100_1011,
|
||||
0b_1010_1011_0100,
|
||||
0b_1001_0011_0011,
|
||||
0b_0110_1100_1100,
|
||||
0b_0100_1011_0011,
|
||||
0b_1011_0010_1010,
|
||||
0b_0011_0100_1101,
|
||||
0b_1100_1001_0110,
|
||||
0b_0101_0110_1010,
|
||||
0b_1010_1001_0101,
|
||||
0b_1010_1101_0100,
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_takuzu(&puzzle);
|
||||
@ -235,18 +235,18 @@ fn takuzu_grid3() {
|
||||
];
|
||||
|
||||
let expected = [
|
||||
0b_101010011001,
|
||||
0b_110010010011,
|
||||
0b_001101101100,
|
||||
0b_101101001010,
|
||||
0b_010010110011,
|
||||
0b_010010110101,
|
||||
0b_101101001100,
|
||||
0b_101101010010,
|
||||
0b_010010101101,
|
||||
0b_011001011001,
|
||||
0b_100110100110,
|
||||
0b_010101100110,
|
||||
0b_1010_1001_1001,
|
||||
0b_1100_1001_0011,
|
||||
0b_0011_0110_1100,
|
||||
0b_1011_0100_1010,
|
||||
0b_0100_1011_0011,
|
||||
0b_0100_1011_0101,
|
||||
0b_1011_0100_1100,
|
||||
0b_1011_0101_0010,
|
||||
0b_0100_1010_1101,
|
||||
0b_0110_0101_1001,
|
||||
0b_1001_1010_0110,
|
||||
0b_0101_0110_0110,
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_takuzu(&puzzle);
|
||||
@ -274,7 +274,7 @@ fn takuzu_grid4() {
|
||||
];
|
||||
|
||||
let (mut sys, vars) = make_takuzu(&puzzle);
|
||||
let dict = &sys.solve_any().expect("solution");
|
||||
let dict = sys.solve_any().expect("solution");
|
||||
print_takuzu(&dict, &vars);
|
||||
println!("takuzu_grid4: {} guesses", sys.num_guesses());
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ fn xkcd_knapsack() {
|
||||
assert_eq!(solutions.len(), 2);
|
||||
|
||||
for dict in solutions.iter() {
|
||||
println!("");
|
||||
println!();
|
||||
for (&var, &(cost, string)) in vars.iter().zip(menu.iter()) {
|
||||
let numer = cost.numer().to_f32().unwrap();
|
||||
let denom = cost.denom().to_f32().unwrap();
|
||||
|
Loading…
Reference in New Issue
Block a user