Only wake affected constraints.

This commit is contained in:
David Wang 2017-02-25 08:05:53 +11:00
parent 6eb25f6af0
commit 0477a5bbb0
4 changed files with 47 additions and 27 deletions

View File

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

View File

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

View File

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

View File

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