Only wake affected constraints.
This commit is contained in:
parent
6eb25f6af0
commit
0477a5bbb0
@ -27,13 +27,12 @@ impl AllDifferent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Constraint for AllDifferent {
|
impl Constraint for AllDifferent {
|
||||||
fn on_assigned(&self, search: &mut PuzzleSearch, var: VarToken, val: Val)
|
fn vars<'a>(&'a self) -> Box<Iterator<Item=&'a VarToken> + 'a> {
|
||||||
-> bool {
|
Box::new(self.vars.iter())
|
||||||
// TODO: constraints should only be called if affected variables are modified.
|
}
|
||||||
if !self.vars.iter().any(|&v| v == var) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fn on_assigned(&self, search: &mut PuzzleSearch, _var: VarToken, val: Val)
|
||||||
|
-> bool {
|
||||||
for &var2 in self.vars.iter() {
|
for &var2 in self.vars.iter() {
|
||||||
if !search.is_assigned(var2) {
|
if !search.is_assigned(var2) {
|
||||||
search.remove_candidate(var2, val);
|
search.remove_candidate(var2, val);
|
||||||
|
@ -9,6 +9,9 @@ use ::{PuzzleSearch,Val,VarToken};
|
|||||||
|
|
||||||
/// Constraint trait.
|
/// Constraint trait.
|
||||||
pub trait Constraint {
|
pub trait Constraint {
|
||||||
|
/// An iterator over the variables that are involved in the constraint.
|
||||||
|
fn vars<'a>(&'a self) -> Box<Iterator<Item=&'a VarToken> + 'a>;
|
||||||
|
|
||||||
/// Applied after a variable has been assigned.
|
/// Applied after a variable has been assigned.
|
||||||
///
|
///
|
||||||
/// Returns true if the search should continue with these variable
|
/// Returns true if the search should continue with these variable
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
use std::mem;
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use bit_set::BitSet;
|
||||||
|
|
||||||
use ::{Constraint,Solution,Val,VarToken};
|
use ::{Constraint,Solution,Val,VarToken};
|
||||||
use constraint;
|
use constraint;
|
||||||
@ -37,6 +39,10 @@ pub struct Puzzle {
|
|||||||
|
|
||||||
// The list of puzzle constraints.
|
// The list of puzzle constraints.
|
||||||
constraints: Vec<Box<Constraint>>,
|
constraints: Vec<Box<Constraint>>,
|
||||||
|
|
||||||
|
// The list of constraints that each variable belongs to. These
|
||||||
|
// will be woken up when the variable's candidates are changed.
|
||||||
|
wake: Vec<BitSet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Intermediate puzzle search state.
|
/// Intermediate puzzle search state.
|
||||||
@ -45,9 +51,8 @@ pub struct PuzzleSearch<'a> {
|
|||||||
puzzle: &'a Puzzle,
|
puzzle: &'a Puzzle,
|
||||||
vars: Vec<VarState>,
|
vars: Vec<VarState>,
|
||||||
|
|
||||||
// A variable to be marked by constraints when updating the list
|
// The list of constraints that need to be re-evaluated.
|
||||||
// of candidates.
|
wake: BitSet,
|
||||||
modified: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------*/
|
/*--------------------------------------------------------------*/
|
||||||
@ -88,6 +93,7 @@ impl Puzzle {
|
|||||||
num_guesses: Cell::new(0),
|
num_guesses: Cell::new(0),
|
||||||
candidates: Vec::new(),
|
candidates: Vec::new(),
|
||||||
constraints: Vec::new(),
|
constraints: Vec::new(),
|
||||||
|
wake: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +110,7 @@ impl Puzzle {
|
|||||||
let var = VarToken(self.num_vars);
|
let var = VarToken(self.num_vars);
|
||||||
self.num_vars = self.num_vars + 1;
|
self.num_vars = self.num_vars + 1;
|
||||||
self.candidates.push(Candidates::None);
|
self.candidates.push(Candidates::None);
|
||||||
|
self.wake.push(BitSet::new());
|
||||||
var
|
var
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,6 +290,11 @@ impl Puzzle {
|
|||||||
|
|
||||||
/// Add a constraint to the puzzle solution.
|
/// Add a constraint to the puzzle solution.
|
||||||
pub fn add_constraint(&mut self, constraint: Box<Constraint>) {
|
pub fn add_constraint(&mut self, constraint: Box<Constraint>) {
|
||||||
|
let cidx = self.constraints.len();
|
||||||
|
for &VarToken(idx) in constraint.vars() {
|
||||||
|
self.wake[idx].insert(cidx);
|
||||||
|
}
|
||||||
|
|
||||||
self.constraints.push(constraint);
|
self.constraints.push(constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,14 +418,20 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
/// Allocate a new puzzle searcher.
|
/// Allocate a new puzzle searcher.
|
||||||
fn new(puzzle: &'a Puzzle) -> Self {
|
fn new(puzzle: &'a Puzzle) -> Self {
|
||||||
let mut vars = Vec::with_capacity(puzzle.num_vars);
|
let mut vars = Vec::with_capacity(puzzle.num_vars);
|
||||||
|
let mut wake = BitSet::new();
|
||||||
|
|
||||||
for c in puzzle.candidates.iter() {
|
for c in puzzle.candidates.iter() {
|
||||||
vars.push(VarState::Unassigned(c.clone()));
|
vars.push(VarState::Unassigned(c.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for cidx in 0..puzzle.constraints.len() {
|
||||||
|
wake.insert(cidx);
|
||||||
|
}
|
||||||
|
|
||||||
PuzzleSearch {
|
PuzzleSearch {
|
||||||
puzzle: puzzle,
|
puzzle: puzzle,
|
||||||
vars: vars,
|
vars: vars,
|
||||||
modified: false,
|
wake: wake,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,14 +475,14 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
&mut Candidates::Value(v) => {
|
&mut Candidates::Value(v) => {
|
||||||
if v == val {
|
if v == val {
|
||||||
*cs = Candidates::None;
|
*cs = Candidates::None;
|
||||||
self.modified = true;
|
self.wake.union_with(&self.puzzle.wake[idx]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&mut Candidates::Set(ref mut rc) => {
|
&mut Candidates::Set(ref mut rc) => {
|
||||||
if rc.contains(&val) {
|
if rc.contains(&val) {
|
||||||
let mut set = Rc::make_mut(rc);
|
let mut set = Rc::make_mut(rc);
|
||||||
set.remove(&val);
|
set.remove(&val);
|
||||||
self.modified = true;
|
self.wake.union_with(&self.puzzle.wake[idx]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -523,11 +541,13 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
fn assign(&mut self, idx: usize, val: Val) -> bool {
|
fn assign(&mut self, idx: usize, val: Val) -> bool {
|
||||||
let var = VarToken(idx);
|
let var = VarToken(idx);
|
||||||
self.vars[idx] = VarState::Assigned(val);
|
self.vars[idx] = VarState::Assigned(val);
|
||||||
self.modified = true;
|
self.wake.union_with(&self.puzzle.wake[idx]);
|
||||||
|
|
||||||
for constraint in self.puzzle.constraints.iter() {
|
for (cidx, constraint) in self.puzzle.constraints.iter().enumerate() {
|
||||||
if !constraint.on_assigned(self, var, val) {
|
if self.puzzle.wake[idx].contains(cidx) {
|
||||||
return false;
|
if !constraint.on_assigned(self, var, val) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,11 +559,7 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
///
|
///
|
||||||
/// Returns false if a contradiction was found.
|
/// Returns false if a contradiction was found.
|
||||||
fn constrain(&mut self) -> bool {
|
fn constrain(&mut self) -> bool {
|
||||||
let mut repeat = true;
|
while !self.wake.is_empty() {
|
||||||
|
|
||||||
while repeat || self.modified {
|
|
||||||
repeat = false;
|
|
||||||
|
|
||||||
// "Gimme" phase:
|
// "Gimme" phase:
|
||||||
// - abort if any variables with 0 candidates,
|
// - abort if any variables with 0 candidates,
|
||||||
// - assign variables with only 1 candidate.
|
// - assign variables with only 1 candidate.
|
||||||
@ -565,7 +581,6 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
if !self.assign(idx, val) {
|
if !self.assign(idx, val) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
repeat = true;
|
|
||||||
last_gimme = idx;
|
last_gimme = idx;
|
||||||
} else if idx == last_gimme {
|
} else if idx == last_gimme {
|
||||||
break;
|
break;
|
||||||
@ -575,11 +590,10 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply constraints.
|
// Apply constraints.
|
||||||
if repeat || self.modified {
|
if !self.wake.is_empty() {
|
||||||
self.modified = false;
|
let wake = mem::replace(&mut self.wake, BitSet::new());
|
||||||
|
for cidx in wake.iter() {
|
||||||
for constraint in self.puzzle.constraints.iter() {
|
if !self.puzzle.constraints[cidx].on_updated(self) {
|
||||||
if !constraint.on_updated(self) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@ struct NoDiagonal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Constraint for NoDiagonal {
|
impl Constraint for NoDiagonal {
|
||||||
|
fn vars<'a>(&'a self) -> Box<Iterator<Item=&'a VarToken> + 'a> {
|
||||||
|
Box::new(self.vars.iter())
|
||||||
|
}
|
||||||
|
|
||||||
fn on_assigned(&self, search: &mut PuzzleSearch, var: VarToken, val: Val)
|
fn on_assigned(&self, search: &mut PuzzleSearch, var: VarToken, val: Val)
|
||||||
-> bool {
|
-> bool {
|
||||||
let y1 = self.vars.iter().position(|&v| v == var).expect("unreachable");
|
let y1 = self.vars.iter().position(|&v| v == var).expect("unreachable");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user