//! Constraints building //! //! # Ideas //! //! Each constraints will be updated on every assignment, //! thus their status is always inspectable. //! A constraint applies to a set of variables, identified //! by a key of type `K`. //! A constraint owns references to actual values assigned, //! used to perform checks. //! //! //! The problem is to clarify the way Constraints operate. //! Do they compute their status from some data on demand ? //! Do they keep their status updated by watching the Variables //! they act on ? //! Worse, do they have superpowers ? //! Could they filter on a variable domain, according to some other variable //! state ? This would mean that constraints won't judge a result, but guide //! the solving process to avoid erroring paths, like a constraint-driven //! solving. This would be powerfull but maybe far too complex... //! //! On the other hand, we can implement a simple Observer pattern, with strong //! coupling to [`Problem`](crate::solver::Problem). //! Because of this, we can safely play with private fields of Problem, and in //! return, provide a public api to build specific constraints. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Status { Validated,// Solution is valid Unknown, // Constraint cannot resolve yet (unbound variables) Violated, // Solution is invalid } use std::collections::HashMap; // *Like this enum ValueChecker { AllDifferent, AllSame, } pub struct Constraint<'c, V, K> { status: Status, variables: HashMap<&'c K, Option<&'c V>>, // TODO: add a ValueChecker Trait object, // or just a simple Enum.* // to provide the check_status procedure, given // a vector to variables values. } impl<'c, V, K> Constraint<'c, V, K> where K: Eq + std::hash::Hash, V: PartialEq, { pub fn new(vars: Vec<&'c K>) -> Self { let len = vars.len(); Self { status: Status::Unknown, variables: vars.into_iter() .zip(vec![None; len]) .collect(), } } pub fn status(&self) -> &Status { &self.status } fn check_status(vars: Vec<&Option<&V>>) -> Status { /// LEts do an hacky NotEqualConstraint let vars_len = vars.len(); let set_vars: Vec<&Option<&V>> = vars.into_iter().filter(|v| v.is_some()).collect(); let is_complete = vars_len == set_vars.len(); for (idx, value) in set_vars.iter().enumerate() { let violated = set_vars.iter() .enumerate() .filter(|(i,_)| *i != idx) .fold(false, |res, (_,v)| { res || v == value }); if violated { return Status::Violated; } } match is_complete { true => Status::Validated, false => Status::Unknown, } } fn update_status(&mut self) { self.status = Self::check_status(self.variables.values().collect()); } pub fn update(&mut self, key: &K, new_value: Option<&'c V>) { if let Some(value) = self.variables.get_mut(key) { // Actually update values dbg!(*value = new_value); self.update_status(); } } } #[cfg(test)] mod tests { #[test] fn test_all_different_problem() { use crate::solver::{Domain, Problem}; use super::Constraint; let domain = Domain::new(vec![1, 2, 3]); let problem = Problem::build() .add_variable("Left", domain.all(), None) .add_variable("Right", domain.all(), None) .add_constraint(Constraint::new(vec![&"Left", &"Right"])) .finish(); let solutions = vec![ (("Left", Some(&1)), ("Right", Some(&2))), (("Left", Some(&1)), ("Right", Some(&3))), (("Left", Some(&2)), ("Right", Some(&1))), (("Left", Some(&2)), ("Right", Some(&3))), (("Left", Some(&3)), ("Right", Some(&1))), (("Left", Some(&3)), ("Right", Some(&2))), ]; let results = problem.solve_all(); println!("{:#?}", results); assert!(results.len() == solutions.len()); results.into_iter().for_each(|res| { let res = (("Left", *res.get("Left").unwrap()), ("Right", *res.get("Right").unwrap())) ; assert!(solutions.contains(&res)); }); } }