All Different: detect values that can only be assigned to one variable.
queen_6x6: 66 -> 62 guesses. queen_7x7: 179 -> 167 guesses. queen_8x8: 662 -> 590 guesses. sudoku_hardest: 1850 -> 172 guesses.
This commit is contained in:
parent
7c3ba0faee
commit
73bf143595
@ -1,6 +1,6 @@
|
|||||||
//! All different implementation.
|
//! All different implementation.
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use ::{Constraint,PuzzleSearch,Val,VarToken};
|
use ::{Constraint,PuzzleSearch,Val,VarToken};
|
||||||
|
|
||||||
@ -47,18 +47,30 @@ impl Constraint for AllDifferent {
|
|||||||
fn on_updated(&self, search: &mut PuzzleSearch) -> bool {
|
fn on_updated(&self, search: &mut PuzzleSearch) -> bool {
|
||||||
// Build a table of which values can be assigned to which variables.
|
// Build a table of which values can be assigned to which variables.
|
||||||
let mut num_unassigned = 0;
|
let mut num_unassigned = 0;
|
||||||
let mut all_candidates = HashSet::new();
|
let mut all_candidates = HashMap::new();
|
||||||
|
|
||||||
for &var in self.vars.iter().filter(|&var| !search.is_assigned(*var)) {
|
for &var in self.vars.iter().filter(|&var| !search.is_assigned(*var)) {
|
||||||
num_unassigned = num_unassigned + 1;
|
num_unassigned = num_unassigned + 1;
|
||||||
|
|
||||||
for val in search.get_unassigned(var) {
|
for val in search.get_unassigned(var) {
|
||||||
all_candidates.insert(val);
|
if all_candidates.contains_key(&val) {
|
||||||
|
all_candidates.insert(val, None);
|
||||||
|
} else {
|
||||||
|
all_candidates.insert(val, Some(var));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if num_unassigned > all_candidates.len() {
|
if num_unassigned > all_candidates.len() {
|
||||||
// More unassigned variables than candidates, contradiction.
|
// More unassigned variables than candidates, contradiction.
|
||||||
return false;
|
return false;
|
||||||
|
} else if num_unassigned == all_candidates.len() {
|
||||||
|
// As many as variables as candidates.
|
||||||
|
for (&val, &opt) in all_candidates.iter() {
|
||||||
|
if let Some(var) = opt {
|
||||||
|
search.set_candidate(var, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
@ -109,4 +121,19 @@ mod tests {
|
|||||||
let search = puzzle.step();
|
let search = puzzle.step();
|
||||||
assert!(search.is_none());
|
assert!(search.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_constrain_by_value() {
|
||||||
|
let mut puzzle = Puzzle::new();
|
||||||
|
let v0 = puzzle.new_var_with_candidates(&[1,2]);
|
||||||
|
let v1 = puzzle.new_var_with_candidates(&[1,2]);
|
||||||
|
let v2 = puzzle.new_var_with_candidates(&[1,2,3]);
|
||||||
|
|
||||||
|
puzzle.all_different(&[v0,v1,v2]);
|
||||||
|
|
||||||
|
let search = puzzle.step().expect("contradiction");
|
||||||
|
assert_eq!(search.get_unassigned(v0).collect::<Vec<Val>>(), &[1,2]);
|
||||||
|
assert_eq!(search.get_unassigned(v1).collect::<Vec<Val>>(), &[1,2]);
|
||||||
|
assert_eq!(search[v2], 3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -466,6 +466,35 @@ impl<'a> PuzzleSearch<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a variable to a known value.
|
||||||
|
pub fn set_candidate(&mut self, var: VarToken, val: Val) {
|
||||||
|
let VarToken(idx) = var;
|
||||||
|
match &mut self.vars[idx] {
|
||||||
|
&mut VarState::Assigned(_) => (),
|
||||||
|
&mut VarState::Unassigned(ref mut cs) => match cs {
|
||||||
|
&mut Candidates::None => return,
|
||||||
|
&mut Candidates::Value(v) => {
|
||||||
|
if v != val {
|
||||||
|
*cs = Candidates::None;
|
||||||
|
self.wake.union_with(&self.puzzle.wake[idx]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
&mut Candidates::Set(ref mut rc) => {
|
||||||
|
if rc.contains(&val) {
|
||||||
|
let mut set = Rc::make_mut(rc);
|
||||||
|
set.clear();
|
||||||
|
set.insert(val);
|
||||||
|
self.wake.union_with(&self.puzzle.wake[idx]);
|
||||||
|
} else {
|
||||||
|
let mut set = Rc::make_mut(rc);
|
||||||
|
set.clear();
|
||||||
|
self.wake.union_with(&self.puzzle.wake[idx]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove a single candidate from an unassigned variable.
|
/// Remove a single candidate from an unassigned variable.
|
||||||
pub fn remove_candidate(&mut self, var: VarToken, val: Val) {
|
pub fn remove_candidate(&mut self, var: VarToken, val: Val) {
|
||||||
let VarToken(idx) = var;
|
let VarToken(idx) = var;
|
||||||
|
Loading…
Reference in New Issue
Block a user