diff --git a/cookbook/src/lib.rs b/cookbook/src/lib.rs index 31e1bb2..3aab4e2 100644 --- a/cookbook/src/lib.rs +++ b/cookbook/src/lib.rs @@ -1,3 +1,7 @@ +mod meal; + +pub use self::meal::Meal; + #[cfg(test)] mod tests { #[test] diff --git a/cookbook/src/meal.rs b/cookbook/src/meal.rs new file mode 100644 index 0000000..506f9de --- /dev/null +++ b/cookbook/src/meal.rs @@ -0,0 +1,19 @@ + + +#[derive(Debug,Clone)] +pub struct Meal { + name: String, + nutritional_value: i32, +} + +impl Meal { + pub fn new(name: String, nutritional_value: i32) -> Meal { + Meal { name, nutritional_value } + } + + pub fn nutritional_value(&self) -> i32 { + self.nutritional_value + } +} + + diff --git a/cookbook/src/meals.rs b/cookbook/src/meals.rs deleted file mode 100644 index e1561d4..0000000 --- a/cookbook/src/meals.rs +++ /dev/null @@ -1,11 +0,0 @@ - -enum Meal { - FullMeal, - Composed, -} - -fn retrieve_all() -> Vec { - vec!() -} - - diff --git a/planner/src/bin/weekly.rs b/planner/src/bin/weekly.rs new file mode 100644 index 0000000..3bed713 --- /dev/null +++ b/planner/src/bin/weekly.rs @@ -0,0 +1,47 @@ +//! The weekly menu planner +//! + +use cookbook::Meal; +use planner::solver::{Variables, Domain, solve_all}; + + +fn generate_weekly_menu() -> String { + let assignments: Variables = [ + ("LundiMidi".to_string(), None), ("LundiSoir".to_string(), None), + ("MardiMidi".to_string(), None), ("MardiSoir".to_string(), None), + ("MercrediMidi".to_string(), None), ("MercrediSoir".to_string(), None), + ].iter().cloned().collect(); + let meals: Domain = Domain::new(vec![ + Meal::new("Raclette".to_string(), 800), + Meal::new("Soupe".to_string(), 400), + ]); + let validator = |vars: &Variables| { + let mut result = true; + for day in ["Lundi", "Mardi", "Mercredi"].into_iter() { + let all_day = vars.keys().filter(|k| k.starts_with(day)); + let mut nutri_value = 0; + for key in all_day { + nutri_value += vars.get(key) + .expect("no value here !") + .expect("no meal there !") + .nutritional_value() + } + println!("{} -> {}", day, nutri_value); + if nutri_value != 1200 { result = false; }; + } + println!("Validator returns {}", result); + result + }; + + let solutions = solve_all(assignments, &meals, validator); + format!("{:#?}", solutions) +} + +fn main() { + println!("{}", generate_weekly_menu()); +} + +#[cfg(test)] +mod tests { + +} diff --git a/planner/src/lib.rs b/planner/src/lib.rs index cde50e2..ce3bbca 100644 --- a/planner/src/lib.rs +++ b/planner/src/lib.rs @@ -1,4 +1,6 @@ -mod solver; +extern crate cookbook; + +pub mod solver; #[cfg(test)] mod tests { diff --git a/planner/src/rules.rs b/planner/src/rules.rs new file mode 100644 index 0000000..b7f24eb --- /dev/null +++ b/planner/src/rules.rs @@ -0,0 +1,16 @@ +//! Rules used by the `planner` +//! A rule is a constraint on valid solutions, but also provides insights +//! and eventually inferences to optimize the solving process. + +// Nutritional values +// - Per day : according to user profile (man: 2000kcal, woman: 1800kcal) +// - Per meal : some meals should have higher nutrional values than others + +// Ingredients +// - Per week : should use most of a limited set of ingredients (excluding +// condiments, ...) +// - To consume : must use a small set of ingredients (leftovers) +// + +// Price +// - Per week : should restrict ingredients cost to a given amount diff --git a/planner/src/solver.rs b/planner/src/solver.rs index a9456e6..31422ee 100644 --- a/planner/src/solver.rs +++ b/planner/src/solver.rs @@ -2,7 +2,7 @@ use std::fmt; use std::clone::Clone; use std::collections::HashMap; -type Variables<'a, V> = HashMap>; +pub type Variables<'a, V> = HashMap>; enum Assignment<'a, V> { Update(String, &'a V), @@ -11,12 +11,12 @@ enum Assignment<'a, V> { #[derive(Clone)] -struct Domain { +pub struct Domain { values: Vec } impl Domain { - fn new(values: Vec) -> Domain { + pub fn new(values: Vec) -> Domain { Domain { values } } } @@ -35,6 +35,7 @@ fn assign_next<'a,'b, V>(assign: &'b Variables<'a, V>, domain: &'a Domain) where V: fmt::Debug { // Panics on empty domain + // If domain values are filtered, then the branch is a dead end if domain.values.is_empty() { panic!("No values in domain : {:?}", domain); }; // TODO: should be able to inject a choosing strategy @@ -51,8 +52,12 @@ fn assign_next<'a,'b, V>(assign: &'b Variables<'a, V>, domain: &'a Domain) } /// Visit all possible solutions, using a stack. -fn solve_all<'a, V: Clone + fmt::Debug>(mut assign: Variables<'a, V>, domain: &'a Domain, is_valid: fn(&Variables<'a,V>) -> bool) - -> Vec> +pub fn solve_all<'a, V>( + mut assign: Variables<'a, V>, + domain: &'a Domain, + is_valid: fn(&Variables<'a,V>) -> bool + ) -> Vec> + where V: Clone + fmt::Debug { let mut solutions: Vec> = vec![]; let mut stack: Vec> = vec![];