136 lines
4.3 KiB
Rust
136 lines
4.3 KiB
Rust
//! 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));
|
|
});
|
|
}
|
|
}
|