@@ -150,12 +150,18 @@ auto goal = blackboard_type{
150150 .stone = 1
151151};
152152
153- auto p = plan<blackboard_type>(actions, initial, goal);
154- p.run(initial); // will mutate the blackboard
153+ auto p = planner<blackboard_type>(actions, initial, goal);
154+
155+ auto blackboard = initial;
156+ while (p) {
157+ p.run_next(blackboard); // will mutate the blackboard
158+ }
155159```
156160*/
157161
158162#include < unordered_set>
163+ #include < functional>
164+ #include < coroutine>
159165#include < memory>
160166#include < vector>
161167#include < queue>
@@ -216,96 +222,11 @@ namespace aitoolkit::goap {
216222 * @ingroup goap
217223 * @class plan
218224 * @brief A plan is a sequence of actions that will lead to a goal state.
219- *
220- * The plan is created by providing a list of actions, an initial blackboard
221- * state, and a goal blackboard state. The plan will then find a sequence of
222- * actions that will lead to the goal state.
223- *
224- * The plan can then be run on a blackboard to mutate it.
225225 */
226226 template <blackboard_trait T>
227227 class plan {
228228 public:
229- /* *
230- * @brief Create a plan.
231- *
232- * @param actions The list of actions that can be performed.
233- * @param initital_blackboard The initial state of the blackboard.
234- * @param goal_blackboard The goal state of the blackboard.
235- * @param max_iterations The maximum number of iterations to perform
236- * before giving up. If 0, the plan will run until it finds a solution.
237- */
238- plan (
239- std::vector<action_ptr<T>> actions,
240- T initital_blackboard,
241- T goal_blackboard,
242- size_t max_iterations = 0
243- ) {
244- struct node_type {
245- T blackboard;
246- float cost;
247-
248- action_ptr<T> action_taken;
249- std::shared_ptr<node_type> parent;
250- };
251-
252- using node_ptr = std::shared_ptr<node_type>;
253-
254- struct node_compare {
255- bool operator ()(const node_ptr& a, const node_ptr& b) const {
256- return a->cost > b->cost ;
257- }
258- };
259-
260- std::priority_queue<node_ptr, std::vector<node_ptr>, node_compare> open_set;
261- std::unordered_set<T> closed_set;
262-
263- open_set.push (std::make_shared<node_type>(node_type{
264- .blackboard = initital_blackboard,
265- .cost = 0 .0f ,
266- .action_taken = nullptr ,
267- .parent = nullptr
268- }));
269-
270- for (
271- size_t iteration = 0 ;
272- !open_set.empty () && (max_iterations == 0 || iteration < max_iterations);
273- ++iteration
274- ) {
275- auto current_node = open_set.top ();
276- open_set.pop ();
277-
278- if (current_node->blackboard == goal_blackboard) {
279- while (current_node->parent != nullptr ) {
280- m_actions.push (current_node->action_taken );
281- current_node = current_node->parent ;
282- }
283-
284- break ;
285- }
286-
287- if (!closed_set.contains (current_node->blackboard )) {
288- closed_set.insert (current_node->blackboard );
289-
290- for (auto & action : actions) {
291- if (action->check_preconditions (current_node->blackboard )) {
292- auto next_blackboard = current_node->blackboard ;
293- action->apply_effects (next_blackboard);
294- auto next_cost = current_node->cost + action->cost (current_node->blackboard );
295-
296- if (!closed_set.contains (next_blackboard)) {
297- open_set.push (std::make_shared<node_type>(node_type{
298- .blackboard = next_blackboard,
299- .cost = next_cost,
300- .action_taken = action,
301- .parent = current_node
302- }));
303- }
304- }
305- }
306- }
307- }
308- }
229+ plan (std::queue<action_ptr<T>> actions) : m_actions(actions) {}
309230
310231 /* *
311232 * @brief Get the number of actions in the plan.
@@ -322,20 +243,104 @@ namespace aitoolkit::goap {
322243 }
323244
324245 /* *
325- * @brief Run the plan on a blackboard .
246+ * @brief Execute the next planned action .
326247 */
327- void run (T& blackboard) {
328- auto actions = m_actions;
329-
330- while (!actions.empty ()) {
331- auto action = actions.front ();
332- actions.pop ();
333-
248+ void run_next (T& blackboard) {
249+ if (!m_actions.empty ()) {
250+ auto action = m_actions.front ();
251+ m_actions.pop ();
334252 action->apply_effects (blackboard);
335253 }
336254 }
255+ };
256+
257+ /* *
258+ * @ingroup goap
259+ * @brief Create a plan.
260+ *
261+ * The plan is created by providing a list of actions, an initial blackboard
262+ * state, and a goal blackboard state. The plan will then find a sequence of
263+ * actions that will lead to the goal state.
264+ *
265+ * @param actions The list of actions that can be performed.
266+ * @param initital_blackboard The initial state of the blackboard.
267+ * @param goal_blackboard The goal state of the blackboard.
268+ * @param max_iterations The maximum number of iterations to perform
269+ * before giving up. If 0, the plan will run until it finds a solution.
270+ * @return A plan that can be executed.
271+ */
272+ template <blackboard_trait T>
273+ plan<T> planner (
274+ std::vector<action_ptr<T>> actions,
275+ T initital_blackboard,
276+ T goal_blackboard,
277+ size_t max_iterations = 0
278+ ) {
279+ struct node_type {
280+ T blackboard;
281+ float cost;
282+
283+ action_ptr<T> action_taken;
284+ std::shared_ptr<node_type> parent;
285+ };
286+
287+ using node_ptr = std::shared_ptr<node_type>;
288+
289+ struct node_compare {
290+ bool operator ()(const node_ptr& a, const node_ptr& b) const {
291+ return a->cost > b->cost ;
292+ }
293+ };
294+
295+ std::priority_queue<node_ptr, std::vector<node_ptr>, node_compare> open_set;
296+ std::unordered_set<T> closed_set;
297+
298+ open_set.push (std::make_shared<node_type>(node_type{
299+ .blackboard = initital_blackboard,
300+ .cost = 0 .0f ,
301+ .action_taken = nullptr ,
302+ .parent = nullptr
303+ }));
304+
305+ for (
306+ size_t iteration = 0 ;
307+ !open_set.empty () && (max_iterations == 0 || iteration < max_iterations);
308+ ++iteration
309+ ) {
310+ auto current_node = open_set.top ();
311+ open_set.pop ();
312+
313+ if (current_node->blackboard == goal_blackboard) {
314+ auto actions = std::queue<action_ptr<T>>{};
315+
316+ while (current_node->parent != nullptr ) {
317+ actions.push (current_node->action_taken );
318+ current_node = current_node->parent ;
319+ }
320+
321+ return plan<T>(actions);
322+ }
337323
338- private:
339- std::queue<action_ptr<T>> m_actions;
324+ if (!closed_set.contains (current_node->blackboard )) {
325+ closed_set.insert (current_node->blackboard );
326+
327+ for (auto & action : actions) {
328+ if (action->check_preconditions (current_node->blackboard )) {
329+ auto next_blackboard = current_node->blackboard ;
330+ action->apply_effects (next_blackboard);
331+ auto next_cost = current_node->cost + action->cost (current_node->blackboard );
332+
333+ if (!closed_set.contains (next_blackboard)) {
334+ open_set.push (std::make_shared<node_type>(node_type{
335+ .blackboard = next_blackboard,
336+ .cost = next_cost,
337+ .action_taken = action,
338+ .parent = current_node
339+ }));
340+ }
341+ }
342+ }
343+ }
344+ }
340345 };
341346}
0 commit comments