1use std::collections::{HashSet};
2use std::hash::Hash;
3use std::fmt::{Debug, Display};
4use log::{debug};
5
6#[macro_use] pub mod macros;
7pub mod proposition;
8pub mod action;
9pub mod plangraph;
10pub mod solver;
11mod layer;
12mod pairset;
13
14pub use crate::proposition::Proposition;
15pub use crate::action::{Action, ActionType};
16pub use crate::plangraph::{PlanGraph, Solution};
17pub use crate::solver::{GraphPlanSolver, SimpleSolver};
18
19
20pub struct Domain<'a,
24 ActionId: Debug + Hash + Ord + Clone,
25 PropositionId: Debug + Display + Hash + Ord + Clone> {
26 initial_props: HashSet<&'a Proposition<PropositionId>>,
27 goals: HashSet<&'a Proposition<PropositionId>>,
28 actions: HashSet<Action<'a, ActionId, PropositionId>>
29}
30
31pub struct GraphPlan<'a,
32 ActionId: Debug + Hash + Ord + Clone,
33 PropositionId: Debug + Display + Hash + Ord + Clone> {
34 plangraph: PlanGraph<'a, ActionId, PropositionId>,
35}
36
37impl<'a,
38 ActionId: Debug + Hash + Ord + Clone,
39 PropositionId: Debug + Display + Hash + Ord + Clone> GraphPlan<'a, ActionId, PropositionId> {
40
41 pub fn new(initial_props: HashSet<&'a Proposition<PropositionId>>,
44 goals: HashSet<&'a Proposition<PropositionId>>,
45 actions: HashSet<&'a Action<'a, ActionId, PropositionId>>)
46 -> GraphPlan<'a, ActionId, PropositionId> {
47 let plangraph = PlanGraph::new(
48 initial_props,
49 goals,
50 actions,
51 );
52 GraphPlan { plangraph }
53 }
54
55 pub fn from_domain(domain: &'a Domain<'a, ActionId, PropositionId>)
56 -> GraphPlan<'a, ActionId, PropositionId> {
57 let plangraph = PlanGraph::new(
58 domain.initial_props.clone(),
59 domain.goals.clone(),
60 domain.actions.iter().collect(),
61 );
62 GraphPlan { plangraph }
63 }
64
65 pub fn create_domain(initial_props: HashSet<&'a Proposition<PropositionId>>,
68 goals: HashSet<&'a Proposition<PropositionId>>,
69 actions: HashSet<&'a Action<'a, ActionId, PropositionId>>)
70 -> Domain<'a, ActionId, PropositionId> {
71 let mut all_actions = HashSet::new();
72
73 for p in &initial_props {
74 all_actions.insert(Action::new_maintenance(*p));
75 }
76
77 for a in actions {
78 all_actions.insert(a.to_owned());
79
80 for p in &a.reqs {
81 all_actions.insert(Action::new_maintenance(p));
82 }
83
84 for p in &a.effects {
85 all_actions.insert(Action::new_maintenance(p));
86 }
87 }
88
89 Domain {
90 initial_props,
91 goals,
92 actions: all_actions,
93 }
94 }
95
96 pub fn search<Solver>(&mut self) -> Option<Solution<'a, ActionId, PropositionId>>
97 where Solver: GraphPlanSolver<'a, ActionId, PropositionId> {
98
99 let mut tries = 0;
100 let mut solution = None;
101 let max_tries = self.plangraph.actions.len() + 1;
102
103 while tries < max_tries {
104 self.plangraph.extend();
105 if self.plangraph.has_leveled_off() {
108 break;
109 }
110
111 if !self.plangraph.has_possible_solution() {
112 debug!("No solution exists at depth {}", self.plangraph.depth());
113 tries += 1;
114 continue
115 }
116
117 if let Some(result) = Solver::search(&self.plangraph) {
118 solution = Some(result);
119 break;
120 } else {
121 debug!("No solution found at depth {}", self.plangraph.depth());
122 tries += 1
123 }
124 };
125
126 solution
127 }
128
129 pub fn format_plan(solution: Solution<ActionId, PropositionId>) -> Solution<ActionId, PropositionId> {
131 solution.iter()
132 .map(|s| s.iter()
133 .filter(|i| match i.id {
134 ActionType::Action(_) => true,
135 ActionType::Maintenance(_) => false})
136 .cloned()
137 .collect())
138 .collect()
139 }
140}
141
142#[cfg(test)]
143mod integration_test {
144 use crate::GraphPlan;
145 use crate::proposition::Proposition;
146 use crate::action::Action;
147 use crate::solver::SimpleSolver;
148
149 #[test]
150 fn integration() {
151 let p1 = Proposition::from("tired");
152 let not_p1 = p1.negate();
153 let p2 = Proposition::from("dog needs to pee");
154 let not_p2 = p2.negate();
155
156 let a1 = Action::new(
157 "coffee",
158 hashset!{&p1},
159 hashset!{¬_p1}
160 );
161
162 let a2 = Action::new(
163 "walk dog",
164 hashset!{&p2, ¬_p1},
165 hashset!{¬_p2},
166 );
167
168 let actions = hashset!{&a1, &a2};
169 let initial_props = hashset!{&p1, &p2};
170 let goals = hashset!{¬_p1, ¬_p2};
171
172 let domain = GraphPlan::create_domain(
173 initial_props,
174 goals,
175 actions,
176 );
177
178 let mut pg = GraphPlan::<&str, &str>::from_domain(&domain);
179 assert!(pg.search::<SimpleSolver>() != None, "Solution should not be None");
180 }
181}