├── GOAP.pdf ├── .gitignore ├── src ├── json test ├── worldstate.cpp ├── goap_json_test.cpp ├── proposition.cpp ├── task_manager.cpp ├── goap_test.cpp ├── action.cpp ├── goap.cpp └── action_planner.cpp ├── package.xml ├── README.md ├── License.txt ├── CMakeLists.txt └── include └── task_planning ├── worldstate.h ├── task_manager.h ├── task_manager.tpp └── action_planner.h /GOAP.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UTNuclearRoboticsPublic/task_planning/HEAD/GOAP.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | 3 | # Emacs 4 | .#* 5 | 6 | # kdevelop config files 7 | .kdev_include_paths 8 | -------------------------------------------------------------------------------- /src/json test: -------------------------------------------------------------------------------- 1 | "load": { 2 | "pre": [ 3 | {"atom": "loaded", "value": "false"} 4 | ] 5 | "pst": [ 6 | {"atom": "loaded", "value": "true"} 7 | ] 8 | } 9 | 10 | "shoot": { 11 | "pre": [ 12 | {"atom": "loaded", "value": "true"} 13 | {"atom": "enemyalive", "value": "true"} 14 | ] 15 | "pst": [ 16 | {"atom": "enemyalive", "value": "false"} 17 | {"atom": "loaded", "value": "false"} 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | task_planning 4 | 0.0.0 5 | Provides Goal Oriented Action Planning (GOAP) library 6 | 7 | Blake Anderson 8 | LANS 9 | Blake Anderson 10 | 11 | catkin 12 | 13 | roscpp 14 | roslib 15 | 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # task_planning 2 | Goal Oriented Action Planning (GOAP) and task execution. 3 | 4 | The Task Planning package was initially written at Los Alamos National Laboratory (LANL). Later revisions were done at The University of Texas in the Nuclear Robotics Group. The software is open source, see License.txt. 5 | 6 | TASK_PLANNING ROS PACKAGE GUIDE 7 | ---------------------------------------------------------------- 8 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 9 | ---------------------------------------------------------------- 10 | 11 | TABLE OF CONTENTS 12 | 1 Introduction 13 | 14 | 2 Overview 15 | 16 | 3 Usage 17 | 18 | 4 Contact 19 | 20 | 5 License 21 | 22 | 1 INTRODUCTION 23 | 24 | This package provides a library of classes implementing Goal Oriented Action Planning (GOAP). Although this is organized as a ROS package, the library itself does not utilize any ROS features. 25 | 26 | 2 OVERVIEW 27 | 28 | The following classes implement the GOAP algorithm: 29 | 30 | Worldstate - Stores a vector of boolean variables (atoms) which represent the state of the world and robot. 31 | 32 | Action - Modifies the world atoms in some way. An action is defined by its preconditions, postconditions, and cost function. 33 | 34 | ActionPlanner - Stores the set of actions and produces action plans using the createPlan() function. 35 | 36 | TaskManager - Top level class providing both task planning and execution. It executes task plans by associating callback functions with each action. 37 | 38 | 3 USAGE 39 | 40 | See GOAP.pdf for explanation of action planning principles and example code. 41 | 42 | rosrun task_planning goap_test - Performs a test of the ActionPlanner class. 43 | 44 | 4 CONTACT 45 | 46 | The lab members responsible for this package are listed below with their contact information. 47 | 48 | Blake Anderson 49 | blakeanderson@utexas.edu 50 | 51 | 5 LICENSE 52 | 53 | See License.txt for terms of use for this package. 54 | 55 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Los Alamos National Security, LLC 2 | All rights reserved. 3 | Copyright 2016. Los Alamos National Security, LLC. This software was produced under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National Laboratory (LANL), which is operated by Los Alamos National Security, LLC for the U.S. Department of Energy. The U.S. Government has rights to use, reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified to produce derivative works, such modified software should be clearly marked, so as not to confuse it with the version available from LANL. 4 | 5 | Additionally, redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 3. Neither the name of Los Alamos National Security, LLC, Los Alamos National Laboratory, LANL, the U.S. Government, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /src/worldstate.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : worldstate.cpp 3 | // Project : task_planning 4 | // Created : 5/25/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | #include 32 | 33 | namespace goap { 34 | 35 | void Worldstate::initialize(std::size_t new_size) 36 | { 37 | values.resize(new_size, false); 38 | care.resize(new_size, false); 39 | } 40 | 41 | void Worldstate::clear() 42 | { 43 | values.clear(); 44 | care.clear(); 45 | } 46 | 47 | } // namespace goap -------------------------------------------------------------------------------- /src/goap_json_test.cpp: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------------- 2 | goap_test.cpp 3 | Version: In Development 4 | Commit Date: Never 5 | 6 | Authors: Blake Anderson 7 | The University of Texas at Austin 8 | Department of Mechanical Engineering 9 | Nuclear and Applied Robotics Group 10 | 11 | Modified from code by Abraham Stolk. 12 | 13 | Description: Test script for Goal Oriented Action Planning 14 | 15 | Command Line Arguments: None 16 | -------------------------------------------------------------------------------*/ 17 | 18 | /* 19 | Copyright 2012 Abraham T. Stolk 20 | 21 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 22 | 23 | http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 26 | */ 27 | 28 | #include "task_planning/goap.h" // for planner interface. 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | int main( int argc, char* argv[] ) 38 | { 39 | goap::GOAP goap; 40 | goap::actionplanner_t ap; 41 | 42 | std::string file_path = ros::package::getPath("task_planning") + "/json_test"; 43 | std::cout << file_path << std::endl; 44 | 45 | if (!goap.load_json(file_path, &ap)) { 46 | return 1; 47 | } 48 | 49 | goap::worldstate_t fr; 50 | goap.goap_worldstate_set( &ap, &fr, "loaded", false ); 51 | goap.goap_worldstate_set( &ap, &fr, "enemy_alive", true ); 52 | 53 | goap.goap_set_cost( &ap, "shoot", 1 ); 54 | goap.goap_set_cost( &ap, "load", 1 ); 55 | 56 | std::string desc; 57 | goap.goap_description(&ap, &desc); 58 | printf("Planner:\n%s", desc.c_str()); 59 | 60 | goap::worldstate_t goal; 61 | goap.goap_worldstate_set(&ap, &goal, "enemy_alive", false); 62 | 63 | goap.goap_worldstate_description(&ap, &goal, &desc); 64 | printf("GOAL: %s", desc.c_str()); 65 | //goap_worldstate_set( &ap, &goal, "alive", true ); // add this to avoid suicide actions in plan. 66 | 67 | std::vector states; 68 | std::vector plan; 69 | 70 | const int plancost = goap.astar_plan(&ap, fr, goal, &plan, &states); 71 | printf("plancost = %d\n", plancost); 72 | 73 | goap.goap_worldstate_description(&ap, &fr, &desc); 74 | printf("Start: %s\n", desc.c_str()); 75 | for (int i = 0; i < plan.size(); ++i) 76 | { 77 | goap.goap_worldstate_description(&ap, &states.at(i), &desc); 78 | printf("%d: %-20s%s\n", i, plan.at(i).c_str(), desc.c_str()); 79 | } 80 | 81 | return plancost; 82 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(task_planning) 3 | 4 | set(CMAKE_CXX_FLAGS "-std=c++11") 5 | 6 | ## Find catkin macros and libraries 7 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) 8 | ## is used, also find other catkin packages 9 | find_package(catkin REQUIRED 10 | roscpp 11 | roslib 12 | ) 13 | 14 | ## System dependencies are found with CMake's conventions 15 | find_package(Boost REQUIRED system chrono) 16 | 17 | catkin_package( 18 | INCLUDE_DIRS include 19 | LIBRARIES task_planner 20 | CATKIN_DEPENDS roscpp roslib 21 | DEPENDS Boost 22 | ) 23 | 24 | include_directories(include) 25 | 26 | add_library(task_planner 27 | src/task_manager.cpp 28 | src/action_planner.cpp 29 | src/action.cpp 30 | src/worldstate.cpp 31 | src/proposition.cpp 32 | ) 33 | 34 | ## Declare a cpp executable 35 | add_executable(goap_test src/goap_test.cpp) 36 | 37 | ## Add cmake target dependencies of the executable/library 38 | ## as an example, message headers may need to be generated before nodes 39 | #add_dependencies(task_planning_node task_planning_generate_messages_cpp) 40 | 41 | ## Specify libraries to link a library or executable target against 42 | target_link_libraries(task_planner 43 | ${Boost_LIBRARIES} 44 | ) 45 | 46 | target_link_libraries(goap_test task_planner ${Boost_LIBRARIES}) 47 | 48 | ############# 49 | ## Install ## 50 | ############# 51 | 52 | # all install targets should use catkin DESTINATION variables 53 | # See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html 54 | 55 | ## Mark executable scripts (Python etc.) for installation 56 | ## in contrast to setup.py, you can choose the destination 57 | # install(PROGRAMS 58 | # scripts/my_python_script 59 | # DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 60 | # ) 61 | 62 | ## Mark executables and/or libraries for installation 63 | # install(TARGETS task_planning task_planning_node 64 | # ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 65 | # LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 66 | # RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 67 | # ) 68 | 69 | ## Mark cpp header files for installation 70 | #install(DIRECTORY include/${PROJECT_NAME}/ 71 | # DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} 72 | # FILES_MATCHING PATTERN "*.h" 73 | # PATTERN ".svn" EXCLUDE 74 | #) 75 | 76 | ## Mark other files for installation (e.g. launch and bag files, etc.) 77 | # install(FILES 78 | # # myfile1 79 | # # myfile2 80 | # DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 81 | # ) 82 | 83 | ############# 84 | ## Testing ## 85 | ############# 86 | 87 | ## Add gtest based cpp test target and link libraries 88 | # catkin_add_gtest(${PROJECT_NAME}-test test/test_task_planning.cpp) 89 | # if(TARGET ${PROJECT_NAME}-test) 90 | # target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) 91 | # endif() 92 | 93 | ## Add folders to be run by python nosetests 94 | # catkin_add_nosetests(test) 95 | -------------------------------------------------------------------------------- /include/task_planning/worldstate.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : worldstate.h 3 | // Project : task_planning 4 | // Created : 5/25/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | /* 32 | * Defines a world state using a set of boolean variables. 33 | */ 34 | 35 | #ifndef GOAP_WORLDSTATE_H 36 | #define GOAP_WORLDSTATE_H 37 | 38 | #define BOOST_DYNAMIC_BITSET_DONT_USE_FRIENDS // needed for hash_value to access m_bits 39 | #include 40 | #include 41 | 42 | namespace goap { 43 | 44 | // Describes the world state by listing values (t/f) for all known atoms. 45 | class Worldstate 46 | { 47 | public: 48 | boost::dynamic_bitset<> values; // Values for atoms. 49 | boost::dynamic_bitset<> care; // Mask for atoms that do not matter. 50 | 51 | /** 52 | * @breif Resize values and care 53 | * @param new_size Set values and care to this size. 54 | */ 55 | void initialize(std::size_t new_size); 56 | 57 | /** 58 | * @breif Clears values and care. 59 | */ 60 | void clear(); 61 | 62 | bool operator==(const Worldstate &rhs) const { return values == rhs.values; } 63 | 64 | bool operator<(const Worldstate &rhs) const { 65 | hash hasher; 66 | return hasher(*this) < hasher(rhs); 67 | } 68 | 69 | struct hash 70 | { 71 | size_t operator()(const Worldstate& ws) const { 72 | return boost::hash_value(ws.values.m_bits); 73 | } 74 | }; 75 | }; 76 | 77 | } // namespace goap 78 | 79 | #endif -------------------------------------------------------------------------------- /src/proposition.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : proposition.cpp 3 | // Project : task_planning 4 | // Created : 6/3/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace goap { 37 | 38 | bool ActionPlanner::Proposition::parse(std::string definition) 39 | { 40 | boost::trim(definition); 41 | 42 | // Find the start of the parameter list 43 | std::size_t param_start = definition.find('('); 44 | if (param_start == std::string::npos) { 45 | printf("Malformed Proposition, '(' expected: %s\n", definition.c_str()); 46 | return false; 47 | } 48 | 49 | if (param_start == 0) { 50 | printf("Malformed Proposition, missing root: %s\n", definition.c_str()); 51 | return false; 52 | } 53 | 54 | std::size_t param_end = definition.find(')', param_start+1); 55 | if (param_end == std::string::npos) { 56 | printf("Malformed Proposition, ')' expected: %s\n", definition.c_str()); 57 | return false; 58 | } 59 | 60 | if (param_end < definition.size()-1) { 61 | printf("Malformed Proposition, characters found after param: %s\n", definition.c_str()); 62 | return false; 63 | } 64 | 65 | // Tokenize the parameters 66 | std::string param_list = definition.substr(param_start+1); 67 | param_list.pop_back(); // Remove ')' 68 | 69 | typedef boost::tokenizer > tokenizer; 70 | 71 | boost::char_separator sep(","); 72 | tokenizer tokens(param_list, sep); 73 | 74 | for (tokenizer::iterator tok_iter = tokens.begin(); tok_iter != tokens.end(); ++tok_iter) { 75 | std::string param = *tok_iter; 76 | 77 | parameter new_param; 78 | if (!parseParam(param, &new_param)) { 79 | printf("Malformed Proposition, failed to parse param: %s\n", param.c_str()); 80 | return false; 81 | } 82 | 83 | params.push_back(new_param); 84 | } 85 | 86 | root = definition.substr(0, param_start); 87 | name = definition; 88 | 89 | return true; 90 | } 91 | 92 | void ActionPlanner::Proposition::buildName() 93 | { 94 | std::stringstream output; 95 | 96 | output << root << "("; 97 | 98 | for (size_t i = 0, size = params.size(); i < size; ++i) { 99 | if (i != 0) { 100 | output << ", "; 101 | } 102 | 103 | output << params.at(i).label; 104 | } 105 | 106 | output << ")"; 107 | name = output.str(); 108 | } 109 | 110 | bool ActionPlanner::Proposition::parseParam(std::string text, parameter* parameter) 111 | { 112 | boost::trim(text); 113 | 114 | parameter->negated = false; 115 | parameter->all = false; 116 | parameter->literal = false; 117 | parameter->set_members = NULL; 118 | 119 | // Check if empty 120 | if (text.empty()) { 121 | return true; 122 | } 123 | 124 | // Look for ':' separator and parse the two parts 125 | std::size_t separator = text.find(':'); 126 | bool separated = (separator != std::string::npos); 127 | 128 | if (separated) { 129 | parameter->entity_set_name = text.substr(separator+1); 130 | boost::trim(parameter->entity_set_name); 131 | 132 | if (parameter->entity_set_name.empty()) { 133 | printf("Malformed Proposition, empty string following ':'. Expected parameter type.\n."); 134 | return false; 135 | } 136 | } 137 | 138 | parameter->label = text.substr(0, separator); // is entire string if no separator 139 | boost::trim(parameter->label); 140 | if (parameter->label.empty()) { 141 | printf("Malformed Proposition, empty parameter label.\n."); 142 | return false; 143 | } 144 | 145 | // Parse for negated parameter 146 | if (parameter->label.front() == '~') { 147 | 148 | parameter->negated = true; 149 | parameter->label.erase(0,1); // Remove the '~' 150 | 151 | if (parameter->label.empty()) { 152 | printf("Malformed Proposition, negated parameter must reference an entity set.\n."); 153 | return false; 154 | } 155 | } // end negated parsing 156 | 157 | // Parse for literal 158 | if (parameter->label.front() == '$') { 159 | parameter->literal = true; 160 | parameter->label.erase(0,1); // remove the '$' 161 | 162 | // Must have entity set 163 | if (!separated) { 164 | printf("Malformed Proposition, literal parameter must reference an entity set.\n"); 165 | return false; 166 | } 167 | } // end literal parsing 168 | 169 | 170 | // Parse for all parameter 171 | if (parameter->label == "ALL") { 172 | 173 | // Cannot combine all with negated 174 | if (parameter->negated) { 175 | printf("Malformed Proposition, can't combine ""ALL"" with ""~""\n"); 176 | return false; 177 | } 178 | 179 | // Cannot combine all with literal 180 | if (parameter->literal) { 181 | printf("Malformed Proposition, can't combine ""ALL"" with ""$"" in parameter.\n"); 182 | return false; 183 | } 184 | 185 | // Must have entity set 186 | if (!separated) { 187 | printf("Malformed Proposition, ""ALL"" parameter must reference an entity set.\n"); 188 | return false; 189 | } 190 | 191 | parameter->all = true; 192 | } // end ALL parsing 193 | 194 | 195 | return true; 196 | } 197 | 198 | ActionPlanner::Proposition::parameter* ActionPlanner::Proposition::getParameter(std::string value) 199 | { 200 | parameter* param = NULL; 201 | std::vector::iterator it = std::find(params.begin(), params.end(), value); 202 | 203 | if (it != params.end()) { 204 | param = &(*it); 205 | } 206 | 207 | return param; 208 | } 209 | 210 | } // namespace goap -------------------------------------------------------------------------------- /src/task_manager.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : task_manager.cpp 3 | // Project : task_planning 4 | // Created : 5/25/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | #include "task_planning/task_manager.h" 32 | 33 | #include 34 | 35 | namespace goap { 36 | 37 | TaskManager::TaskManager(ActionPlanner planner) : 38 | planner_(planner) 39 | { 40 | action_counter_ = plan_.end(); 41 | 42 | // Initialize the worldstates 43 | planner_.updateWorldstateSize(&start_state_); 44 | planner_.updateWorldstateSize(¤t_state_); 45 | planner_.updateWorldstateSize(&goal_state_); 46 | } 47 | 48 | bool TaskManager::buildPlan(int* cost, std::vector* worldstates) 49 | { 50 | int plan_cost = planner_.createPlan(start_state_, goal_state_, &plan_, worldstates); 51 | 52 | if (plan_cost != -1) { 53 | action_counter_ = plan_.begin(); 54 | } 55 | 56 | *cost = plan_cost; 57 | 58 | return plan_cost != -1; 59 | } 60 | 61 | 62 | bool TaskManager::runPlan() 63 | { 64 | action_counter_ = plan_.begin(); 65 | current_state_ = start_state_; 66 | 67 | // Iterate until end of action stack 68 | while (action_counter_ != plan_.end()) { 69 | 70 | // Verify that preconditions are met. If not, skip this action 71 | bool is_possible; 72 | planner_.isActionPossible(¤t_state_, *action_counter_, &is_possible); 73 | if (!is_possible) { 74 | std::cout << "Error in TaskManager::runPlan. Preconditions not met for action " << *action_counter_ << std::endl; 75 | action_counter_++; 76 | } 77 | else { 78 | preActionFunction(); 79 | RETURN_TYPE ret = executeAction(*action_counter_); 80 | 81 | // Respond to the RETURN_TYPE of the action. 82 | switch (ret) { 83 | case CONTINUE: 84 | // Update worldstate 85 | planner_.doAction(*action_counter_, ¤t_state_, ¤t_state_); 86 | 87 | // Move to next action 88 | action_counter_++; 89 | break; 90 | case CONTINUE_FAILED: 91 | action_counter_++; 92 | break; 93 | case REPEAT: 94 | // Do not increment the action stack counter 95 | break; 96 | case BAD_CALL: 97 | std::cout << "In TaskManager::runPlan. Action " << *action_counter_ << " returned BAD_CALL. Exiting plan.\n"; 98 | // We do not break, so we continue to case FAIL_TERMINATE. 99 | case TERMINATE: 100 | action_counter_ = plan_.end(); 101 | return false; 102 | case REPLAN: 103 | { 104 | int cost; 105 | std::vector worldstates; 106 | start_state_ = current_state_; 107 | 108 | // Try to build new plan 109 | if (buildPlan(&cost, &worldstates)) { 110 | printf("New Action Plan:\n"); 111 | for (int i = 0; i < plan_.size(); ++i) { 112 | printf("%d: %s\n", i, plan_.at(i).c_str()); 113 | } 114 | 115 | // Start at beginning of new plan 116 | action_counter_ = plan_.begin(); 117 | break; 118 | } 119 | else { 120 | // No valid plan 121 | std::cout << "Error in TaskManager::runPlan. Failed to replan. Valid plan not found. Exiting plan.\n"; 122 | action_counter_ = plan_.end(); 123 | return false; 124 | } 125 | } 126 | default: 127 | // Invalid return type 128 | std::cout << "Error in TaskManager::runPlan. Unhandled return type " << ret << ". Exiting plan.\n"; 129 | action_counter_ = plan_.end(); 130 | return false; 131 | } 132 | } 133 | } 134 | 135 | // We reached the end of the action stack 136 | return true; 137 | } 138 | 139 | 140 | bool TaskManager::setActionCallback(std::string action_name, ActionCallback func) 141 | { 142 | // Check that action exists in the planner 143 | if (planner_.findAction(action_name) == NULL) { 144 | std::cout << "Error in TaskManager::setActionCallback. Action " << action_name << " not found.\n"; 145 | return false; 146 | } 147 | 148 | callback_funcs_[action_name] = func; 149 | return true; 150 | } 151 | 152 | 153 | bool TaskManager::setStartStateProposition(std::string atom, bool value) 154 | { 155 | return planner_.setProposition(&start_state_, atom, value); 156 | } 157 | 158 | bool TaskManager::setCurrentStateProposition(std::string atom, bool value) 159 | { 160 | return planner_.setProposition(¤t_state_, atom, value); 161 | } 162 | 163 | bool TaskManager::setGoalStateProposition(std::string atom, bool value) 164 | { 165 | return planner_.setProposition(&goal_state_, atom, value); 166 | } 167 | 168 | void TaskManager::clearStartState() 169 | { 170 | start_state_.clear(); 171 | } 172 | 173 | void TaskManager::clearCurrentState() 174 | { 175 | current_state_.clear(); 176 | } 177 | 178 | void TaskManager::clearGoalState() 179 | { 180 | goal_state_.clear(); 181 | } 182 | 183 | action_plan_t TaskManager::getPlan() const 184 | { 185 | return plan_; 186 | } 187 | 188 | const goap::ActionPlanner& TaskManager::getPlanner() const 189 | { 190 | return planner_; 191 | } 192 | 193 | bool TaskManager::getCurrentStateProposition(std::string atom, bool* result) const 194 | { 195 | return planner_.getPropositionValue(¤t_state_, atom, result); 196 | } 197 | 198 | void TaskManager::setStartStatetoCurrent() 199 | { 200 | start_state_ = current_state_; 201 | } 202 | 203 | std::string TaskManager::getCurrentAction() const 204 | { 205 | if (action_counter_ != plan_.end()) { 206 | return *action_counter_; 207 | } 208 | 209 | return ""; 210 | } 211 | 212 | TaskManager::RETURN_TYPE TaskManager::executeAction(std::string action) const 213 | { 214 | ActionCallback function; 215 | 216 | // Check that the function exists 217 | try { 218 | function = callback_funcs_.at(action); 219 | } catch(std::out_of_range) { 220 | return BAD_CALL; 221 | } 222 | 223 | // Execute callback function and return the result 224 | return function(); 225 | } 226 | 227 | void TaskManager::preActionFunction() const 228 | { 229 | std::cout << "Running action " << *action_counter_ << ".\n"; 230 | } 231 | 232 | } // namespace goap -------------------------------------------------------------------------------- /src/goap_test.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : goap_test.cpp 3 | // Project : task_planning 4 | // Created : 6/6/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | /* 32 | * Tests the goap::ActionPlanner class and its subsidiary classes. 33 | * The test simulates a waypoint survey task for a mobile robot. 34 | * Uses the GOAP 2.0 algorithm utilizing Action Description Language (ADL) 35 | * https://en.wikipedia.org/wiki/Action_description_language 36 | * 37 | * Usage: rosrun task_planning goap_test [num_waypoints] 38 | */ 39 | 40 | #include "task_planning/action_planner.h" 41 | 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | 48 | #include 49 | #include 50 | 51 | 52 | int main( int argc, char* argv[] ) 53 | { 54 | // Get number of waypoints from command line 55 | int num_waypoints = 5; // Default 56 | if( argc == 2 ) { 57 | num_waypoints = atoi(argv[1]); 58 | } 59 | 60 | // Declare ActionPlanner 61 | goap::ActionPlanner ap; 62 | 63 | boost::chrono::thread_clock clock; 64 | 65 | /* 66 | * Define actions using ADL: https://en.wikipedia.org/wiki/Action_description_language 67 | * 68 | * Action parameters are defined by (name:type). 69 | * This defines the action MoveRobot which takes three parameters. 70 | * Preconditions are propositions which must be met in order to perform the action. 71 | * Effects are propositions that will be set by the action. 72 | * Propositions can have parameters as well. Using a param named in the base definition will link the proposition to it. 73 | * 74 | * Requirements: 75 | * Definitions must use correct syntax. 76 | * Overloaded actions are not allowed; the root names must be unique. 77 | * An action must have at least one effect. 78 | * An action param must have both a name and type. 79 | * Proposition params only have a name. 80 | * Actions and propositions are allowed to take no parameters, ie MoveRobot(). 81 | * Negate a proposition by putting ~ in front. 82 | */ 83 | 84 | ap.addAction("Survey(r:robot, a:location)", // Base definition 85 | {"At(r,a)", "~Is_Surveyed(a)"}, // Precondition list 86 | {"Is_Surveyed(a)"}); // Effect list 87 | 88 | ap.addAction("MoveRobot(r:robot, destination:location)", 89 | {"~Docked(r)", "~At(r,destination)"}, 90 | {"At(r,destination)", "~At(r,~destination:location)"}); // negated parameter 91 | 92 | // Survey location_3 twice 93 | ap.addAction("DoubleCheck(r:robot)", 94 | {"At(r, $location_3:location)", "Is_Surveyed($location_3:location)"}, // literal parameters 95 | {"Double_Checked_3()"}); // void parameter 96 | 97 | 98 | ap.addAction("Dock(r:robot)", 99 | {"~Docked(r)"}, 100 | {"Docked(r)", "~At(r,ALL:location)"}); // this is an ALL parameter 101 | 102 | ap.addAction("Undock(r:robot)", 103 | {"Docked(r)"}, 104 | {"~Docked(r)"}); 105 | 106 | /* 107 | * Declare the entities in the worldspace 108 | * 109 | * addEntitySet( set_name, list_of_members ) 110 | * The sets are linked to the param types named in the action definitions. 111 | * EntitySets can be declared before the actions or vice versa; order doesn't matter. 112 | */ 113 | ap.addEntitySet("robot", {"Pioneer"}); 114 | 115 | /* 116 | * You can specify a range of entities. 117 | * This example will produce location_1, location_2... location_[num_waypoints} 118 | */ 119 | ap.addEntitySet("location", num_waypoints); 120 | 121 | /* 122 | * buildActionSpace() takes the declared actions and entities and compiles them. 123 | * 124 | * It creates a version of each actions for each valid permutations of their parameters. 125 | */ 126 | boost::chrono::thread_clock::time_point build_start = clock.now(); 127 | if (!ap.buildActionSpace()) { 128 | printf("Error: Failed to build action space."); 129 | return 1; 130 | } 131 | boost::chrono::duration build_duration = clock.now() - build_start; 132 | 133 | // Print out the created actions and propositions. 134 | std::cout << ap.description() << std::endl; 135 | 136 | /* 137 | * Define the initial worldstate. 138 | * Propositions in a worldspace are initialized to false. 139 | * Here we set the proposition At(Pioneer, location_1) = true 140 | */ 141 | goap::Worldstate start; 142 | ap.setProposition(&start, "Docked(Pioneer)", true); 143 | 144 | /* 145 | * Define the goal worldstate. 146 | * 147 | * You can also set a whole group of propositions using ADL. 148 | * Below we set Is_Surveyed(location) for all values of location. 149 | */ 150 | goap::Worldstate goal; 151 | ap.setProposition(&goal, "Is_Surveyed(ALL:location)", true); 152 | ap.setProposition(&goal, "Double_Checked_3()", true); 153 | ap.setProposition(&goal, "Docked(Pioneer)", true); 154 | 155 | // Print the starting worldstate 156 | std::string desc; 157 | desc = ap.worldstateDescription(&start); 158 | printf("Start: %s\n", desc.c_str()); 159 | 160 | // Print the goal worldstate 161 | desc = ap.worldstateDescription(&goal); 162 | printf("GOAL: %s", desc.c_str()); 163 | 164 | std::vector states; 165 | goap::action_plan_t plan; 166 | 167 | // Create the action plan 168 | boost::chrono::thread_clock::time_point planning_start = clock.now(); 169 | const int cost = ap.createPlan(start, goal, &plan, &states); 170 | boost::chrono::duration planning_duration = clock.now() - planning_start; 171 | 172 | // Print the action plan 173 | printf("ACTION PLAN\nPlan cost = %d\n", cost); 174 | for (int i = 0; i < plan.size(); ++i) 175 | { 176 | desc = ap.worldstateDescription(&states.at(i)); 177 | printf("%d: %-20s\n", i, plan.at(i).c_str()); 178 | } 179 | 180 | printf("Build time: %f secs\n", build_duration.count()); 181 | printf("Plan time: %f secs\n", planning_duration.count()); 182 | 183 | return 0; 184 | } -------------------------------------------------------------------------------- /src/action.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : action.cpp 3 | // Project : task_planning 4 | // Created : 5/25/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | namespace goap { 38 | 39 | ActionPlanner::Action::Action() { 40 | cost = 0; 41 | cost_func = NULL; 42 | } 43 | 44 | ActionPlanner::Action::Action(int num_atoms) : 45 | Action() 46 | { 47 | pre.initialize(num_atoms); 48 | pst.initialize(num_atoms); 49 | } 50 | 51 | bool ActionPlanner::Action::parse(std::string definition, string_vec preconditions, string_vec effects) 52 | { 53 | /** Parse the definition **/ 54 | boost::trim(definition); 55 | 56 | // Find the start of the parameter list 57 | std::size_t param_start = definition.find('('); 58 | if (param_start == std::string::npos) { 59 | printf("Malformed action %s. '(' expected.\n", definition.c_str()); 60 | return false; 61 | } 62 | 63 | if (param_start == 0) { 64 | printf("Malformed action %s. Missing root.\n", definition.c_str()); 65 | return false; 66 | } 67 | 68 | std::size_t param_end = definition.find(')', param_start+1); 69 | if (param_end == std::string::npos) { 70 | printf("Malformed action %s. ')' expected.\n", definition.c_str()); 71 | return false; 72 | } 73 | 74 | if (param_end < definition.size()-1) { 75 | printf("Malformed action %s. Characters found after param list.\n", definition.c_str()); 76 | return false; 77 | } 78 | 79 | root = definition.substr(0, param_start); 80 | name = definition; 81 | 82 | // Tokenize the parameters 83 | std::string param_list = definition.substr(param_start+1); 84 | param_list.pop_back(); 85 | 86 | typedef boost::tokenizer > tokenizer; 87 | 88 | boost::char_separator sep(","); 89 | tokenizer tokens(param_list, sep); 90 | 91 | for (tokenizer::iterator tok_iter = tokens.begin(); tok_iter != tokens.end(); ++tok_iter) { 92 | std::string param = *tok_iter; 93 | 94 | // Separate the label and type 95 | std::size_t param_separator = param.find(':'); 96 | std::string label = param.substr(0, param_separator); 97 | std::string type = param.substr(param_separator+1); 98 | 99 | boost::trim(label); 100 | boost::trim(type); 101 | 102 | // check if this parameter already exists 103 | parameter* new_param = getParameter(label); 104 | if (new_param != NULL) { 105 | printf("Malformed action %s. Multiple params with label %s.\n", definition.c_str(), label.c_str()); 106 | return false; 107 | } 108 | 109 | // build parameter struct and add to list 110 | new_param = new parameter; 111 | new_param->label = label; 112 | new_param->type = type; 113 | new_param->set_members = NULL; 114 | params.push_back(*new_param); 115 | delete new_param; 116 | } 117 | 118 | /** Add preconditions **/ 119 | BOOST_FOREACH(std::string precondition, preconditions) { 120 | if (!buildPrecondition(precondition)) { 121 | return false; 122 | } 123 | } 124 | 125 | /** Add effects **/ 126 | if (effects.empty()) { 127 | printf("Malformed action, no effects found.\n"); 128 | return false; 129 | } 130 | BOOST_FOREACH(std::string effect, effects) { 131 | if (!buildEffect(effect)) { 132 | return false; 133 | } 134 | } 135 | 136 | return true; 137 | } 138 | 139 | bool ActionPlanner::Action::buildPrecondition(std::string definition) 140 | { 141 | boost::trim(definition); 142 | 143 | bool req_value; 144 | 145 | // Check if first char is '~' indicating NOT 146 | if (definition.at(0) == '~') { 147 | definition.erase(0,1); // erase first character 148 | req_value = false; 149 | } 150 | else { 151 | req_value = true; 152 | } 153 | 154 | // Parse the definition 155 | Proposition new_prop; 156 | if (!new_prop.parse(definition)) { 157 | return false; 158 | } 159 | 160 | preconditions.push_back(condition_t(new_prop, req_value)); 161 | 162 | return true; 163 | } 164 | 165 | bool ActionPlanner::Action::buildEffect(std::string definition) 166 | { 167 | boost::trim(definition); 168 | 169 | bool set_value; 170 | 171 | // Check if first char is '~' indicating NOT 172 | if (definition.at(0) == '~') { 173 | definition.erase(0,1); // erase first character 174 | set_value = false; 175 | } 176 | else { 177 | set_value = true; 178 | } 179 | 180 | // Parse the definition 181 | Proposition new_prop; 182 | if (!new_prop.parse(definition)) { 183 | return false; 184 | } 185 | 186 | effects.push_back(condition_t(new_prop, set_value)); 187 | 188 | return true; 189 | } 190 | 191 | void ActionPlanner::Action::buildName() 192 | { 193 | std::stringstream output; 194 | 195 | output << root << "("; 196 | 197 | for (Action::param_vec::iterator param = params.begin(); param != params.end(); param++) { 198 | if (param != params.begin()) { 199 | output << ", "; 200 | } 201 | 202 | output << param->label; 203 | } 204 | 205 | output << ")"; 206 | 207 | name = output.str(); 208 | } 209 | 210 | void ActionPlanner::Action::set_cost(int val) 211 | { 212 | cost = val; 213 | cost_func = NULL; 214 | } 215 | 216 | void ActionPlanner::Action::set_cost(cost_function func) 217 | { 218 | cost_func = func; 219 | } 220 | 221 | int ActionPlanner::Action::get_cost(const ActionPlanner* ap, const Worldstate* ws) const 222 | { 223 | if (cost_func != NULL) { 224 | return cost_func(ap, ws); 225 | } 226 | return cost; 227 | } 228 | 229 | ActionPlanner::Action::parameter* ActionPlanner::Action::getParameter(param_label label) 230 | { 231 | parameter* param = NULL; 232 | param_vec::iterator it = std::find(params.begin(), params.end(), label); 233 | 234 | if (it != params.end()) { 235 | param = &(*it); 236 | } 237 | 238 | return param; 239 | } 240 | 241 | } // namespace goap -------------------------------------------------------------------------------- /include/task_planning/task_manager.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : task_manager.h 3 | // Project : task_planning 4 | // Created : 5/25/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | #ifndef GOAP_TASK_MANAGER_H 32 | #define GOAP_TASK_MANAGER_H 33 | 34 | #include "action_planner.h" 35 | 36 | #include 37 | #include 38 | 39 | #include 40 | 41 | namespace goap { 42 | 43 | /* 44 | * Class TaskManager - Encapsulate the planning and execution of complex tasks using GOAP. 45 | * 46 | * Actions are associated with callback functions (typedef ActionCallback). The action stack 47 | * is run in a loop which reacts to the return codes (enum RETURN_TYPE) of the callback functions. 48 | */ 49 | class TaskManager 50 | { 51 | public: 52 | 53 | /* 54 | * Callback functions must return this type. 55 | * CONTINUE - Action executed successfully. Modify internal worldstate and continue to next action. 56 | * CONTINUE_FAILED - Action failed, but continue anyway. Does not modify the internal worldstate. Subsequent actions may not be possible. 57 | * REPEAT - Action did not execute successfully. Try again. 58 | * TERMINATE - Action did not execute successfully. End the action plan. 59 | * REPLAN - Update the plan and continue. Use this if an external change occurs to the worldstate which we must account for. 60 | * BAD_CALL - Use to flag errors unrelated to the actual execution of the action. A useful distinction for debugging. 61 | */ 62 | enum RETURN_TYPE {CONTINUE, CONTINUE_FAILED, REPEAT, TERMINATE, REPLAN, BAD_CALL}; 63 | 64 | // Action callback function with zero parameters 65 | typedef std::function ActionCallback; 66 | 67 | // Action callback function with one parameter 68 | template 69 | using ActionCallback_1 = std::function; 70 | 71 | // Action callback function with two parameters 72 | template 73 | using ActionCallback_2 = std::function; 74 | 75 | // Action callback function with three parameters 76 | template 77 | using ActionCallback_3 = std::function; 78 | 79 | // Action callback function with four parameters 80 | template 81 | using ActionCallback_4 = std::function; 82 | 83 | // Action callback function with five parameters 84 | template 85 | using ActionCallback_5 = std::function; 86 | 87 | /** 88 | * @brief Constructor 89 | * @param planner The planner to associate with this TaskManager. Immutable after construction. 90 | */ 91 | TaskManager(ActionPlanner planner); 92 | 93 | /** 94 | * @brief Calls the createPlan method of planner_ 95 | * @param cost Outputs the cost of the created plan. 96 | * @param worldstates Outputs the worldstate sequence of the created plan. 97 | * @return True on success. False if no valid plan was found. 98 | */ 99 | bool buildPlan(int* cost, std::vector* worldstates); 100 | 101 | /** 102 | * @brief Execute the action plan. 103 | * @return True if the end of the plan was reached. False if an error, BAD_CALL or TERMINATE were encountered. 104 | */ 105 | bool runPlan(); 106 | 107 | /** 108 | * @brief Associate a callback function with the specified action 109 | * @param action_name The name of the action. 110 | * @param func The callback function to associate with action. See ActionCallback definition. 111 | * @return True on success. False if the action was not found. 112 | */ 113 | bool setActionCallback(std::string action_name, ActionCallback func); 114 | 115 | template 116 | bool setActionCallback(std::string action_name, ActionCallback_1 func, std::vector first); 117 | 118 | template 119 | bool setActionCallback(std::string action_name, ActionCallback_2 func, std::vector first, std::vector second); 120 | 121 | template 122 | bool setActionCallback(std::string action_name, ActionCallback_3 func, std::vector first, std::vector second, std::vector third); 123 | 124 | template 125 | bool setActionCallback(std::string action_name, ActionCallback_4 func, std::vector first, std::vector second, std::vector third, std::vector fourth); 126 | 127 | template 128 | bool setActionCallback(std::string action_name, ActionCallback_5 func, std::vector first, std::vector second, std::vector third, std::vector fourth, std::vector fifth); 129 | 130 | /** 131 | * @brief Set an atom of the start state. The atom must exist in the planner. 132 | * @param atom Name of the atom to be modified 133 | * @param value The value to be set 134 | * @return True on success. 135 | */ 136 | bool setStartStateProposition(std::string atom, bool value); 137 | 138 | /** 139 | * @brief Set an atom of the current state. The atom must exist in the planner. 140 | * @param atom Name of the atom to be modified 141 | * @param value The value to be set 142 | * @return True on success. 143 | */ 144 | bool setCurrentStateProposition(std::string atom, bool value); 145 | 146 | /** 147 | * @brief Set an atom of the goal state. The atom must exist in the planner. 148 | * @param atom Name of the atom to be modified 149 | * @param value The value to be set 150 | * @return True on success. 151 | */ 152 | bool setGoalStateProposition(std::string atom, bool value); 153 | 154 | /** 155 | * @brief Clear the start state. 156 | */ 157 | void clearStartState(); 158 | 159 | /** 160 | * @brief Clear the current state. 161 | */ 162 | void clearCurrentState(); 163 | 164 | /** 165 | * @brief Clear the goal state. 166 | */ 167 | void clearGoalState(); 168 | 169 | /** 170 | * @brief Return the plan held by the manager. 171 | * @return The action plan. 172 | */ 173 | action_plan_t getPlan() const; 174 | 175 | /** 176 | * @brief Return const reference to the action planner. 177 | * @return The action planner 178 | */ 179 | const goap::ActionPlanner& getPlanner() const; 180 | 181 | /** 182 | * @brief Get the current value of an atom. 183 | * @param atom Name of the atom to be retrieved 184 | * @param result Outputs the value of the atom 185 | * @return True on success. 186 | */ 187 | bool getCurrentStateProposition(std::string atom, bool* result) const; 188 | 189 | /** 190 | * @breif Sets the start state equal to the current state 191 | */ 192 | void setStartStatetoCurrent(); 193 | 194 | /** 195 | * @brief Get the name of the current action while the plan is running. 196 | * @return The action name. Returns empty string if the plan is not running. 197 | */ 198 | std::string getCurrentAction() const; 199 | 200 | private: 201 | 202 | /** 203 | * @brief Calls the callback function of the given action. 204 | * @param action The action to execute 205 | * @return Echos the return type of the callback function. Returns BAD_CALL if the action has no associated callback. 206 | */ 207 | RETURN_TYPE executeAction(std::string action) const; 208 | 209 | /** 210 | * @brief This function is called prior to each action when running the action stack. 211 | */ 212 | virtual void preActionFunction() const; 213 | 214 | bool actionPermuteHelper(std::string action_name, ActionPlanner::action_vector* permuted_actions); 215 | 216 | // GOAP planner used by this manager. It is ummutable after the TaskManager object is constructed. 217 | const ActionPlanner planner_; 218 | 219 | // Stores generated action plans. 220 | action_plan_t plan_; 221 | 222 | // Stores the current location in the action stack. 223 | std::vector::iterator action_counter_; 224 | 225 | // Track the necessary world states 226 | Worldstate start_state_, current_state_, goal_state_; 227 | 228 | // Stores the callback functions associated with the actions 229 | std::unordered_map callback_funcs_; 230 | }; 231 | 232 | /** Template definitions **/ 233 | #include "task_manager.tpp" 234 | 235 | } // namespace goap 236 | 237 | #endif -------------------------------------------------------------------------------- /include/task_planning/task_manager.tpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : task_manager.tpp 3 | // Project : task_planning 4 | // Created : 6/12/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | // Contains the template definitions for TaskManager 32 | 33 | template 34 | bool TaskManager::setActionCallback(std::string action_name, ActionCallback_1 func, std::vector first) 35 | { 36 | const std::size_t arrity = 0; 37 | 38 | ActionPlanner::action_vector permuted_actions; 39 | 40 | if (!actionPermuteHelper(action_name, &permuted_actions)) { 41 | return false; 42 | } 43 | 44 | BOOST_FOREACH (ActionPlanner::Action action, permuted_actions) { 45 | const ActionPlanner::Action::parameter* param; 46 | try { 47 | param = &action.params.at(arrity); 48 | } catch (std::out_of_range) { 49 | std::cout << "TaskManager::setActionCallback failed. Expected action with " << arrity+1 << " parameter(s)." << std::endl; 50 | return false; 51 | } 52 | 53 | // Get index of parameter in entity list 54 | std::vector::const_iterator it = std::find(param->set_members->begin(), param->set_members->end(), param->label); 55 | std::size_t index = it - param->set_members->begin(); 56 | 57 | A* func_arg; 58 | try { 59 | func_arg = &first.at(index); 60 | } catch (std::out_of_range) { 61 | std::cout << "TaskManager::setActionCallback failed. Received insufficient arguments in vector." << std::endl; 62 | return false; 63 | } 64 | 65 | ActionCallback new_func = std::bind(func, *func_arg); 66 | if (!setActionCallback(action.name, new_func)) { 67 | return false; 68 | } 69 | } 70 | return true; 71 | } 72 | 73 | 74 | template 75 | bool TaskManager::setActionCallback(std::string action_name, ActionCallback_2 func, std::vector first, std::vector second) 76 | { 77 | const std::size_t arrity = 1; 78 | 79 | ActionPlanner::action_vector permuted_actions; 80 | 81 | if (!actionPermuteHelper(action_name, &permuted_actions)) { 82 | return false; 83 | } 84 | 85 | BOOST_FOREACH (ActionPlanner::Action action, permuted_actions) { 86 | const ActionPlanner::Action::parameter* param; 87 | try { 88 | param = &action.params.at(arrity); 89 | } catch (std::out_of_range) { 90 | std::cout << "TaskManager::setActionCallback failed. Expected action with " << arrity+1 << " parameter(s)." << std::endl; 91 | return false; 92 | } 93 | 94 | // Get index of parameter in entity list 95 | std::vector::const_iterator it = std::find(param->set_members->begin(), param->set_members->end(), param->label); 96 | std::size_t index = it - param->set_members->begin(); 97 | 98 | B* func_arg; 99 | try { 100 | func_arg = &second.at(index); 101 | } catch (std::out_of_range) { 102 | std::cout << "TaskManager::setActionCallback failed. Received insufficient arguments in vector." << std::endl; 103 | return false; 104 | } 105 | 106 | ActionCallback_1 new_func = std::bind(func, std::placeholders::_1, *func_arg); 107 | if (!setActionCallback(action.name, new_func, first)) { 108 | return false; 109 | } 110 | } 111 | return true; 112 | } 113 | 114 | 115 | template 116 | bool TaskManager::setActionCallback(std::string action_name, ActionCallback_3 func, std::vector first, std::vector second, std::vector third) 117 | { 118 | const std::size_t arrity = 2; 119 | 120 | ActionPlanner::action_vector permuted_actions; 121 | 122 | if (!actionPermuteHelper(action_name, &permuted_actions)) { 123 | return false; 124 | } 125 | 126 | BOOST_FOREACH (ActionPlanner::Action action, permuted_actions) { 127 | const ActionPlanner::Action::parameter* param; 128 | try { 129 | param = &action.params.at(arrity); 130 | } catch (std::out_of_range) { 131 | std::cout << "TaskManager::setActionCallback failed. Expected action with " << arrity+1 << " parameter(s)." << std::endl; 132 | return false; 133 | } 134 | 135 | // Get index of parameter in entity list 136 | std::vector::const_iterator it = std::find(param->set_members->begin(), param->set_members->end(), param->label); 137 | std::size_t index = it - param->set_members->begin(); 138 | 139 | C* func_arg; 140 | try { 141 | func_arg = &third.at(index); 142 | } catch (std::out_of_range) { 143 | std::cout << "TaskManager::setActionCallback failed. Received insufficient arguments in vector." << std::endl; 144 | return false; 145 | } 146 | 147 | ActionCallback_2 new_func = std::bind(func, std::placeholders::_1, std::placeholders::_2, *func_arg); 148 | if (!setActionCallback(action.name, new_func, first, second)) { 149 | return false; 150 | } 151 | } 152 | return true; 153 | } 154 | 155 | 156 | template 157 | bool TaskManager::setActionCallback(std::string action_name, ActionCallback_4 func, std::vector first, std::vector second, std::vector third, std::vector fourth) 158 | { 159 | const std::size_t arrity = 3; 160 | 161 | ActionPlanner::action_vector permuted_actions; 162 | 163 | if (!actionPermuteHelper(action_name, &permuted_actions)) { 164 | return false; 165 | } 166 | 167 | BOOST_FOREACH (ActionPlanner::Action action, permuted_actions) { 168 | const ActionPlanner::Action::parameter* param; 169 | try { 170 | param = &action.params.at(arrity); 171 | } catch (std::out_of_range) { 172 | std::cout << "TaskManager::setActionCallback failed. Expected action with " << arrity+1 << " parameter(s)." << std::endl; 173 | return false; 174 | } 175 | 176 | // Get index of parameter in entity list 177 | std::vector::const_iterator it = std::find(param->set_members->begin(), param->set_members->end(), param->label); 178 | std::size_t index = it - param->set_members->begin(); 179 | 180 | D* func_arg; 181 | try { 182 | func_arg = &fourth.at(index); 183 | } catch (std::out_of_range) { 184 | std::cout << "TaskManager::setActionCallback failed. Received insufficient arguments in vector." << std::endl; 185 | return false; 186 | } 187 | 188 | ActionCallback_3 new_func = std::bind(func, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, *func_arg); 189 | if (!setActionCallback(action.name, new_func, first, second, third)) { 190 | return false; 191 | } 192 | } 193 | return true; 194 | } 195 | 196 | template 197 | bool TaskManager::setActionCallback(std::string action_name, ActionCallback_5 func, std::vector first, std::vector second, std::vector third, std::vector fourth, std::vector fifth) 198 | { 199 | const std::size_t arrity = 4; 200 | 201 | ActionPlanner::action_vector permuted_actions; 202 | 203 | if (!actionPermuteHelper(action_name, &permuted_actions)) { 204 | return false; 205 | } 206 | 207 | BOOST_FOREACH (ActionPlanner::Action action, permuted_actions) { 208 | const ActionPlanner::Action::parameter* param; 209 | try { 210 | param = &action.params.at(arrity); 211 | } catch (std::out_of_range) { 212 | std::cout << "TaskManager::setActionCallback failed. Expected action with " << arrity+1 << " parameter(s)." << std::endl; 213 | return false; 214 | } 215 | 216 | // Get index of parameter in entity list 217 | std::vector::const_iterator it = std::find(param->set_members->begin(), param->set_members->end(), param->label); 218 | std::size_t index = it - param->set_members->begin(); 219 | 220 | E* func_arg; 221 | try { 222 | func_arg = &fifth.at(index); 223 | } catch (std::out_of_range) { 224 | std::cout << "TaskManager::setActionCallback failed. Received insufficient arguments in vector." << std::endl; 225 | return false; 226 | } 227 | 228 | ActionCallback_4 new_func = std::bind(func, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, *func_arg); 229 | if (!setActionCallback(action.name, new_func, first, second, third, fourth)) { 230 | return false; 231 | } 232 | } 233 | return true; 234 | } 235 | 236 | bool TaskManager::actionPermuteHelper(std::string action_name, ActionPlanner::action_vector* permuted_actions) 237 | { 238 | 239 | permuted_actions->clear(); 240 | 241 | // Look in built actions 242 | const ActionPlanner::Action* action_ptr = planner_.findAction(action_name); 243 | 244 | // If found 245 | if (action_ptr != NULL) { 246 | permuted_actions->push_back(*action_ptr); 247 | } 248 | else { 249 | // Look in unbuilt actions 250 | action_ptr = planner_.findUnbuiltAction(action_name); 251 | // If found 252 | if (action_ptr != NULL) { 253 | ActionPlanner::Action action = *action_ptr; 254 | 255 | if (!planner_.linkEntities(action)) { 256 | return false; 257 | } 258 | 259 | // Permute 260 | if (!planner_.permuteAction(action, permuted_actions)) { 261 | return false; 262 | } 263 | 264 | } 265 | else { 266 | std::cout << "setActionCallback() - Action " << action_name << " not found." << std::endl; 267 | return false; 268 | } 269 | } 270 | 271 | return true; 272 | } -------------------------------------------------------------------------------- /src/goap.cpp: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------------- 2 | goap.cpp 3 | Version: In Development 4 | Commit Date: Never 5 | 6 | Authors: Blake Anderson 7 | The University of Texas at Austin 8 | Department of Mechanical Engineering 9 | Nuclear and Applied Robotics Group 10 | 11 | Modified from code by Abraham Stolk. 12 | 13 | Description: The GOAP class provides an interface for defining a Goal Oriented Action Planner. 14 | 15 | Command Line Arguments: None 16 | -------------------------------------------------------------------------------*/ 17 | 18 | /* 19 | Copyright 2012 Abraham T. Stolk 20 | 21 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 22 | 23 | http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 26 | */ 27 | 28 | #include "task_planning/goap.h" 29 | 30 | namespace goap 31 | { 32 | 33 | GOAP::GOAP() {} 34 | 35 | bool GOAP::load_json(std::string filename, actionplanner_t* ap) 36 | { 37 | Json::Value root; 38 | Json::Reader reader; 39 | std::ifstream json_stream(filename.c_str(), std::ifstream::in); 40 | bool parse_success = reader.parse(json_stream, root); 41 | if ( !parse_success ) 42 | { 43 | std::stringstream error_ss; 44 | std::cout << "Failed to parse JSON file\n" << reader.getFormattedErrorMessages(); 45 | 46 | return false; 47 | } 48 | 49 | // Clear old planner data 50 | goap_actionplanner_clear(ap); 51 | 52 | // Iterate over the actions 53 | for (Json::Value::iterator action = root.begin(); action != root.end(); action++) { 54 | std::string action_name = action.memberName(); 55 | 56 | Json::Value pre = (*action)["pre"]; 57 | Json::Value pst = (*action)["pst"]; 58 | 59 | // Iterate over preconditions 60 | for (Json::Value::iterator precondition = pre.begin(); precondition != pre.end(); precondition++) { 61 | 62 | std::string atom = (*precondition)["atom"].asString(); 63 | bool value = (*precondition)["value"].asBool(); 64 | goap_set_pre(ap, action_name, atom, value); 65 | } 66 | 67 | // Iterate over postconditions 68 | for (Json::Value::iterator postconditions = pst.begin(); postconditions != pst.end(); postconditions++) { 69 | 70 | std::string atom = (*postconditions)["atom"].asString(); 71 | bool value = (*postconditions)["value"].asBool(); 72 | goap_set_pst(ap, action_name, atom, value); 73 | } 74 | } 75 | 76 | return true; 77 | } 78 | 79 | bool GOAP::find_atom_index(actionplanner_t* ap, std::string atomname, int* index) 80 | { 81 | std::vector *vec_ptr = &(ap->atm_names); 82 | if (vec_ptr->empty()) 83 | return false; 84 | 85 | for (int i = 0; i < vec_ptr->size(); i++) { 86 | if (vec_ptr->at(i) == atomname) { 87 | *index = i; 88 | return true; 89 | } 90 | } 91 | 92 | return false; 93 | } 94 | 95 | bool GOAP::find_action_index(actionplanner_t* ap, std::string actionname, int* index) 96 | { 97 | std::vector *vec_ptr = &(ap->actions); 98 | if (vec_ptr->empty()) 99 | return false; 100 | 101 | for (int i = 0; i < vec_ptr->size(); i++) { 102 | if (vec_ptr->at(i).name == actionname) { 103 | *index = i; 104 | return true; 105 | } 106 | } 107 | 108 | return false; 109 | } 110 | 111 | bool GOAP::add_atom_to_planner(actionplanner_t* ap, std::string atomname, int* index) 112 | { 113 | if (!find_atom_index(ap, atomname, index)) { 114 | ap->atm_names.push_back(atomname); 115 | *index = ap->atm_names.size() - 1; 116 | 117 | std::size_t num_atms = ap->atm_names.size(); 118 | 119 | for (std::vector::iterator it = ap->actions.begin(); it != ap->actions.end(); it++) { 120 | goap_worldstate_clear(&it->pre, num_atms); 121 | goap_worldstate_clear(&it->pst, num_atms); 122 | } 123 | 124 | return true; 125 | } 126 | 127 | return false; 128 | } 129 | 130 | bool GOAP::add_action_to_planner(actionplanner_t* ap, std::string actionname, int* index) 131 | { 132 | if (!find_action_index(ap, actionname, index)) { 133 | action_t new_act; 134 | new_act.name = actionname; 135 | 136 | std::size_t num_atms = ap->atm_names.size(); 137 | goap_worldstate_clear(&new_act.pre, num_atms); 138 | goap_worldstate_clear(&new_act.pst, num_atms); 139 | 140 | ap->actions.push_back(new_act); 141 | 142 | int f = ap->actions.size(); 143 | *index = ap->actions.size() - 1; 144 | return true; 145 | } 146 | 147 | return false; 148 | } 149 | 150 | void GOAP::goap_actionplanner_clear(actionplanner_t* ap) 151 | { 152 | ap->atm_names.clear(); 153 | ap->actions.clear(); 154 | } 155 | 156 | void GOAP::goap_worldstate_clear(worldstate_t* ws, std::size_t new_size) 157 | { 158 | ws->values.resize(new_size, false); 159 | ws->care.resize(new_size, false); 160 | } 161 | 162 | bool GOAP::goap_worldstate_set(actionplanner_t* ap, worldstate_t* ws, std::string atomname, bool value) 163 | { 164 | int index; 165 | if(!find_atom_index(ap, atomname, &index)) { 166 | printf("Error in GOAP::goap_worldstate_set: Atom %s not found in action planner.\n", atomname.c_str()); 167 | return false; 168 | } 169 | 170 | std::size_t num_atms = ap->atm_names.size(); 171 | goap_worldstate_clear(ws, num_atms); 172 | 173 | ws->values[index] = value; 174 | ws->care[index] = true; 175 | 176 | return true; 177 | } 178 | 179 | bool GOAP::goap_get_atom_value(actionplanner_t* ap, worldstate_t* ws, std::string atomname, bool* result) { 180 | int index; 181 | if(!find_atom_index(ap, atomname, &index)) { 182 | printf("Error in GOAP::goap_get_atom_value: Atom %s not found in action planner.\n", atomname.c_str()); 183 | return false; 184 | } 185 | 186 | *result = ws->values[index]; 187 | return true; 188 | } 189 | 190 | void GOAP::goap_set_pre(actionplanner_t* ap, std::string actionname, std::string atomname, bool value) 191 | { 192 | int action_index; 193 | add_action_to_planner(ap, actionname, &action_index); 194 | 195 | int atom_index; 196 | add_atom_to_planner(ap, atomname, &atom_index); 197 | 198 | worldstate_t* preconditions = &ap->actions.at(action_index).pre; 199 | goap_worldstate_set(ap, preconditions, atomname, value); 200 | } 201 | 202 | void GOAP::goap_set_pst(actionplanner_t* ap, std::string actionname, std::string atomname, bool value) 203 | { 204 | int action_index; 205 | add_action_to_planner(ap, actionname, &action_index); 206 | 207 | int atom_index; 208 | add_atom_to_planner(ap, atomname, &atom_index); 209 | 210 | worldstate_t* postconditions = &ap->actions.at(action_index).pst; 211 | goap_worldstate_set(ap, postconditions, atomname, value); 212 | } 213 | 214 | bool GOAP::goap_set_cost(actionplanner_t* ap, std::string actionname, float cost) 215 | { 216 | int index; 217 | if (!find_action_index(ap, actionname, &index)) { 218 | printf("Error in GOAP::goap_set_cost: Action %s not found in action planner.\n", actionname.c_str()); 219 | return false; 220 | } 221 | 222 | ap->actions.at(index).set_cost(cost); 223 | return true; 224 | } 225 | 226 | bool GOAP::goap_set_cost(actionplanner_t* ap, std::string actionname, float (*func)(actionplanner_t* ap, worldstate_t* ws)) 227 | { 228 | int index; 229 | if (!find_action_index(ap, actionname, &index)) { 230 | printf("Error in GOAP::goap_set_cost: Action %s not found in action planner.\n", actionname.c_str()); 231 | return false; 232 | } 233 | 234 | ap->actions.at(index).set_cost(func); 235 | return true; 236 | } 237 | 238 | void GOAP::goap_worldstate_description(actionplanner_t* ap, worldstate_t* ws, std::string* output) 239 | { 240 | goap_worldstate_clear(ws, ap->atm_names.size()); 241 | 242 | std::stringstream sstream; 243 | for (uint i = 0; i < ap->atm_names.size(); i++) { 244 | 245 | if (ws->care[i]) { 246 | std::string atom_name = ap->atm_names.at(i); 247 | 248 | if (ws->values[i]) { 249 | boost::to_upper(atom_name); 250 | } 251 | 252 | sstream << atom_name << ", "; 253 | } 254 | } 255 | *output = sstream.str(); 256 | } 257 | 258 | void GOAP::goap_description(actionplanner_t* ap, std::string* output) 259 | { 260 | std::stringstream sstream; 261 | for (std::vector::iterator it = ap->actions.begin(); it != ap->actions.end(); it++) { 262 | sstream << *output << it->name << "\n" << "Preconditions\n"; 263 | 264 | worldstate_t* pre = &(it->pre); 265 | worldstate_t* pst = &(it->pst); 266 | 267 | for (std::size_t i = 0; i < pre->values.size(); i++) { 268 | if (pre->care[i]) 269 | sstream << *output << ap->atm_names.at(i) << ": " << pre->values[i] << "\n"; 270 | } 271 | sstream << "Postconditions\n"; 272 | 273 | for (std::size_t i = 0; i < pst->values.size(); i++) { 274 | if (pst->care[i]) 275 | sstream << ap->atm_names.at(i) << ": " << pst->values[i] << "\n"; 276 | } 277 | } 278 | *output = sstream.str(); 279 | } 280 | 281 | 282 | bool GOAP::do_action(actionplanner_t* ap, std::string actionname, worldstate_t fr, worldstate_t* to) 283 | { 284 | int index; 285 | if (find_action_index(ap, actionname, &index)) { 286 | 287 | do_action(ap, index, fr, to); 288 | return true; 289 | } 290 | 291 | return false; 292 | } 293 | 294 | int GOAP::calc_h(const worldstate_t* fr, const worldstate_t* to ) 295 | { 296 | const boost::dynamic_bitset<> diff = ( (fr->values ^ to->values) & to->care ); 297 | return (int)diff.count(); 298 | } 299 | 300 | bool GOAP::idx_in_opened(const worldstate_t* ws, int* index) 301 | { 302 | for (int i = 0; i < opened.size(); i++) { 303 | if (opened.at(i).ws.values == ws->values) { 304 | *index = i; 305 | return true; 306 | } 307 | } 308 | return false; 309 | } 310 | 311 | bool GOAP::idx_in_closed(const worldstate_t* ws, int* index) 312 | { 313 | for (int i = 0; i < closed.size(); i++) { 314 | if (closed.at(i).ws.values == ws->values) { 315 | *index = i; 316 | return true; 317 | } 318 | } 319 | return false; 320 | } 321 | 322 | void GOAP::reconstruct_plan(actionplanner_t* ap, astarnode_t* goalnode, ActionPlan_t* plan, std::vector* worldstates) 323 | { 324 | astarnode_t* curnode = goalnode; 325 | while (curnode != NULL && curnode->actionname != "start") 326 | { 327 | plan->insert(plan->begin(), curnode->actionname); 328 | worldstates->insert(worldstates->begin(), curnode->ws); 329 | 330 | int index; 331 | if (idx_in_closed(&curnode->parentws, &index)) 332 | curnode = &closed.at(index); 333 | else 334 | curnode = NULL; 335 | } 336 | } 337 | 338 | std::vector::iterator GOAP::get_lowest_rank() 339 | { 340 | std::vector::iterator lowest_rank = opened.begin(); 341 | for (std::vector::iterator it = opened.begin(); it != opened.end(); it++) { 342 | if (lowest_rank->f > it->f) 343 | lowest_rank = it; 344 | } 345 | return lowest_rank; 346 | } 347 | 348 | int GOAP::astar_plan 349 | ( 350 | actionplanner_t* ap, 351 | worldstate_t start, 352 | worldstate_t goal, 353 | ActionPlan_t* plan, 354 | std::vector* worldstates 355 | ) 356 | { 357 | // put start in opened list 358 | astarnode_t n0; 359 | n0.ws = start; 360 | n0.parentws = start; 361 | n0.g = 0; 362 | n0.h = calc_h(&start, &goal); 363 | n0.f = n0.g + n0.h; 364 | n0.actionname = "start"; 365 | opened.clear(); 366 | closed.clear(); 367 | opened.push_back(n0); 368 | 369 | do 370 | { 371 | if (opened.empty()) { 372 | printf("Error in astar::astar_plan: Path not found.\n"); 373 | return -1; 374 | } 375 | std::vector::iterator lowest_rank = get_lowest_rank(); 376 | 377 | // remove the node with the lowest rank 378 | astarnode_t cur = *lowest_rank; 379 | opened.erase(lowest_rank); 380 | 381 | // if it matches the goal, we are done! 382 | if ( (cur.ws.values & goal.care) == (goal.values & goal.care) ) 383 | { 384 | reconstruct_plan(ap, &cur, plan, worldstates); 385 | return cur.f; 386 | } 387 | // add it to closed 388 | closed.push_back(cur); 389 | 390 | // iterate over neighbours 391 | std::vector actionnames; 392 | std::vector actioncosts; 393 | std::vector to; 394 | const int numtransitions = goap_get_possible_state_transitions(ap, cur.ws, &to, &actionnames, &actioncosts); 395 | 396 | for (int i=0; i* to, std::vector* actionnames, std::vector* actioncosts) 441 | { 442 | for (std::vector::iterator it = ap->actions.begin(); it != ap->actions.end(); it++) { 443 | const worldstate_t* pre = &(it->pre); 444 | 445 | const bool met = (pre->values & pre->care) == (fr.values & pre->care); 446 | 447 | if (met) { 448 | actionnames->push_back(it->name); 449 | actioncosts->push_back(it->get_cost(ap, &fr)); 450 | worldstate_t to_ws; 451 | do_action(ap, it-ap->actions.begin(), fr, &to_ws); 452 | to->push_back(to_ws); 453 | } 454 | } 455 | 456 | return actionnames->size(); 457 | } 458 | 459 | bool GOAP::is_action_possible(actionplanner_t* ap, worldstate_t* ws, std::string action_name, bool* result) 460 | { 461 | int index; 462 | if (!find_action_index(ap, action_name, &index)) { 463 | printf("Error in GOAP::is_action_possible: Action %s not found in action planner.\n", action_name.c_str()); 464 | return false; 465 | } 466 | 467 | worldstate_t* pre = &ap->actions.at(index).pre; 468 | *result = (pre->values & pre->care) == (ws->values & pre->care); 469 | return true; 470 | } 471 | 472 | bool GOAP::do_action(actionplanner_t* ap, int actionnr, worldstate_t fr, worldstate_t* to) 473 | { 474 | worldstate_t* pst = &ap->actions[actionnr].pst; 475 | boost::dynamic_bitset<> notcare = pst->care; 476 | notcare.flip(); 477 | 478 | to->values = (fr.values & notcare) | (pst->values & pst->care); 479 | to->care = fr.care | pst->care; 480 | return true; 481 | } 482 | 483 | } // namespace goap -------------------------------------------------------------------------------- /include/task_planning/action_planner.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : action_planner.h 3 | // Project : task_planning 4 | // Created : 5/25/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | /* 32 | * Provides goal oriented action planning interface. 33 | */ 34 | 35 | #ifndef GOAP_ACTIONPLANNER_H 36 | #define GOAP_ACTIONPLANNER_H 37 | 38 | #include "worldstate.h" 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #include 56 | 57 | namespace goap { 58 | 59 | class ActionPlanner; 60 | 61 | typedef std::vector action_plan_t; 62 | typedef int (*cost_function)(const ActionPlanner* ap, const Worldstate* ws); 63 | 64 | // Action planner that keeps track of world state propositions and its action repertoire. 65 | class ActionPlanner 66 | { 67 | friend class TaskManager; 68 | 69 | public: 70 | 71 | /** 72 | * @brief Produce an action plan 73 | * @param start - The starting worldstate 74 | * @param goal - The goal worldstate 75 | * @param plan - Outputs the created action plan 76 | * @param worldstates - Outputs the worldstates that the plan passes through 77 | * @return The plan cost, or -1 if no plan was found. 78 | 79 | from: http://theory.stanford.edu/~amitp/GameProgramming/ImplementationNotes.html 80 | OPEN = priority queue containing START 81 | CLOSED = empty set 82 | while lowest rank in OPEN is not the GOAL: 83 | current = remove lowest rank item from OPEN 84 | add current to CLOSED 85 | for neighbors of current: 86 | cost = g(current) + movementcost(current, neighbor) 87 | if neighbor in OPEN and cost less than g(neighbor): 88 | remove neighbor from OPEN, because new path is better 89 | if neighbor in CLOSED and cost less than g(neighbor): ** 90 | remove neighbor from CLOSED 91 | if neighbor not in OPEN and neighbor not in CLOSED: 92 | set g(neighbor) to cost 93 | add neighbor to OPEN 94 | set priority queue rank to g(neighbor) + h(neighbor) 95 | set neighbor's parent to current 96 | */ 97 | int createPlan 98 | ( 99 | const Worldstate& start, 100 | const Worldstate& goal, 101 | action_plan_t* plan, 102 | std::vector* worldstates 103 | ) const; 104 | 105 | /** 106 | * @brief Clear contents of this planner. 107 | */ 108 | void clear(); 109 | 110 | /** 111 | * @brief Add an action to be planner using ADL syntax. 112 | * @param definition - The base action definition 113 | * @param preconditions - Vector of preconditions 114 | * @param effects - Vector of effects 115 | * @return - True on success. 116 | */ 117 | bool addAction(std::string definition, std::vector preconditions, std::vector effects); 118 | 119 | /** 120 | * @brief Add a set of entities to be planner using ADL syntax. This version takes a vector of names to assign to the members. Member names must be unique. 121 | * @param name - The name of the set 122 | * @param members - Vector of member names 123 | * @return - True on success. 124 | */ 125 | bool addEntitySet(std::string name, std::vector members); 126 | 127 | /** 128 | * @brief Add a set of entities to be planner using ADL syntax. This version takes the number of members to create and assigns [set name]_i to the members. 129 | * @param name - The name of the set 130 | * @param num - The number of members to create 131 | * @return - True on success. 132 | */ 133 | bool addEntitySet(std::string name, int num); 134 | 135 | /** 136 | * @brief Build action space from provided ADL actions and entities. 137 | * @return - True on success. 138 | */ 139 | bool buildActionSpace(); 140 | 141 | /** 142 | * @brief Set a constant cost for named action. 143 | * @param actionname - The name of the action to be modified 144 | * @param cost - The new constant cost 145 | * @return True on success, false if the action is not in the planner. 146 | */ 147 | bool setActionCost(std::string actionname, int cost); 148 | 149 | /** 150 | * @brief Set a cost function for named action. 151 | * @param actionname - The name of the action to be modified 152 | * @param func - Pointer to cost function. 153 | * @return True on success, false if the action is not in the planner. 154 | */ 155 | bool setActionCost(std::string actionname, cost_function func); 156 | 157 | /** 158 | * @brief Set a proposition of worldstate to specified value. 159 | * @param ws - World state to be modified 160 | * @param name - The name of the proposition to be modified. This can use literal or generic params. 161 | * @param value - The new value of propositionname 162 | * @return - True on success, failure if the proposition is not in the planner. 163 | */ 164 | bool setProposition(Worldstate* ws, std::string name, bool value) const; 165 | 166 | /** 167 | * @brief Get the value of a proposition in a specified worldstate. 168 | * @param ws - Worldstate containing the value 169 | * @param name - The name of the proposition to be checked 170 | * @param result - Outputs the value of the proposition 171 | * @return - Teu on success. Failure if the action doesn't exist 172 | */ 173 | bool getPropositionValue(const Worldstate* ws, std::string name, bool* result) const; 174 | 175 | /** 176 | * @brief Determine if the preconditions of an action are met by a given worldstate 177 | * @param ws - The worldstate to be tested 178 | * @param action_name - The action name 179 | * @param result - Outputs whether the action is possible. 180 | * @return - True on success. False if the action doesn't exist. 181 | */ 182 | bool isActionPossible(const Worldstate* ws, std::string action_name, bool* result) const; 183 | 184 | /** 185 | * @brief Performs an action on a worldstate, producing the resultant worldstate 186 | * @param actionnname - The name of the action 187 | * @param fr - The starting worldstate 188 | * @param to - Outputs the resultant worldspace 189 | * return - True on success, false on failure 190 | */ 191 | bool doAction(std::string actionname, const Worldstate* fr, Worldstate* to) const; 192 | 193 | /** 194 | * @brief Get string which describes the action planner by listing all actions with preconditions and effects. 195 | * @param output - Description string 196 | */ 197 | std::string description() const; 198 | 199 | /** 200 | * @brief Describe the worldstate by listing propositions that matter, in lowercase for false-valued, and uppercase for true-valued propositions. 201 | * @param ws - The worldstate to be described 202 | * @return - String containing the output 203 | */ 204 | std::string worldstateDescription(const Worldstate* ws) const; 205 | 206 | /** 207 | * @brief Update the size of a worldstate to match the number of propositions in this planner. 208 | * @param ws The worldstate to modify. 209 | */ 210 | void updateWorldstateSize(Worldstate* ws) const; 211 | 212 | private: 213 | 214 | typedef std::string param_label, param_type; 215 | typedef std::vector string_vec; 216 | 217 | // Used internally by the createPlan function. Represents a node in the graph search. 218 | struct astarnode_t 219 | { 220 | Worldstate ws; // The state of the world at this node. 221 | int g; // The cost so far. 222 | int f; // g+heuristic. 223 | std::string actionname; // How did we get to this node? 224 | Worldstate* parentws; // Where did we come from? 225 | }; 226 | 227 | class NodeMap 228 | { 229 | public: 230 | 231 | struct sequenced{}; 232 | struct hash_key {}; 233 | 234 | typedef boost::multi_index::tag sequenced_tag; 235 | typedef boost::multi_index::tag hash_tag; 236 | 237 | typedef boost::multi_index_container< 238 | astarnode_t*, 239 | boost::multi_index::indexed_by< 240 | boost::multi_index::hashed_unique< 241 | hash_tag, 242 | boost::multi_index::member, 243 | Worldstate::hash 244 | >, 245 | boost::multi_index::sequenced 246 | > 247 | > node_map_t; 248 | 249 | typedef node_map_t::index::type hash_index; 250 | typedef node_map_t::index::type sequence_index; 251 | typedef node_map_t::index_iterator::type hash_iter; 252 | typedef node_map_t::index_iterator::type sequence_iter; 253 | 254 | node_map_t& operator() () { return map_; } 255 | 256 | hash_index& getHashed() { return map_.get(); }; 257 | 258 | sequence_index& getSequence() { return map_.get(); }; 259 | 260 | ~NodeMap() 261 | { 262 | for (sequence_iter it = getSequence().begin(); it != getSequence().end(); ++it) 263 | delete *it; 264 | } 265 | 266 | private: 267 | node_map_t map_; 268 | }; 269 | 270 | struct EntitySet 271 | { 272 | param_type name; 273 | 274 | typedef std::vector member_list; 275 | member_list members; 276 | 277 | bool operator==(const param_type &rhs) const { return name == rhs; } 278 | }; 279 | 280 | class Proposition 281 | { 282 | public: 283 | /* 284 | * A "variable" parameter references a parameter in the base action. 285 | * This causes the proposition to receive the value of the action parameter. 286 | * For example: RobotAt(a) sets RobotAt true for the value of a. 287 | * ~RobotAt(a) sets RobotAt false for the value of a. 288 | * 289 | * A "literal" parameter references a specific entity. This makes the proposition the same for all versions of the action. 290 | * A litetal parameter has a '$' in front of the parameter label. 291 | * For example: RobotAt($location_1) sets RobotAt true for location_1. 292 | * RobotAt($location_1) sets RobotAt false for location_1. 293 | * 294 | * A "negated" parameter references all entities in a set other than the specified one. You must also provide the set name. 295 | * A negated parameter has a '~' in front of the parameter label. 296 | * For example: RobotAt(~a:location) sets RobotAt true for all values other than a in set location. 297 | * ~RobotAt(~a:location) sets RobotAt false for all values other than a in set location. 298 | * 299 | * You can declare a parameter that is both negated and literal. 300 | * For example: RobotAt(~$location_1:location) sets RobotAt true for all values other than location_1 in set location. 301 | * ~RobotAt(~$location_1:location) sets RobotAt false for all values other than location_1 in set location. 302 | * 303 | * An "ALL" parameter references all entities of a set. 304 | * For example: RobotAt(ALL:location) sets RobotAt true for all locations. 305 | * ~RobotAt(ALL:location) sets RobotAt false for all locations. 306 | * 307 | * You cannot negate an all parameter. 308 | */ 309 | typedef struct { 310 | std::string raw_text; // The raw text provided by client 311 | param_label label; // Holds name of parsed parameter label 312 | param_type entity_set_name; // Name of set referenced by parameter 313 | const EntitySet::member_list* set_members; // Points to entities referenced by parameter 314 | 315 | bool negated; // Is this a "not" param? 316 | bool all; // Is this an "all" param? 317 | bool literal; // Is the text a literal? 318 | 319 | bool operator==(const std::string &rhs) const { return label == rhs; } 320 | } parameter; 321 | std::vector params; 322 | 323 | /** 324 | * @breif Build this proposition by parsing the user text 325 | * @param definition The raw text from user 326 | * @return True on successful parse 327 | */ 328 | bool parse(std::string definition); 329 | 330 | /** 331 | * @breif Build the proposition name using the parameter values 332 | */ 333 | void buildName(); 334 | 335 | /** 336 | * @breif Helper function for parse(). Parses a parameter. 337 | * @param param The raw text to parse 338 | * @param parameter Output reference to the created parameter object 339 | * @return True on successful parse. 340 | */ 341 | bool parseParam(std::string param, parameter* parameter); 342 | 343 | /** 344 | * @breif Search function for the parameter list 345 | * @param label The derived_value to search for. 346 | * @return Pointer to parameter, or NULL if not found. 347 | */ 348 | parameter* getParameter(std::string value); 349 | 350 | std::string name, root; 351 | 352 | 353 | bool operator==(const Proposition &rhs) const { return name == rhs.name; } 354 | 355 | bool operator==(const std::string &rhs) const { return name == rhs; } 356 | }; 357 | 358 | class Action 359 | { 360 | public: 361 | Action(); 362 | Action(int); 363 | 364 | // action params are modeled like this 365 | typedef struct { 366 | param_label label; 367 | param_type type; 368 | const std::vector* set_members; 369 | 370 | bool operator==(const param_label &rhs) const { return label == rhs; } 371 | } parameter; 372 | 373 | /** 374 | * @breif Build this action by parsing the user text 375 | * @param definition The raw text from user 376 | * @return True on successful parse 377 | */ 378 | bool parse(std::string definition, string_vec preconditions, string_vec effects); 379 | 380 | /** 381 | * @breif Helper for parse. Parses a proposition and adds to precondition list 382 | * @param definition The raw text from user 383 | * @return True on successful parse 384 | */ 385 | bool buildPrecondition(std::string definition); 386 | 387 | /** 388 | * @breif Helper for parse. Parses a proposition and adds to effect list 389 | * @param definition The raw text from user 390 | * @return True on successful parse 391 | */ 392 | bool buildEffect(std::string definition); 393 | 394 | /** 395 | * @brief Set the cost to a constant value. 396 | * @param val - The new constant value 397 | */ 398 | void set_cost(int val); 399 | 400 | /** 401 | * @brief Associate a cost function with the action. This allows the cost to depend on the worldstate. 402 | * @param func - Function pointer referencing the cost function 403 | */ 404 | void set_cost(cost_function func); 405 | 406 | /** 407 | * @brief Get the cost of the action. 408 | * @param ap - The action planner. Not needed if we are not using a cost function for thsi action. 409 | * @param ws - The worldstate. Not needed if we are not using a cost function for thsi action. 410 | * @return The action cost 411 | */ 412 | int get_cost(const ActionPlanner* ap = NULL, const Worldstate* ws = NULL) const; 413 | 414 | /** 415 | * @breif Updates the name member using current parameter values 416 | */ 417 | void buildName(); 418 | 419 | /** 420 | * @breif Search function for the parameter list 421 | * @param label The label to search for. 422 | * @return Pointer to parameter, or NULL if not found. 423 | */ 424 | parameter* getParameter(param_label label); 425 | 426 | std::string name; // full name 427 | std::string root; // name without the parameter list 428 | 429 | typedef std::vector param_vec; 430 | param_vec params; // holds the params 431 | 432 | typedef std::pair condition_t; 433 | std::vector preconditions, effects; 434 | 435 | // Use this criterion for sorting Actions alphabetically in a vector 436 | bool operator<(const Action &rhs) const { return name < rhs.name; } 437 | 438 | bool operator==(const Action &rhs) const { return name == rhs.name; } 439 | 440 | bool operator==(const std::string &rhs) const { return name == rhs; } 441 | 442 | // These store the compiled preconditions and effects. The planner algorithm uses these. 443 | Worldstate pre, pst; 444 | 445 | private: 446 | int cost; 447 | int (*cost_func)(const ActionPlanner*, const Worldstate*); 448 | }; 449 | 450 | typedef std::list action_vector; 451 | 452 | /** 453 | * @brief Add a new action to the action planner. 454 | * @param actionname - The desired action name 455 | * @return - Reference to the action 456 | */ 457 | Action* addAction(std::string actionname); 458 | 459 | /** 460 | * @brief Add a precondition for an action. If this proposition is not already present in the planner, it will be added. 461 | * @param actionname - The action to be modified. 462 | * @param name - The proposition to be modified. 463 | * @param value - The new value of the precondition. 464 | * @return - True on success 465 | */ 466 | bool setPrecondition(std::string actionname, std::string name, bool value); 467 | 468 | /** 469 | * @brief Add an effect for an action. If this proposition is not already present in the planner, it will be added. 470 | * @param actionname - The action to be modified. 471 | * @param name - The proposition to be modified. 472 | * @param value - The new value of precondition. 473 | * @return - True on success 474 | */ 475 | bool setEffect(std::string actionname, std::string name, bool value); 476 | 477 | /** 478 | * @brief Add a new proposition to the action planner. 479 | * @param propositionname - The desired proposition name 480 | * @param index - Index of the proposition in proposition_names_ 481 | * @return - True if the success, false if proposition with that name already existed. 482 | */ 483 | bool addProposition(std::string propositionname); 484 | 485 | /** 486 | * @brief Attempt to match an ADL proposition to existing atoms. 487 | * @param propositionname - The desired proposition name 488 | * @param index - Index of the proposition in proposition_names_ 489 | * @return - True if the success, false if proposition with that name already existed. 490 | */ 491 | bool setPropositionADL(Worldstate* ws, std::string name, bool value) const; 492 | 493 | /** 494 | * @brief Get the lowest rank node in the opened nodes. 495 | * @param nodes The node map to search 496 | * @return Iterator to the lowest ranked node 497 | */ 498 | NodeMap::sequence_iter getLowestRank(NodeMap* nodes) const; 499 | 500 | /** 501 | * @brief Internal function to reconstruct the plan by tracing from last node to initial node. 502 | * @param goalnode - The goal 503 | * @param plan - Vector of actions to be taken 504 | * @param worldstates - The worldstates we will pass through 505 | */ 506 | void reconstructPlan(astarnode_t* goalnode, NodeMap* nodes, action_plan_t* plan, std::vector* worldstates) const; 507 | 508 | /** 509 | * @brief Given the specified 'from' state, list all possible 'to' states along with the action required, and the action cost. For internal use. 510 | * @param fr - The 'from' state 511 | * @param to - the 'to' state 512 | * @return The number of possible transitions 513 | */ 514 | int getPossibleStateTransitions(const Worldstate* fr, std::vector* to, std::vector* actionnames, std::vector* actioncosts) const; 515 | 516 | /** 517 | * @brief Perform action specified by number on a worldstate 518 | * @param actionnr - The index of the action to perform 519 | * @param fr - The worldstate to be acted on 520 | * @param to - The new worldstate 521 | * @return - True on success. False on failure. 522 | */ 523 | bool doActionInternal(const Action* action, const Worldstate* fr, Worldstate* to) const; 524 | 525 | /** 526 | * @brief Return the index of the indicated proposition. 527 | * @param name - The desired proposition name 528 | * @param index - Outputs the index of the proposition in proposition_names_ 529 | * @return - True if the proposition was found, else false 530 | */ 531 | bool findPropositionIndex(std::string name, int* index = NULL) const; 532 | 533 | /** 534 | * @brief Return a pointer to the indicated action. 535 | * @param actionname - The desired action name 536 | * @return - Reference to the action 537 | */ 538 | Action* findAction(std::string actionname); 539 | 540 | /** 541 | * @brief Return a pointer to the indicated action. Const-overloaded version. 542 | * @param actionname - The desired action name 543 | * @return - Reference to the action 544 | */ 545 | const Action* findAction(std::string actionname) const; 546 | 547 | /** 548 | * @brief Return a pointer to the indicated action. 549 | * @param actionname - The desired action name 550 | * @return - Reference to the action 551 | */ 552 | Action* findUnbuiltAction(std::string actionname); 553 | 554 | /** 555 | * @brief Return a pointer to the indicated action. Const-overloaded version. 556 | * @param actionname - The desired action name 557 | * @return - Reference to the action 558 | */ 559 | const Action* findUnbuiltAction(std::string actionname) const; 560 | 561 | /** 562 | * @brief Return a pointer to the indicated EntitySet. 563 | * @param actionname - The desired action name 564 | * @return - Reference to the action 565 | */ 566 | EntitySet* findEntitySet(std::string name); 567 | 568 | /** 569 | * @brief Return a pointer to the indicated EntitySet. Const-overloaded version. 570 | * @param actionname - The desired action name 571 | * @return - Reference to the action 572 | */ 573 | const EntitySet* findEntitySet(std::string name) const; 574 | 575 | /** 576 | * @brief This is our heuristic: estimate for remaining distance is the number of mismatched propositions that matter. 577 | * @param fr - The starting worldspace 578 | * @param to - The end worldspace 579 | * @return The distance between the two worldspaces 580 | */ 581 | int calcH(const Worldstate* fr, const Worldstate* to) const; 582 | 583 | /** 584 | * @breif Build permutations of an action. 585 | * @param action - The action to be permuted 586 | * @return The permutations 587 | */ 588 | bool permuteAction(const Action &action, action_vector* result) const; 589 | 590 | /** 591 | * @breif Helper function for permuteAction to insert permuted parameters into an action. 592 | * @param action - The action to be modified 593 | * @param original - The param name to be replaced 594 | * @param replacement - The param name to be inserted 595 | * @return true on success 596 | */ 597 | bool substituteParamLabel(Action &action, param_label original, param_label replacement) const; 598 | 599 | /** 600 | * @brief Helper function for permuteAction to verify the mutual exclusion rule for action parameters. 601 | * @param labels - The parameter labels 602 | * @param v - The parameter values 603 | * @return True if the mutual exclusion rule is met 604 | */ 605 | template 606 | bool elementUniqueness(const string_vec& labels, const std::vector& v) const; 607 | 608 | /** 609 | * @breif Links the action definitions to the entity sets. 610 | * @return true on success 611 | */ 612 | bool linkEntities(Action& action) const; 613 | 614 | /** 615 | * @breif Expands parameters set ALL. 616 | * @return true on success 617 | */ 618 | bool expandAllParams(Action &action) const; 619 | 620 | /** 621 | * @breif Expands parameters set ALL. 622 | * @return The Propositions produced by the expansion 623 | */ 624 | std::vector expandAllCondition(Action::condition_t condition) const; 625 | 626 | /** 627 | * @breif Expands negated parameters. 628 | * @return true on success 629 | */ 630 | bool expandNegatedParams(Action &action) const; 631 | 632 | /** 633 | * @breif Expands negated parameters. 634 | * @return The Propositions produced by the expansion 635 | */ 636 | std::vector expandNegatedCondition(Action::condition_t condition) const; 637 | 638 | action_vector actions_; 639 | action_vector unbuilt_actions_; 640 | 641 | typedef std::forward_list entity_list; 642 | entity_list entities_; 643 | 644 | typedef boost::bimaps::bimap prop_index_map_t; 645 | typedef prop_index_map_t::value_type prop_index_t; 646 | prop_index_map_t proposition_index_; 647 | }; 648 | 649 | } // namespace goap 650 | 651 | #endif -------------------------------------------------------------------------------- /src/action_planner.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Title : action_planner.cpp 3 | // Project : task_planning 4 | // Created : 5/25/2016 5 | // Author : Blake Anderson 6 | // Platforms : Ubuntu 64-bit 7 | // Copyright : Copyright© The University of Texas at Austin, 2014-2017. All rights reserved. 8 | // 9 | // All files within this directory are subject to the following, unless an alternative 10 | // license is explicitly included within the text of each file. 11 | // 12 | // This software and documentation constitute an unpublished work 13 | // and contain valuable trade secrets and proprietary information 14 | // belonging to the University. None of the foregoing material may be 15 | // copied or duplicated or disclosed without the express, written 16 | // permission of the University. THE UNIVERSITY EXPRESSLY DISCLAIMS ANY 17 | // AND ALL WARRANTIES CONCERNING THIS SOFTWARE AND DOCUMENTATION, 18 | // INCLUDING ANY WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 19 | // PARTICULAR PURPOSE, AND WARRANTIES OF PERFORMANCE, AND ANY WARRANTY 20 | // THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE. 21 | // NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF 22 | // THE SOFTWARE OR DOCUMENTATION. Under no circumstances shall the 23 | // University be liable for incidental, special, indirect, direct or 24 | // consequential damages or loss of profits, interruption of business, 25 | // or related expenses which may arise from use of software or documentation, 26 | // including but not limited to those resulting from defects in software 27 | // and/or documentation, or loss or inaccuracy of data of any kind. 28 | // 29 | /////////////////////////////////////////////////////////////////////////////// 30 | 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | namespace goap { 44 | 45 | /** 46 | * @breif Produces permutation and then increments the counter. Run this in a do...while loop to run the counter forward. 47 | * @param v Vector of vector references containing the elements to be permuted. 48 | * @param it Counter vector. It is incremented by one. 49 | * @return True if we have not reached the end of the permutations. 50 | */ 51 | template 52 | bool next_permutation(const std::vector*>& v, std::vector& it) 53 | { 54 | // Increment the counter 55 | for (std::size_t i = 0, size = it.size(); i != size; ++i) { 56 | const std::size_t index = size - 1 - i; 57 | ++it[index]; 58 | if (it[index] == v[index]->size()) { 59 | it[index] = 0; 60 | } else { 61 | return true; 62 | } 63 | } 64 | return false; 65 | } 66 | 67 | 68 | 69 | /** 70 | * @breif Determine if all elements in a container are unique 71 | * @param first Front of range 72 | * @param last End of range 73 | * @return True if all elements in the range are unique 74 | */ 75 | template 76 | bool check_unique(FwdIterator first, FwdIterator last) 77 | { 78 | FwdIterator result = first; 79 | std::unordered_set seen; 80 | 81 | for (; first != last; ++first) { 82 | if (!seen.insert(*first).second) { 83 | return false; 84 | } 85 | } 86 | return true; 87 | } 88 | 89 | 90 | 91 | 92 | int ActionPlanner::createPlan 93 | ( 94 | const Worldstate& start, 95 | const Worldstate& goal, 96 | action_plan_t* plan, 97 | std::vector* worldstates 98 | ) const 99 | { 100 | NodeMap opened; // The set of nodes we should consider. 101 | NodeMap closed; // The set of nodes we already visited. 102 | 103 | // create start node 104 | astarnode_t* n0 = new astarnode_t; 105 | n0->ws = start; 106 | n0->parentws = new Worldstate; 107 | *n0->parentws = start; 108 | n0->g = 0; 109 | n0->f = n0->g + calcH(&start, &goal); 110 | n0->actionname = "start"; 111 | opened().insert(n0); 112 | 113 | while (!opened().empty()) { 114 | 115 | const NodeMap::sequence_iter lowest_rank = getLowestRank(&opened); 116 | 117 | // remove the node with the lowest rank 118 | astarnode_t* cur; 119 | cur = *lowest_rank; 120 | 121 | opened.getSequence().erase(lowest_rank); 122 | 123 | // if it matches the goal, we are done! 124 | if ( (cur->ws.values & goal.care) == (goal.values & goal.care) ) { 125 | reconstructPlan(cur, &closed, plan, worldstates); 126 | return cur->f; 127 | } 128 | // add it to closed 129 | closed().insert(cur); 130 | 131 | // iterate over neighbours 132 | std::vector actionnames; 133 | std::vector actioncosts; 134 | std::vector to; 135 | 136 | const int numtransitions = getPossibleStateTransitions(&cur->ws, &to, &actionnames, &actioncosts); 137 | 138 | for (int i=0; ig + actioncosts[i]; 140 | 141 | // if neighbor in CLOSED and cost less than g(neighbor): 142 | NodeMap::hash_iter closed_it = closed.getHashed().find(to[i]); 143 | if (closed_it != closed().end()) { 144 | if (cost < (**closed_it).g) { 145 | // remove neighbor from CLOSED 146 | if (!closed().empty()) { 147 | (**closed_it).g = cost; 148 | (**closed_it).f = cost + calcH(&to[i], &goal); 149 | (**closed_it).parentws = &cur->ws; 150 | opened().insert(*closed_it); 151 | closed().erase(closed_it); 152 | } 153 | } 154 | else continue; 155 | } 156 | else { 157 | // if neighbor in OPEN and cost less than g(neighbor): 158 | NodeMap::hash_iter opened_it = opened.getHashed().find(to[i]); 159 | if (opened_it != opened().end()) { 160 | if (cost < (**opened_it).g) { 161 | // remove neighbor from OPEN, because new path is better 162 | if (!opened().empty()) { 163 | (**opened_it).g = cost; 164 | (**opened_it).f = cost + calcH(&to[i], &goal); 165 | (**opened_it).parentws = &cur->ws; 166 | } 167 | } 168 | else continue; 169 | } 170 | else { 171 | // if neighbor not in OPEN and neighbor not in CLOSED: 172 | astarnode_t* new_node = new astarnode_t; 173 | new_node->ws = to[i]; 174 | new_node->g = cost; 175 | new_node->f = cost + calcH(&to[i], &goal); 176 | new_node->actionname = *(actionnames[i]); 177 | new_node->parentws = &cur->ws; 178 | opened().insert(new_node); 179 | } 180 | } 181 | 182 | // if neighbor not in OPEN and neighbor not in CLOSED: 183 | // astarnode_t new_node; 184 | // new_node.ws = to[i]; 185 | // new_node.g = cost; 186 | // new_node.f = cost + calcH(&to[i], &goal); 187 | // new_node.actionname = *(actionnames[i]); 188 | // new_node.parentws = cur.ws; 189 | // opened.insert(new_node); 190 | } 191 | } // end while 192 | 193 | printf("Error in ActionPlanner::createPlan - Path not found.\n"); 194 | return -1; 195 | } 196 | 197 | 198 | 199 | bool ActionPlanner::buildActionSpace() 200 | { 201 | // Clear built action list 202 | actions_.clear(); 203 | 204 | // For each Action 205 | BOOST_FOREACH (Action& action, unbuilt_actions_) { 206 | 207 | if (!linkEntities(action)) { 208 | return false; 209 | } 210 | 211 | // handle the "all" propositions 212 | if (!expandAllParams(action)) { 213 | return false; 214 | } 215 | 216 | // Build permutations of Action 217 | action_vector permuted_actions; 218 | if (!permuteAction(action, &permuted_actions)) { 219 | printf("ActionPlanner::buildActionSpace - Permutation failed for action %s.", action.name.c_str()); 220 | return false; 221 | } 222 | 223 | // handle the negated propositions 224 | BOOST_FOREACH (Action& action, permuted_actions) { 225 | if (!expandNegatedParams(action)) { 226 | return false; 227 | } 228 | } 229 | 230 | // Add permuted Actions to list (addAction guarantees that actions will be unique) 231 | actions_.splice(actions_.end(), permuted_actions); 232 | } 233 | 234 | // Set pre and pst of built actions 235 | BOOST_FOREACH (Action& action, actions_) { 236 | 237 | // Set pre 238 | BOOST_FOREACH (Action::condition_t precondition, action.preconditions) { 239 | 240 | if (!setPrecondition(action.name, precondition.first.name, precondition.second)) { 241 | printf("ActionPlanner::buildActionSpace - Setting pre failed for action %s.", action.name.c_str()); 242 | return false; 243 | } 244 | } 245 | 246 | // Set effects 247 | BOOST_FOREACH (Action::condition_t effect, action.effects) { 248 | if (!setEffect(action.name, effect.first.name, effect.second)) { 249 | printf("ActionPlanner::buildActionSpace - Setting pst failed for action %s.", action.name.c_str()); 250 | return false; 251 | } 252 | } 253 | } 254 | 255 | return true; 256 | } 257 | 258 | 259 | 260 | void ActionPlanner::clear() 261 | { 262 | proposition_index_.clear(); 263 | entities_.clear(); 264 | actions_.clear(); 265 | unbuilt_actions_.clear(); 266 | } 267 | 268 | 269 | 270 | bool ActionPlanner::addAction(std::string definition, std::vector preconditions, std::vector effects) 271 | { 272 | Action new_action; 273 | if (!new_action.parse(definition, preconditions, effects)) { 274 | printf("ActionPlanner::addAction - Failed to add action %s.", definition.c_str()); 275 | return false; 276 | } 277 | 278 | unbuilt_actions_.push_back(new_action); 279 | 280 | return true; 281 | } 282 | 283 | 284 | 285 | bool ActionPlanner::addEntitySet(std::string name, std::vector members) 286 | { 287 | // Validate 288 | if (members.empty()) { 289 | printf("Error in ActionPlanner::addEntitySet: No members received."); 290 | return false; 291 | } 292 | 293 | if (name.empty()) { 294 | printf("Error in ActionPlanner::addEntitySet: Empty name received."); 295 | return false; 296 | } 297 | 298 | // Verify that all member names are unique 299 | if (!check_unique(members.begin(), members.end())) { 300 | printf("Error in ActionPlanner::addEntitySet: Member list contains duplicate elements."); 301 | return false; 302 | } 303 | 304 | // Check if a set with this name exists 305 | EntitySet* new_set = findEntitySet(name); 306 | if (new_set == NULL) { 307 | // Set does not already exist. Create new one 308 | new_set = new EntitySet; 309 | new_set->name = name; 310 | new_set->members = members; 311 | entities_.push_front(*new_set); 312 | delete new_set; 313 | } 314 | else { 315 | // Set already exists. Update members. 316 | new_set->members = members; 317 | } 318 | 319 | return true; 320 | } 321 | 322 | 323 | 324 | bool ActionPlanner::addEntitySet(std::string name, int num) 325 | { 326 | // Validate 327 | if (num < 1) { 328 | printf("Error in ActionPlanner::addEntitySet: num must be positive. Received %d.\n", num); 329 | return false; 330 | } 331 | 332 | // Build member list with generic names 333 | string_vec members; 334 | members.reserve(num); 335 | for (int i = 1; i <= num; ++i) { 336 | members.push_back(name + "_" + std::to_string(i)); 337 | } 338 | 339 | return addEntitySet(name, members); 340 | } 341 | 342 | 343 | 344 | bool ActionPlanner::setPrecondition(std::string actionname, std::string name, bool value) 345 | { 346 | Action* action = findAction(actionname); 347 | if (!action) { 348 | return false; 349 | } 350 | 351 | int proposition_index; 352 | addProposition(name); 353 | 354 | return setProposition(&action->pre, name, value); 355 | } 356 | 357 | 358 | 359 | bool ActionPlanner::setEffect(std::string actionname, std::string name, bool value) 360 | { 361 | Action* action = findAction(actionname); 362 | if (!action) { 363 | return false; 364 | } 365 | 366 | int proposition_index; 367 | addProposition(name); 368 | 369 | return setProposition(&action->pst, name, value); 370 | } 371 | 372 | 373 | 374 | bool ActionPlanner::setActionCost(std::string actionname, int cost) 375 | { 376 | Action* action = findAction(actionname); 377 | if (!action) { 378 | printf("Error in ActionPlanner::setActionCost: Action %s not found in action planner.\n", actionname.c_str()); 379 | return false; 380 | } 381 | 382 | action->set_cost(cost); 383 | return true; 384 | } 385 | 386 | 387 | 388 | bool ActionPlanner::setActionCost(std::string actionname, int (*func)(const ActionPlanner* ap, const Worldstate* ws)) 389 | { 390 | Action* action = findAction(actionname); 391 | if (!action) { 392 | printf("Error in ActionPlanner::setActionCost: Action %s not found in action planner.\n", actionname.c_str()); 393 | return false; 394 | } 395 | 396 | action->set_cost(func); 397 | return true; 398 | } 399 | 400 | 401 | 402 | bool ActionPlanner::setProposition(Worldstate* ws, std::string name, bool value) const 403 | { 404 | // See if name matches any existing atoms 405 | int index; 406 | if(!findPropositionIndex(name, &index)) { 407 | 408 | // See if name can be interpreted via ADL 409 | if (setPropositionADL(ws, name, value)) { 410 | return true; 411 | } 412 | 413 | printf("Error in ActionPlanner::setProposition: Proposition %s not found in action planner.\n", name.c_str()); 414 | return false; 415 | } 416 | 417 | updateWorldstateSize(ws); 418 | 419 | ws->values[index] = value; 420 | ws->care[index] = true; 421 | 422 | return true; 423 | } 424 | 425 | 426 | 427 | bool ActionPlanner::setPropositionADL(Worldstate* ws, std::string name, bool value) const 428 | { 429 | Worldstate new_ws = *ws; 430 | 431 | Proposition prop; 432 | if (!prop.parse(name)) { 433 | return false; 434 | } 435 | 436 | Action temp_action; 437 | temp_action.preconditions.push_back(Action::condition_t(prop, value)); 438 | 439 | if (!linkEntities(temp_action)) { 440 | return false; 441 | } 442 | 443 | // handle the "all" propositions 444 | if (!expandAllParams(temp_action)) { 445 | return false; 446 | } 447 | 448 | // handle the negated propositions 449 | if (!expandNegatedParams(temp_action)) { 450 | return false; 451 | } 452 | 453 | std::vector created_conditions = temp_action.preconditions; 454 | 455 | BOOST_FOREACH (Action::condition_t& condition, created_conditions) { 456 | condition.first.buildName(); 457 | 458 | int index; 459 | if(!findPropositionIndex(condition.first.name, &index)) { 460 | return false; 461 | } 462 | 463 | updateWorldstateSize(&new_ws); 464 | 465 | new_ws.values[index] = condition.second; 466 | new_ws.care[index] = true; 467 | } 468 | 469 | *ws = new_ws; 470 | return true; 471 | } 472 | 473 | 474 | 475 | bool ActionPlanner::getPropositionValue(const Worldstate* ws, std::string name, bool* result) const 476 | { 477 | int index; 478 | if(!findPropositionIndex(name, &index)) { 479 | printf("Error in GOAP::getPropositionValue: Proposition %s not found in action planner.\n", name.c_str()); 480 | return false; 481 | } 482 | 483 | *result = ws->values[index]; 484 | return true; 485 | } 486 | 487 | 488 | 489 | bool ActionPlanner::isActionPossible(const Worldstate* ws, std::string action_name, bool* result) const 490 | { 491 | const Action* action = findAction(action_name); 492 | if (!action) { 493 | printf("ActionPlanner::isActionPossible: Action %s not found in action planner.\n", action_name.c_str()); 494 | return false; 495 | } 496 | 497 | const Worldstate* pre = &action->pre; 498 | *result = (pre->values & pre->care) == (ws->values & pre->care); 499 | return true; 500 | } 501 | 502 | 503 | 504 | bool ActionPlanner::doAction(std::string actionname, const Worldstate* fr, Worldstate* to) const 505 | { 506 | const Action* action = findAction(actionname); 507 | if (action) { 508 | doActionInternal(action, fr, to); 509 | return true; 510 | } 511 | 512 | return false; 513 | } 514 | 515 | 516 | 517 | std::string ActionPlanner::description() const 518 | { 519 | std::stringstream sstream; 520 | 521 | // Print the proposition list 522 | sstream << "Propositions:"; 523 | //for (prop_index_map_t::right_const_iterator it = proposition_index_.right.begin(); it != proposition_index_.right.end(); it++) { 524 | for (std::size_t i = 0, size = proposition_index_.size(); i < size; ++i) { 525 | sstream << " " << proposition_index_.left.at(i); 526 | } 527 | sstream << "\n\n"; 528 | 529 | // Print the actions 530 | BOOST_FOREACH (const Action& action, actions_) { 531 | 532 | const Worldstate* pre = &(action.pre); 533 | const Worldstate* pst = &(action.pst); 534 | 535 | // Print preconditions 536 | sstream << "Action: " << action.name << "\nPreconditions:\n"; 537 | BOOST_FOREACH (Action::condition_t precondition, action.preconditions) { 538 | sstream << "\t" << precondition.first.name << ": " << precondition.second << "\n"; 539 | } 540 | 541 | // Print effects 542 | sstream << "Effects:\n"; 543 | BOOST_FOREACH (Action::condition_t effect, action.effects) { 544 | sstream << "\t" << effect.first.name << ": " << effect.second << "\n"; 545 | } 546 | 547 | sstream << "\n"; 548 | } 549 | return sstream.str(); 550 | } 551 | 552 | 553 | 554 | std::string ActionPlanner::worldstateDescription(const Worldstate* ws) const 555 | { 556 | std::stringstream sstream; 557 | // Iterate through all propositions in planner 558 | for (uint i = 0; i < proposition_index_.size(); i++) { 559 | 560 | // Ignore if care == false 561 | if (ws->care[i]) { 562 | 563 | std::string proposition_name = proposition_index_.left.at(i); 564 | 565 | // Add ~ prefix for false 566 | if (!ws->values[i]) { 567 | proposition_name = "~" + proposition_name; 568 | } 569 | 570 | sstream << proposition_name << ", "; 571 | } 572 | } 573 | sstream << std::endl; 574 | 575 | return sstream.str(); 576 | } 577 | 578 | 579 | 580 | void ActionPlanner::updateWorldstateSize(Worldstate* ws) const 581 | { 582 | ws->initialize(proposition_index_.size()); 583 | } 584 | 585 | 586 | 587 | ActionPlanner::Action* ActionPlanner::addAction(std::string actionname) 588 | { 589 | Action* new_action = findAction(actionname); 590 | if (!new_action) { 591 | new_action = new Action(proposition_index_.size()); 592 | new_action->name = actionname; 593 | actions_.push_back(*new_action); 594 | 595 | delete new_action; 596 | new_action = &actions_.back(); 597 | } 598 | 599 | return new_action; 600 | } 601 | 602 | 603 | 604 | bool ActionPlanner::addProposition(std::string name) 605 | { 606 | // See if proposition already exists 607 | try { 608 | proposition_index_.right.at(name); 609 | } catch (std::out_of_range) { // Does not exist 610 | proposition_index_.insert( prop_index_t(proposition_index_.size(), name) ); 611 | 612 | // Update size of all action preconditions and postconditions 613 | for (action_vector::iterator it = actions_.begin(); it != actions_.end(); ++it) { 614 | updateWorldstateSize(&it->pre); 615 | updateWorldstateSize(&it->pst); 616 | } 617 | 618 | return true; 619 | } 620 | 621 | return false; 622 | } 623 | 624 | 625 | 626 | ActionPlanner::NodeMap::sequence_iter ActionPlanner::getLowestRank(NodeMap* nodes) const 627 | { 628 | NodeMap::sequence_iter lowest_rank = nodes->getSequence().begin(); 629 | for (NodeMap::sequence_iter it = nodes->getSequence().begin(), end = nodes->getSequence().end(); it != end; ++it) { 630 | if ((**lowest_rank).f > (**it).f) 631 | lowest_rank = it; 632 | } 633 | return lowest_rank; 634 | } 635 | 636 | 637 | 638 | void ActionPlanner::reconstructPlan(astarnode_t* goalnode, NodeMap* nodes, action_plan_t* plan, std::vector* worldstates) const 639 | { 640 | plan->clear(); 641 | worldstates->clear(); 642 | 643 | const astarnode_t* curnode = goalnode; 644 | while (curnode != NULL && curnode->actionname != "start") { 645 | plan->insert(plan->begin(), curnode->actionname); 646 | worldstates->insert(worldstates->begin(), curnode->ws); 647 | 648 | const NodeMap::hash_iter it = nodes->getHashed().find(*curnode->parentws); 649 | if (it != nodes->getHashed().end()) 650 | curnode = *it; 651 | else 652 | curnode = NULL; 653 | } 654 | } 655 | 656 | 657 | 658 | int ActionPlanner::getPossibleStateTransitions(const Worldstate* fr, std::vector* to, std::vector* actionnames, std::vector* actioncosts) const 659 | { 660 | Worldstate to_ws; 661 | updateWorldstateSize(&to_ws); 662 | 663 | for (action_vector::const_iterator it = actions_.begin(); it != actions_.end(); ++it) { 664 | const Worldstate* pre = &(it->pre); 665 | 666 | if ((pre->values & pre->care) == (fr->values & pre->care)) { 667 | actionnames->push_back(&(it->name)); 668 | actioncosts->push_back(it->get_cost(this, fr)); 669 | 670 | doActionInternal(&(*it), fr, &to_ws); 671 | to->push_back(to_ws); 672 | } 673 | } 674 | 675 | return actionnames->size(); 676 | } 677 | 678 | 679 | 680 | bool ActionPlanner::doActionInternal(const Action* action, const Worldstate* fr, Worldstate* to) const 681 | { 682 | const Worldstate* pst = &action->pst; 683 | boost::dynamic_bitset<> notcare = pst->care; 684 | notcare.flip(); 685 | 686 | to->values = (fr->values & notcare) | (pst->values & pst->care); 687 | to->care = fr->care | pst->care; 688 | return true; 689 | } 690 | 691 | 692 | 693 | bool ActionPlanner::findPropositionIndex(std::string name, int* index) const 694 | { 695 | try { 696 | *index = proposition_index_.right.at(name); 697 | return true; 698 | } catch (std::out_of_range) { // Does not exist 699 | return false; 700 | } 701 | } 702 | 703 | 704 | 705 | ActionPlanner::Action* ActionPlanner::findAction(std::string actionname) 706 | { 707 | Action* action = NULL; 708 | action_vector::iterator it = std::find(actions_.begin(), actions_.end(), actionname); 709 | 710 | if (it != actions_.end()) { 711 | action = &(*it); 712 | } 713 | 714 | return action; 715 | } 716 | 717 | 718 | 719 | const ActionPlanner::Action* ActionPlanner::findAction(std::string actionname) const 720 | { 721 | const Action* action = NULL; 722 | action_vector::const_iterator it = std::find(actions_.begin(), actions_.end(), actionname); 723 | 724 | if (it != actions_.end()) { 725 | action = &(*it); 726 | } 727 | 728 | return action; 729 | } 730 | 731 | 732 | ActionPlanner::Action* ActionPlanner::findUnbuiltAction(std::string actionname) 733 | { 734 | Action* action = NULL; 735 | 736 | action_vector::iterator it = std::find(unbuilt_actions_.begin(), unbuilt_actions_.end(), actionname); 737 | 738 | if (it != unbuilt_actions_.end()) { 739 | action = &(*it); 740 | } 741 | 742 | return action; 743 | } 744 | 745 | 746 | 747 | const ActionPlanner::Action* ActionPlanner::findUnbuiltAction(std::string actionname) const 748 | { 749 | const Action* action = NULL; 750 | 751 | action_vector::const_iterator it = std::find(unbuilt_actions_.begin(), unbuilt_actions_.end(), actionname); 752 | 753 | if (it != unbuilt_actions_.end()) { 754 | action = &(*it); 755 | } 756 | 757 | return action; 758 | } 759 | 760 | 761 | 762 | ActionPlanner::EntitySet* ActionPlanner::findEntitySet(std::string name) 763 | { 764 | EntitySet* entity_set = NULL; 765 | entity_list::iterator it = std::find(entities_.begin(), entities_.end(), name); 766 | 767 | if (it != entities_.end()) { 768 | entity_set = &(*it); 769 | } 770 | 771 | return entity_set; 772 | } 773 | 774 | 775 | 776 | const ActionPlanner::EntitySet* ActionPlanner::findEntitySet(std::string name) const 777 | { 778 | const EntitySet* entity_set = NULL; 779 | entity_list::const_iterator it = std::find(entities_.begin(), entities_.end(), name); 780 | 781 | if (it != entities_.end()) { 782 | entity_set = &(*it); 783 | } 784 | 785 | return entity_set; 786 | } 787 | 788 | 789 | 790 | int ActionPlanner::calcH(const Worldstate* fr, const Worldstate* to ) const 791 | { 792 | const boost::dynamic_bitset<> diff = ( (fr->values ^ to->values) & to->care ); 793 | return (int)diff.count(); 794 | } 795 | 796 | 797 | 798 | bool ActionPlanner::permuteAction(const Action &action, action_vector* result) const 799 | { 800 | action_vector permutations; 801 | 802 | // Collect this action's entities 803 | std::vector original_labels; 804 | std::vector sets; 805 | sets.reserve(action.params.size()); 806 | BOOST_FOREACH (Action::parameter param, action.params) { 807 | sets.push_back(param.set_members); 808 | original_labels.push_back(param.label); 809 | } 810 | 811 | Action new_action = action; 812 | std::vector counters(sets.size()); 813 | std::vector permutation(sets.size()); 814 | 815 | // Produce the action permutations 816 | do { 817 | // Build this permutation 818 | for (std::size_t i = 0, size = counters.size(); i != size; ++i) { 819 | permutation[i] = sets[i]->at(counters[i]); 820 | } 821 | 822 | // Verify that we obey the mutual excusion rule for the param labels 823 | if (elementUniqueness(original_labels, permutation)) { 824 | 825 | new_action.params = action.params; 826 | new_action.preconditions = action.preconditions; 827 | new_action.effects = action.effects; 828 | 829 | // Insert permuted params into the action 830 | int i = 0; 831 | BOOST_FOREACH (param_label original, original_labels) { 832 | if (!substituteParamLabel(new_action, original, permutation[i])) { 833 | printf("ActionPlanner::permuteAction - Param substitution failed for action %s.", action.name.c_str()); 834 | return false; 835 | } 836 | ++i; 837 | } 838 | 839 | new_action.buildName(); 840 | 841 | // Add to output 842 | permutations.push_back(new_action); 843 | } 844 | } while (next_permutation(sets, counters)); // Increments the permutation counter 845 | 846 | *result = permutations; 847 | return true; 848 | } 849 | 850 | 851 | 852 | bool ActionPlanner::substituteParamLabel(Action &action, param_label original, param_label replacement) const 853 | { 854 | // Verify that the action contains the original parameter 855 | Action::parameter* action_param = action.getParameter(original); 856 | if (action_param == NULL) { 857 | printf("ActionPlanner::substituteParamLabel() - Action %s does not contain param %s.\n", action.name.c_str(), original.c_str()); 858 | return false; 859 | } 860 | // Insert replacement label 861 | action_param->label = replacement; 862 | 863 | // Iterate over action preconditions 864 | BOOST_FOREACH(Action::condition_t &condition, action.preconditions) { 865 | 866 | // Iterate over the parameters of this condition 867 | BOOST_FOREACH(Proposition::parameter ¶m, condition.first.params) { 868 | 869 | // if this parameter references an action parameter (is neither a literal nor all) 870 | if (!param.literal && !param.all) { 871 | 872 | // check for match 873 | if (param.label == original) { 874 | // Insert replacement label into parameter 875 | param.label = replacement; 876 | } 877 | } 878 | } 879 | 880 | // Update name of this Proposition to use substituted params 881 | condition.first.buildName(); 882 | } 883 | 884 | // Do the same thing for the effects 885 | BOOST_FOREACH(Action::condition_t &condition, action.effects) { 886 | 887 | // Iterate over the parameters of this condition 888 | BOOST_FOREACH(Proposition::parameter ¶m, condition.first.params) { 889 | 890 | // if this parameter references an action parameter (is neither a literal nor all) 891 | if (!param.literal && !param.all) { 892 | 893 | // check for match 894 | if (param.label == original) { 895 | // Insert replacement label into parameter 896 | param.label = replacement; 897 | } 898 | } 899 | } 900 | 901 | // Update name of this Proposition to use substituted params 902 | condition.first.buildName(); 903 | } 904 | 905 | // Update the action name to use substituted params 906 | action.buildName(); 907 | 908 | return true; 909 | } 910 | 911 | 912 | 913 | template 914 | bool ActionPlanner::elementUniqueness(const string_vec& labels, const std::vector& v) const 915 | { 916 | for (size_t i = 0, size = labels.size(); i < size; ++i) { 917 | for (size_t j = i+1, size = labels.size(); j < size; ++j) { 918 | if (labels[i] != labels[j]) { // if the labels are different... 919 | if (v[i] == v[j]) { // then the values must be different 920 | return false; 921 | } 922 | } 923 | } 924 | } 925 | 926 | return true; 927 | } 928 | 929 | 930 | 931 | bool ActionPlanner::linkEntities(Action& action) const 932 | { 933 | // For each action parameter 934 | BOOST_FOREACH(Action::parameter& param, action.params) { 935 | 936 | // Confirm that the needed EntitySet exists 937 | const EntitySet* set = findEntitySet(param.type); 938 | if (set == NULL) { 939 | printf("ActionPlanner::associateEntities() - EntitySet %s not found for action %s.\n", param.type.c_str(), action.name.c_str()); 940 | return false; 941 | } 942 | 943 | // Associate the set with the parameter 944 | param.set_members = &(set->members); 945 | } 946 | 947 | // For each precondition 948 | BOOST_FOREACH(Action::condition_t& precondition, action.preconditions) { 949 | 950 | // For each parameter of the proposition 951 | BOOST_FOREACH (Proposition::parameter& param, precondition.first.params) { 952 | 953 | // If this parameter references an entity set 954 | if (!param.entity_set_name.empty()) { 955 | 956 | // Confirm that the needed EntitySet exists 957 | const EntitySet* set = findEntitySet(param.entity_set_name); 958 | if (set == NULL) { 959 | printf("ActionPlanner::associateEntities() - Failed to link EntitySet %s in action %s.\n", param.entity_set_name.c_str(), action.name.c_str()); 960 | return false; 961 | } 962 | 963 | // Associate the set with parameter 964 | param.set_members = &(set->members); 965 | 966 | // For a literal parameter, verify that the set contains the literal 967 | if (param.literal) { 968 | // search the set members 969 | EntitySet::member_list::const_iterator it = std::find(param.set_members->begin(), param.set_members->end(), param.label); 970 | 971 | // not found 972 | if (it == param.set_members->end()) { 973 | printf("ActionPlanner::associateEntities() - Failed to link literal parameter %s in action %s to EntitySet %s.\n", param.label.c_str(), action.name.c_str(), param.entity_set_name.c_str()); 974 | return false; 975 | } 976 | 977 | precondition.first.buildName(); 978 | } 979 | } 980 | } 981 | } 982 | 983 | // For each effect 984 | BOOST_FOREACH(Action::condition_t& effect, action.effects) { 985 | 986 | // For each parameter of the proposition 987 | BOOST_FOREACH (Proposition::parameter& param, effect.first.params) { 988 | 989 | // If this parameter references an entity set 990 | if (!param.entity_set_name.empty()) { 991 | 992 | // Confirm that the needed EntitySet exists 993 | const EntitySet* set = findEntitySet(param.entity_set_name); 994 | if (set == NULL) { 995 | printf("ActionPlanner::associateEntities() - Failed to link EntitySet %s in action %s.\n", param.entity_set_name.c_str(), action.name.c_str()); 996 | return false; 997 | } 998 | 999 | // Associate the set with parameter 1000 | param.set_members = &(set->members); 1001 | 1002 | // For a literal parameter, verify that the set contains the literal 1003 | if (param.literal) { 1004 | // search the set members 1005 | EntitySet::member_list::const_iterator it = std::find(param.set_members->begin(), param.set_members->end(), param.label); 1006 | 1007 | // not found 1008 | if (it == param.set_members->end()) { 1009 | printf("ActionPlanner::associateEntities() - Failed to link literal parameter %s in action %s to EntitySet %s.\n", param.label.c_str(), action.name.c_str(), param.entity_set_name.c_str()); 1010 | return false; 1011 | } 1012 | 1013 | effect.first.buildName(); 1014 | } 1015 | } 1016 | } 1017 | } 1018 | 1019 | return true; 1020 | } 1021 | 1022 | 1023 | 1024 | bool ActionPlanner::expandAllParams(Action &action) const 1025 | { 1026 | std::vector expanded_conditions; 1027 | 1028 | // Iterate over action preconditions 1029 | for(std::vector::iterator condition = action.preconditions.begin(); condition != action.preconditions.end(); ) { 1030 | 1031 | // Build expanded conditions 1032 | expanded_conditions = expandAllCondition(*condition); 1033 | 1034 | // We found a condition to expand 1035 | if (!expanded_conditions.empty()) { 1036 | // Delete the condition that we expanded 1037 | action.preconditions.erase(condition); 1038 | 1039 | // Append expanded conditions to action 1040 | action.preconditions.insert(action.preconditions.end(), expanded_conditions.begin(), expanded_conditions.end()); 1041 | 1042 | // Reset the iteration to the beginning 1043 | condition = action.preconditions.begin(); 1044 | 1045 | expanded_conditions.clear(); 1046 | } 1047 | else { 1048 | ++condition; 1049 | } 1050 | } 1051 | 1052 | // Iterate over action preconditions 1053 | for(std::vector::iterator condition = action.effects.begin(); condition != action.effects.end(); ) { 1054 | 1055 | // Build expanded conditions 1056 | expanded_conditions = expandAllCondition(*condition); 1057 | 1058 | // We found a condition to expand 1059 | if (!expanded_conditions.empty()) { 1060 | // Delete the condition that we expanded 1061 | action.effects.erase(condition); 1062 | 1063 | // Append expanded conditions to action 1064 | action.effects.insert(action.effects.end(), expanded_conditions.begin(), expanded_conditions.end()); 1065 | 1066 | // Reset the iteration to the beginning 1067 | condition = action.effects.begin(); 1068 | 1069 | expanded_conditions.clear(); 1070 | } 1071 | else { 1072 | ++condition; 1073 | } 1074 | } 1075 | 1076 | return true; 1077 | } 1078 | 1079 | 1080 | 1081 | std::vector ActionPlanner::expandAllCondition(Action::condition_t condition) const 1082 | { 1083 | std::vector output; 1084 | 1085 | // Search for an ALL parameter 1086 | BOOST_FOREACH(Proposition::parameter& param, condition.first.params) { 1087 | if (param.all) { 1088 | param.all = false; 1089 | 1090 | // Expand this parameter 1091 | BOOST_FOREACH (param_label member, *param.set_members) { 1092 | param.label = member; 1093 | condition.first.buildName(); 1094 | output.push_back(condition); 1095 | } 1096 | break; 1097 | } 1098 | } 1099 | 1100 | return output; 1101 | } 1102 | 1103 | 1104 | 1105 | 1106 | bool ActionPlanner::expandNegatedParams(Action &action) const 1107 | { 1108 | std::vector expanded_conditions; 1109 | 1110 | // Iterate over action preconditions 1111 | for(std::vector::iterator condition = action.preconditions.begin(); condition != action.preconditions.end(); ) { 1112 | 1113 | // Build expanded conditions 1114 | expanded_conditions = expandNegatedCondition(*condition); 1115 | 1116 | // We found a condition to expand 1117 | if (!expanded_conditions.empty()) { 1118 | // Delete the condition that we expanded 1119 | action.preconditions.erase(condition); 1120 | 1121 | // Append expanded conditions to action 1122 | action.preconditions.insert(action.preconditions.end(), expanded_conditions.begin(), expanded_conditions.end()); 1123 | 1124 | // Reset the iteration to the beginning 1125 | condition = action.preconditions.begin(); 1126 | 1127 | expanded_conditions.clear(); 1128 | } 1129 | else { 1130 | ++condition; 1131 | } 1132 | } 1133 | 1134 | // Iterate over action preconditions 1135 | for(std::vector::iterator condition = action.effects.begin(); condition != action.effects.end(); ) { 1136 | 1137 | // Build expanded conditions 1138 | expanded_conditions = expandNegatedCondition(*condition); 1139 | 1140 | // We found a condition to expand 1141 | if (!expanded_conditions.empty()) { 1142 | // Delete the condition that we expanded 1143 | action.effects.erase(condition); 1144 | 1145 | // Append expanded conditions to action 1146 | action.effects.insert(action.effects.end(), expanded_conditions.begin(), expanded_conditions.end()); 1147 | 1148 | // Reset the iteration to the beginning 1149 | condition = action.effects.begin(); 1150 | 1151 | expanded_conditions.clear(); 1152 | } 1153 | else { 1154 | ++condition; 1155 | } 1156 | } 1157 | 1158 | return true; 1159 | } 1160 | 1161 | 1162 | 1163 | std::vector ActionPlanner::expandNegatedCondition(Action::condition_t condition) const 1164 | { 1165 | std::vector output; 1166 | 1167 | // Search for an ALL parameter 1168 | BOOST_FOREACH(Proposition::parameter& param, condition.first.params) { 1169 | if (param.negated) { 1170 | std::string original = param.label; 1171 | param.negated = false; 1172 | 1173 | // Expand this parameter 1174 | BOOST_FOREACH (param_label member, *param.set_members) { 1175 | if (member != original) { 1176 | param.label = member; 1177 | condition.first.buildName(); 1178 | output.push_back(condition); 1179 | } 1180 | } 1181 | break; 1182 | } 1183 | } 1184 | 1185 | return output; 1186 | } 1187 | 1188 | 1189 | } // namespace goap --------------------------------------------------------------------------------