1#![doc(html_logo_url = "https://github.com/dbrgn/rusty-santa/raw/master/logo.png")]
19
20#[macro_use]
21extern crate log;
22extern crate rand;
23
24use std::collections::{HashMap, HashSet};
25
26use rand::{thread_rng, Rng};
27
28#[derive(Clone)]
29struct Matrix {
30 keys: Vec<String>,
31 indexes: HashMap<String, usize>,
32 data: Vec<Vec<bool>>,
33}
34
35impl Matrix {
36 pub fn new(keys: Vec<String>) -> Self {
37 let size = keys.len();
39
40 let mut indexes = HashMap::new();
42 for (i, key) in keys.iter().enumerate() {
43 indexes.insert(key.clone(), i);
44 }
45
46 let mut data = vec![vec![true; size]; size];
48
49 for i in 0..size {
51 data[i][i] = false;
52 }
53
54 Matrix {
55 keys: keys,
56 indexes: indexes,
57 data: data,
58 }
59 }
60
61 pub fn get(&self, x: &str, y: &str) -> bool {
65 let ix = self.indexes.get(x).unwrap();
66 let iy = self.indexes.get(y).unwrap();
67 self.data[*ix][*iy]
68 }
69
70 pub fn get_row(&self, x: &str) -> Vec<bool> {
74 let ix = self.indexes.get(x).unwrap();
75 self.data[*ix].clone()
76 }
77
78 pub fn set(&mut self, x: &str, y: &str, val: bool) {
82 let ix = self.indexes.get(x).unwrap();
83 let iy = self.indexes.get(y).unwrap();
84 self.data[*ix][*iy] = val;
85 }
86
87 pub fn set_col(&mut self, y: &str, val: bool) {
91 let iy = self.indexes.get(y).unwrap();
92 for row in self.data.iter_mut() {
93 row[*iy] = val;
94 }
95 }
96
97 pub fn contains(&mut self, key: &str) -> bool {
99 self.indexes.contains_key(key)
100 }
101
102 pub fn size(&self) -> usize {
104 self.keys.len()
105 }
106
107 pub fn key_at(&self, index: usize) -> &str {
109 &self.keys[index]
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
114enum Constraint {
115 ExcludePair {
116 a: String,
117 b: String,
118 },
119 Exclude {
120 from: String,
121 to: String,
122 },
123}
124
125#[derive(Debug, Clone)]
127pub struct Group {
128 people_set: HashSet<String>,
129 constraints: Vec<Constraint>,
130
131 max_attempts: u32,
134}
135
136impl Group {
137 pub fn new() -> Self {
139 Group {
140 people_set: HashSet::new(),
141 constraints: vec![],
142 max_attempts: 1000,
143 }
144 }
145
146 pub fn add(&mut self, name: String) {
148 self.people_set.insert(name);
149 }
150
151 fn add_constraint(&mut self, constraint: Constraint) {
152 self.constraints.push(constraint);
153 }
154
155 pub fn exclude(&mut self, from: String, to: String) {
157 let constraint = Constraint::Exclude { from: from, to: to };
158 self.add_constraint(constraint);
159 }
160
161 pub fn exclude_pair(&mut self, a: String, b: String) {
163 let constraint = Constraint::ExcludePair { a: a, b: b };
164 self.add_constraint(constraint);
165 }
166
167 pub fn contains_name(&self, name: &str) -> bool {
169 self.people_set.contains(name)
170 }
171
172 pub fn assign(&self) -> Result<Vec<(String, String)>, AssignError> {
174 let mut rng = thread_rng();
176
177 let mut people: Vec<String> = self.people_set.iter().cloned().collect();
179 rng.shuffle(&mut people);
180
181 'attempt: for _ in 0..self.max_attempts {
182
183 let mut matrix = Matrix::new(people.clone());
185
186 for constraint in self.constraints.iter() {
188 match constraint {
189 &Constraint::ExcludePair{ ref a, ref b } => {
190 if !matrix.contains(a) {
191 return Err(AssignError::BadConstraint(format!("Unknown person \"{}\"", a)));
192 }
193 if !matrix.contains(b) {
194 return Err(AssignError::BadConstraint(format!("Unknown person \"{}\"", b)));
195 }
196 matrix.set(a, b, false);
197 matrix.set(b, a, false);
198 },
199 &Constraint::Exclude { ref from, ref to } => {
200 if !matrix.contains(from) {
201 return Err(AssignError::BadConstraint(format!("Unknown person \"{}\"", from)));
202 }
203 if !matrix.contains(to) {
204 return Err(AssignError::BadConstraint(format!("Unknown person \"{}\"", to)));
205 }
206 matrix.set(from, to, false);
207 }
208 }
209 };
210
211 let mut assignments = vec![];
212 for person in people.iter() {
213 trace!("Drawing recipient for {}", person);
214
215 let mut basket = vec![];
217 {
218 let row = matrix.get_row(&person);
219 for i in 0..row.len() {
220 if row[i] {
221 basket.push(matrix.key_at(i).to_owned());
222 }
223 }
224 }
225 trace!("Options: {:?}", basket);
226
227 if basket.is_empty() {
229 trace!("Attempt failed. Retrying...");
230 continue 'attempt;
231 }
232 let choice = rng.choose(&basket).unwrap();
233 trace!("Picked {}!", choice);
234
235 matrix.set_col(choice, false);
237
238 assignments.push((person.clone(), choice.clone()));
240 }
241
242 return Ok(assignments);
243 }
244 return Err(AssignError::GivingUp)
245 }
246}
247
248#[derive(Debug)]
250pub enum AssignError {
251 BadConstraint(String),
252 GivingUp,
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn matrix_init() {
261 let keys = vec!["a".into(), "b".into(), "c".into()];
262 let matrix = Matrix::new(keys);
263
264 assert!(!matrix.get("a", "a"));
265 assert!(!matrix.get("b", "b"));
266 assert!(!matrix.get("c", "c"));
267
268 assert!(matrix.get("a", "b"));
269 assert!(matrix.get("a", "c"));
270 assert!(matrix.get("b", "a"));
271 assert!(matrix.get("b", "c"));
272 assert!(matrix.get("c", "a"));
273 assert!(matrix.get("c", "b"));
274 }
275
276 #[test]
277 fn matrix_get_row() {
278 let keys = vec!["a".into(), "b".into(), "c".into()];
279 let mut matrix = Matrix::new(keys);
280 assert_eq!(matrix.get_row("a"), vec![false, true, true]);
281 assert_eq!(matrix.get_row("b"), vec![true, false, true]);
282 assert_eq!(matrix.get_row("c"), vec![true, true, false]);
283 }
284
285 #[test]
286 fn matrix_set() {
287 let keys = vec!["a".into(), "b".into(), "c".into()];
288 let mut matrix = Matrix::new(keys);
289
290 assert!(matrix.get("a", "b"));
291 matrix.set("a", "b", false);
292 assert!(!matrix.get("a", "b"));
293 matrix.set("a", "b", true);
294 assert!(matrix.get("a", "b"));
295 }
296
297 #[test]
298 fn matrix_contains() {
299 let keys = vec!["a".into(), "b".into(), "c".into()];
300 let mut matrix = Matrix::new(keys);
301
302 assert!(matrix.contains("a"));
303 assert!(matrix.contains("b"));
304 assert!(matrix.contains("c"));
305 assert!(!matrix.contains("d"));
306 assert!(!matrix.contains("aa"));
307 }
308
309 #[test]
310 fn matrix_size() {
311 let keys = vec!["a".into(), "b".into(), "c".into()];
312 let matrix = Matrix::new(keys);
313 assert_eq!(3, matrix.size());
314
315 let keys = vec!["a".into()];
316 let matrix = Matrix::new(keys);
317 assert_eq!(1, matrix.size());
318 }
319
320 #[test]
322 fn group_simple() {
323 let mut group = Group::new();
324
325 group.add("a".into());
326 group.add("b".into());
327 group.add("c".into());
328
329 let assignments = group.assign().unwrap();
330 assert_eq!(assignments.len(), 3);
331
332 for (from, to) in assignments {
333 match from.as_ref() {
334 "a" => assert!(to == "b" || to == "c"),
335 "b" => assert!(to == "a" || to == "c"),
336 "c" => assert!(to == "a" || to == "b"),
337 _ => panic!(),
338 }
339 }
340 }
341
342 #[test]
344 fn group_may_fail() {
345 let mut group = Group::new();
346
347 group.add("Sheldon".into());
348 group.add("Amy".into());
349 group.add("Leonard".into());
350 group.add("Penny".into());
351 group.add("Rajesh".into());
352
353 group.exclude_pair("Sheldon".into(), "Amy".into());
354 group.exclude_pair("Sheldon".into(), "Leonard".into());
355 group.exclude_pair("Leonard".into(), "Penny".into());
356
357 for i in 0..1000 {
358 group.assign();
359 }
360 }
361}