puzzle_solver/constraint/
alldifferent.rs1use std::collections::HashMap;
4use std::rc::Rc;
5
6use ::{Constraint,PsResult,PuzzleSearch,Val,VarToken};
7
8pub struct AllDifferent {
9 vars: Vec<VarToken>,
10}
11
12impl AllDifferent {
13 pub fn new<'a, I>(vars: I) -> Self
25 where I: IntoIterator<Item=&'a VarToken> {
26 AllDifferent {
27 vars: vars.into_iter().cloned().collect(),
28 }
29 }
30}
31
32impl Constraint for AllDifferent {
33 fn vars<'a>(&'a self) -> Box<Iterator<Item=&'a VarToken> + 'a> {
34 Box::new(self.vars.iter())
35 }
36
37 fn on_assigned(&self, search: &mut PuzzleSearch, var: VarToken, val: Val)
38 -> PsResult<()> {
39 for &var2 in self.vars.iter().filter(|&v| *v != var) {
40 try!(search.remove_candidate(var2, val));
41 }
42
43 Ok(())
44 }
45
46 fn on_updated(&self, search: &mut PuzzleSearch) -> PsResult<()> {
47 let mut num_unassigned = 0;
49 let mut all_candidates = HashMap::new();
50
51 for &var in self.vars.iter().filter(|&var| !search.is_assigned(*var)) {
52 num_unassigned = num_unassigned + 1;
53
54 for val in search.get_unassigned(var) {
55 if all_candidates.contains_key(&val) {
56 all_candidates.insert(val, None);
57 } else {
58 all_candidates.insert(val, Some(var));
59 }
60 }
61 }
62
63 if num_unassigned > all_candidates.len() {
64 return Err(());
66 } else if num_unassigned == all_candidates.len() {
67 for (&val, &opt) in all_candidates.iter() {
69 if let Some(var) = opt {
70 try!(search.set_candidate(var, val));
71 }
72 }
73 }
74
75 Ok(())
76 }
77
78 fn substitute(&self, from: VarToken, to: VarToken)
79 -> PsResult<Rc<Constraint>> {
80 if let Some(idx) = self.vars.iter().position(|&var| var == from) {
81 if !self.vars.contains(&to) {
82 let mut new_vars = self.vars.clone();
83 new_vars[idx] = to;
84 return Ok(Rc::new(AllDifferent{ vars: new_vars }));
85 }
86 }
87
88 Err(())
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use ::{Puzzle,Val};
95
96 #[test]
97 fn test_contradiction() {
98 let mut puzzle = Puzzle::new();
99 let v0 = puzzle.new_var_with_candidates(&[1]);
100 let v1 = puzzle.new_var_with_candidates(&[1]);
101 let v2 = puzzle.new_var_with_candidates(&[1,2,3]);
102
103 puzzle.all_different(&[v0,v1,v2]);
104
105 let solution = puzzle.solve_any();
106 assert!(solution.is_none());
107 }
108
109 #[test]
110 fn test_elimination() {
111 let mut puzzle = Puzzle::new();
112 let v0 = puzzle.new_var_with_candidates(&[1]);
113 let v1 = puzzle.new_var_with_candidates(&[1,2,3]);
114 let v2 = puzzle.new_var_with_candidates(&[1,2,3]);
115
116 puzzle.all_different(&[v0,v1,v2]);
117
118 let search = puzzle.step().expect("contradiction");
119 assert_eq!(search[v0], 1);
120 assert_eq!(search.get_unassigned(v1).collect::<Vec<Val>>(), &[2,3]);
121 assert_eq!(search.get_unassigned(v2).collect::<Vec<Val>>(), &[2,3]);
122 }
123
124 #[test]
125 fn test_contradiction_by_length() {
126 let mut puzzle = Puzzle::new();
127 let v0 = puzzle.new_var_with_candidates(&[1,2]);
128 let v1 = puzzle.new_var_with_candidates(&[1,2]);
129 let v2 = puzzle.new_var_with_candidates(&[1,2]);
130
131 puzzle.all_different(&[v0,v1,v2]);
132
133 let search = puzzle.step();
134 assert!(search.is_none());
135 }
136
137 #[test]
138 fn test_constrain_by_value() {
139 let mut puzzle = Puzzle::new();
140 let v0 = puzzle.new_var_with_candidates(&[1,2]);
141 let v1 = puzzle.new_var_with_candidates(&[1,2]);
142 let v2 = puzzle.new_var_with_candidates(&[1,2,3]);
143
144 puzzle.all_different(&[v0,v1,v2]);
145
146 let search = puzzle.step().expect("contradiction");
147 assert_eq!(search.get_unassigned(v0).collect::<Vec<Val>>(), &[1,2]);
148 assert_eq!(search.get_unassigned(v1).collect::<Vec<Val>>(), &[1,2]);
149 assert_eq!(search[v2], 3);
150 }
151}