working on constraint design
This commit is contained in:
119
planner/src/constraint.rs
Normal file
119
planner/src/constraint.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
//! 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.
|
||||
|
||||
#[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 mut 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));
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user