fix warnings

This commit is contained in:
Andrey Tkachenko 2021-12-08 11:17:32 +04:00
parent da22e1dfbb
commit 631d25cc3c
19 changed files with 263 additions and 238 deletions

View File

@ -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" }

View File

@ -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)
}
}

View File

@ -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 }))
}
}

View File

@ -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
View 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
}
}

View File

@ -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 {

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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]);

View File

@ -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]);

View File

@ -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]);

View File

@ -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 {

View File

@ -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]);

View File

@ -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]);

View File

@ -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]);

View File

@ -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]);

View File

@ -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());
}

View File

@ -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();