├── CMakeLists.txt ├── .gitignore ├── Android.mk ├── astar.h ├── main.c ├── goap.h ├── goap.c ├── astar.c └── README.md /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.4.1) 3 | 4 | # build libgpgoap as a static lib 5 | add_library(gpgoap STATIC 6 | astar.c 7 | goap.c 8 | ) 9 | 10 | 11 | #LOCAL_CFLAGS += -std=c99 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | 4 | # Libraries 5 | *.lib 6 | *.a 7 | 8 | # Shared objects (inc. Windows DLLs) 9 | *.dll 10 | *.so 11 | *.so.* 12 | *.dylib 13 | 14 | # Executables 15 | *.exe 16 | *.out 17 | *.app 18 | 19 | # Dep files 20 | *.d 21 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := gpgoap 6 | LOCAL_SRC_FILES := astar.c goap.c 7 | 8 | #LOCAL_C_INCLUDES := 9 | LOCAL_ARM_NEON := true 10 | LOCAL_ARM_MODE := arm 11 | 12 | LOCAL_CFLAGS += -std=c99 13 | 14 | include $(BUILD_STATIC_LIBRARY) 15 | 16 | -------------------------------------------------------------------------------- /astar.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Abraham T. Stolk 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | #ifndef ASTAR_H 12 | #define ASTAR_H 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif // __cplusplus 18 | 19 | #ifndef LOGI 20 | #ifdef __cplusplus 21 | #include 22 | #else 23 | #include 24 | #endif 25 | #define LOGI(...) { printf( __VA_ARGS__ ); printf("\n"); } 26 | #define LOGW(...) { printf( "WRN " __VA_ARGS__ ); printf("\n"); } 27 | #define LOGE(...) { printf( "ERR " __VA_ARGS__ ); printf("\n"); } 28 | #endif 29 | 30 | #include "goap.h" 31 | 32 | struct astarnode; 33 | 34 | //!< A node in our network of world states. 35 | struct astarnode 36 | { 37 | worldstate_t ws; //!< The state of the world at this node. 38 | int g; //!< The cost so far. 39 | int h; //!< The heuristic for remaining cost (don't overestimate!) 40 | int f; //!< g+h combined. 41 | const char* actionname; //!< How did we get to this node? 42 | worldstate_t parentws; //!< Where did we come from? 43 | }; 44 | 45 | 46 | typedef struct astarnode astarnode_t; 47 | 48 | 49 | //! Make a plan of actions that will reach desired world state. Returns total cost of the plan. 50 | extern int astar_plan 51 | ( 52 | actionplanner_t const* ap, //!< the goap action planner that holds atoms and action repertoire 53 | worldstate_t start, //!< the current world state 54 | worldstate_t goal, //!< the desired world state 55 | const char** plan, //!< for returning all actions that make up plan 56 | worldstate_t* worldstates, //!< for returning intermediate world states 57 | int* plansize //!< in: size of plan buffer, out: size of plan (in nr of steps) 58 | ); 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Abraham T. Stolk 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | #include "goap.h" // for planner interface. 12 | #include "astar.h" // for A* search over worldstate space. 13 | 14 | 15 | #include 16 | #include 17 | 18 | int main( int argc, char* argv[] ) 19 | { 20 | (void) argc; 21 | (void) argv; 22 | static actionplanner_t ap; 23 | goap_actionplanner_clear( &ap ); 24 | 25 | goap_set_pre( &ap, "scout", "armedwithgun", true ); 26 | goap_set_pst( &ap, "scout", "enemyvisible", true ); 27 | 28 | goap_set_pre( &ap, "approach", "enemyvisible", true ); 29 | goap_set_pst( &ap, "approach", "nearenemy", true ); 30 | 31 | goap_set_pre( &ap, "aim", "enemyvisible", true ); 32 | goap_set_pre( &ap, "aim", "weaponloaded", true ); 33 | goap_set_pst( &ap, "aim", "enemylinedup", true ); 34 | 35 | goap_set_pre( &ap, "shoot", "enemylinedup", true ); 36 | goap_set_pst( &ap, "shoot", "enemyalive", false ); 37 | 38 | goap_set_pre( &ap, "load", "armedwithgun", true ); 39 | goap_set_pst( &ap, "load", "weaponloaded", true ); 40 | 41 | goap_set_pre( &ap, "detonatebomb", "armedwithbomb", true ); 42 | goap_set_pre( &ap, "detonatebomb", "nearenemy", true ); 43 | goap_set_pst( &ap, "detonatebomb", "alive", false ); 44 | goap_set_pst( &ap, "detonatebomb", "enemyalive", false ); 45 | 46 | goap_set_pre( &ap, "flee", "enemyvisible", true ); 47 | goap_set_pst( &ap, "flee", "nearenemy", false ); 48 | 49 | 50 | char desc[ 4096 ]; 51 | goap_description( &ap, desc, sizeof(desc) ); 52 | LOGI( "%s", desc ); 53 | 54 | worldstate_t fr; 55 | goap_worldstate_clear( &fr ); 56 | goap_worldstate_set( &ap, &fr, "enemyvisible", false ); 57 | goap_worldstate_set( &ap, &fr, "armedwithgun", true ); 58 | goap_worldstate_set( &ap, &fr, "weaponloaded", false ); 59 | goap_worldstate_set( &ap, &fr, "enemylinedup", false ); 60 | goap_worldstate_set( &ap, &fr, "enemyalive", true ); 61 | goap_worldstate_set( &ap, &fr, "armedwithbomb", true ); 62 | goap_worldstate_set( &ap, &fr, "nearenemy", false ); 63 | goap_worldstate_set( &ap, &fr, "alive", true ); 64 | 65 | goap_set_cost( &ap, "detonatebomb", 5 ); // make suicide more expensive than shooting. 66 | 67 | worldstate_t goal; 68 | goap_worldstate_clear( &goal ); 69 | goap_worldstate_set( &ap, &goal, "enemyalive", false ); 70 | //goap_worldstate_set( &ap, &goal, "alive", true ); // add this to avoid suicide actions in plan. 71 | 72 | worldstate_t states[16]; 73 | const char* plan[16]; 74 | int plansz=16; 75 | const int plancost = astar_plan( &ap, fr, goal, plan, states, &plansz ); 76 | LOGI( "plancost = %d", plancost ); 77 | goap_worldstate_description( &ap, &fr, desc, sizeof( desc ) ); 78 | LOGI( "%-23s%s", "", desc ); 79 | for ( int i=0; i 18 | #include 19 | #else 20 | #include 21 | #include 22 | #endif 23 | 24 | #define MAXATOMS 64 25 | #define MAXACTIONS 64 26 | 27 | typedef int64_t bfield_t; 28 | 29 | //!< Describes the world state by listing values (t/f) for all known atoms. 30 | typedef struct 31 | { 32 | bfield_t values; //!< Values for atoms. 33 | bfield_t dontcare; //!< Mask for atoms that do not matter. 34 | } worldstate_t; 35 | 36 | 37 | //!< Action planner that keeps track of world state atoms and its action repertoire. 38 | typedef struct 39 | { 40 | const char* atm_names[ MAXATOMS ]; //!< Names associated with all world state atoms. 41 | int numatoms; //!< Number of world state atoms. 42 | 43 | const char* act_names[ MAXACTIONS ]; //!< Names of all actions in repertoire. 44 | worldstate_t act_pre[ MAXACTIONS ]; //!< Preconditions for all actions. 45 | worldstate_t act_pst[ MAXACTIONS ]; //!< Postconditions for all actions (action effects). 46 | int act_costs[ MAXACTIONS ]; //!< Cost for all actions. 47 | int numactions; //!< The number of actions in out repertoire. 48 | 49 | } actionplanner_t; 50 | 51 | 52 | //!< Initialize an action planner. It will clear all information on actions and state. 53 | extern void goap_actionplanner_clear( actionplanner_t* ap ); 54 | 55 | //!< Initialize a worldstate to 'dontcare' for all state atoms. 56 | extern void goap_worldstate_clear( worldstate_t* ws ); 57 | 58 | //!< Set an atom of worldstate to specified value. 59 | extern bool goap_worldstate_set( actionplanner_t* ap, worldstate_t* ws, const char* atomname, bool value ); 60 | 61 | //!< Add a precondition for named action. 62 | extern bool goap_set_pre( actionplanner_t* ap, const char* actionname, const char* atomname, bool value ); 63 | 64 | //!< Add a postcondition for named action. 65 | extern bool goap_set_pst( actionplanner_t* ap, const char* actionname, const char* atomname, bool value ); 66 | 67 | //!< Set the cost for named action. 68 | extern bool goap_set_cost( actionplanner_t* ap, const char* actionname, int cost ); 69 | 70 | //!< Describe the action planner by listing all actions with pre and post conditions. For debugging purpose. 71 | extern void goap_description( actionplanner_t* ap, char* buf, int sz ); 72 | 73 | //!< Describe the worldstate by listing atoms that matter, in lowercase for false-valued, and uppercase for true-valued atoms. 74 | extern void goap_worldstate_description( const actionplanner_t* ap, const worldstate_t* ws, char* buf, int sz ); 75 | 76 | //!< Given the specified 'from' state, list all possible 'to' states along with the action required, and the action cost. For internal use. 77 | extern int goap_get_possible_state_transitions( actionplanner_t const* ap, worldstate_t fr, worldstate_t* to, const char** actionnames, int* actioncosts, int cnt ); 78 | 79 | #ifdef __cplusplus 80 | } 81 | #endif 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /goap.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Abraham T. Stolk 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | #if defined( _MSC_VER ) 12 | # define _CRT_SECURE_NO_WARNINGS 13 | # define snprintf _snprintf 14 | #endif 15 | 16 | #include "goap.h" 17 | #include "astar.h" 18 | 19 | #include 20 | #include 21 | 22 | 23 | static int idx_for_atomname( actionplanner_t* ap, const char* atomname ) 24 | { 25 | int idx; 26 | for ( idx=0; idx < ap->numatoms; ++idx ) 27 | if ( !strcmp( ap->atm_names[ idx ], atomname ) ) return idx; 28 | 29 | if ( idx < MAXATOMS ) 30 | { 31 | ap->atm_names[ idx ] = atomname; 32 | ap->numatoms++; 33 | return idx; 34 | } 35 | 36 | return -1; 37 | } 38 | 39 | 40 | static int idx_for_actionname( actionplanner_t* ap, const char* actionname ) 41 | { 42 | int idx; 43 | for ( idx=0; idx < ap->numactions; ++idx ) 44 | if ( !strcmp( ap->act_names[ idx ], actionname ) ) return idx; 45 | 46 | if ( idx < MAXACTIONS ) 47 | { 48 | ap->act_names[ idx ] = actionname; 49 | ap->act_costs[ idx ] = 1; // default cost is 1 50 | ap->numactions++; 51 | return idx; 52 | } 53 | 54 | return -1; 55 | } 56 | 57 | 58 | void goap_actionplanner_clear( actionplanner_t* ap ) 59 | { 60 | ap->numatoms = 0; 61 | ap->numactions = 0; 62 | for ( int i=0; iatm_names[ i ] = 0; 65 | } 66 | for ( int i=0; iact_names[ i ] = 0; 69 | ap->act_costs[ i ] = 0; 70 | goap_worldstate_clear( ap->act_pre+i ); 71 | goap_worldstate_clear( ap->act_pst+i ); 72 | } 73 | } 74 | 75 | 76 | void goap_worldstate_clear( worldstate_t* ws ) 77 | { 78 | ws->values = 0LL; 79 | ws->dontcare = -1LL; 80 | } 81 | 82 | 83 | bool goap_worldstate_set( actionplanner_t* ap, worldstate_t* ws, const char* atomname, bool value ) 84 | { 85 | const int idx = idx_for_atomname( ap, atomname ); 86 | if ( idx == -1 ) return false; 87 | ws->values = value ? ( ws->values | ( 1LL << idx ) ) : ( ws->values & ~( 1LL << idx ) ); 88 | ws->dontcare &= ~( 1LL << idx ); 89 | return true; 90 | } 91 | 92 | 93 | 94 | extern bool goap_set_pre( actionplanner_t* ap, const char* actionname, const char* atomname, bool value ) 95 | { 96 | const int actidx = idx_for_actionname( ap, actionname ); 97 | const int atmidx = idx_for_atomname( ap, atomname ); 98 | if ( actidx == -1 || atmidx == -1 ) return false; 99 | goap_worldstate_set( ap, ap->act_pre+actidx, atomname, value ); 100 | return true; 101 | } 102 | 103 | 104 | bool goap_set_pst( actionplanner_t* ap, const char* actionname, const char* atomname, bool value ) 105 | { 106 | const int actidx = idx_for_actionname( ap, actionname ); 107 | const int atmidx = idx_for_atomname( ap, atomname ); 108 | if ( actidx == -1 || atmidx == -1 ) return false; 109 | goap_worldstate_set( ap, ap->act_pst+actidx, atomname, value ); 110 | return true; 111 | } 112 | 113 | 114 | bool goap_set_cost( actionplanner_t* ap, const char* actionname, int cost ) 115 | { 116 | const int actidx = idx_for_actionname( ap, actionname ); 117 | if ( actidx == -1 ) return false; 118 | ap->act_costs[ actidx ] = cost; 119 | return true; 120 | } 121 | 122 | 123 | void goap_worldstate_description( const actionplanner_t* ap, const worldstate_t* ws, char* buf, int sz ) 124 | { 125 | int added=0; 126 | for ( int i=0; idontcare & ( 1LL << i ) ) == 0LL ) 129 | { 130 | const char* val = ap->atm_names[ i ]; 131 | char upval[ 128 ]; 132 | size_t j; 133 | for ( j=0; jvalues & ( 1LL << i ) ) != 0LL ); 137 | added = snprintf( buf, sz, "%s,", set?upval:val ); 138 | buf += added; sz -= added; 139 | } 140 | } 141 | } 142 | 143 | 144 | void goap_description( actionplanner_t* ap, char* buf, int sz ) 145 | { 146 | int added=0; 147 | for ( int a=0; anumactions; ++a ) 148 | { 149 | added=snprintf( buf, sz, "%s:\n", ap->act_names[ a ] ); 150 | sz -= added; buf += added; 151 | 152 | worldstate_t pre = ap->act_pre[ a ]; 153 | worldstate_t pst = ap->act_pst[ a ]; 154 | for ( int i=0; iatm_names[ i ], v ); 159 | sz -= added; buf+= added; 160 | } 161 | for ( int i=0; iatm_names[ i ], v ); 166 | sz -= added; buf+= added; 167 | } 168 | } 169 | } 170 | 171 | 172 | static worldstate_t goap_do_action( actionplanner_t const* ap, int actionnr, worldstate_t fr ) 173 | { 174 | const worldstate_t pst = ap->act_pst[ actionnr ]; 175 | const bfield_t unaffected = pst.dontcare; 176 | const bfield_t affected = ( unaffected ^ -1LL ); 177 | 178 | fr.values = ( fr.values & unaffected ) | ( pst.values & affected ); 179 | fr.dontcare &= pst.dontcare; 180 | return fr; 181 | } 182 | 183 | 184 | int goap_get_possible_state_transitions( actionplanner_t const* ap, worldstate_t fr, worldstate_t* to, const char** actionnames, int* actioncosts, int cnt ) 185 | { 186 | int writer=0; 187 | for ( int i=0; inumactions && writeract_pre[ i ]; 191 | const bfield_t care = ( pre.dontcare ^ -1LL ); 192 | const bool met = ( ( pre.values & care ) == ( fr.values & care ) ); 193 | if ( met ) 194 | { 195 | actionnames[ writer ] = ap->act_names[ i ]; 196 | actioncosts[ writer ] = ap->act_costs[ i ]; 197 | to[ writer ] = goap_do_action( ap, i, fr ); 198 | ++writer; 199 | } 200 | } 201 | return writer; 202 | } 203 | 204 | -------------------------------------------------------------------------------- /astar.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Abraham T. Stolk 3 | 4 | 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 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | 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. 9 | */ 10 | 11 | #include "astar.h" 12 | #include "goap.h" 13 | 14 | #include 15 | 16 | 17 | #define MAXOPEN 1024 //!< The maximum number of nodes we can store in the opened set. 18 | #define MAXCLOS 1024 //!< The maximum number of nodes we can store in the closed set. 19 | 20 | 21 | static astarnode_t opened[ MAXOPEN ]; //!< The set of nodes we should consider. 22 | static astarnode_t closed[ MAXCLOS ]; //!< The set of nodes we already visited. 23 | 24 | static int numOpened = 0; //!< The nr of nodes in our opened set. 25 | static int numClosed = 0; //!< The nr of nodes in our closed set. 26 | 27 | 28 | //!< This is our heuristic: estimate for remaining distance is the nr of mismatched atoms that matter. 29 | static int calc_h( worldstate_t fr, worldstate_t to ) 30 | { 31 | const bfield_t care = ( to.dontcare ^ -1LL ); 32 | const bfield_t diff = ( ( fr.values & care ) ^ ( to.values & care ) ); 33 | int dist=0; 34 | for ( int i=0; iactionname ) 65 | { 66 | if ( idx >= 0 ) 67 | { 68 | plan[ idx ] = curnode->actionname; 69 | worldstates[ idx ] = curnode->ws; 70 | const int i = idx_in_closed( curnode->parentws ); 71 | curnode = ( i == -1 ) ? 0 : closed+i; 72 | } 73 | --idx; 74 | numsteps++; 75 | } 76 | idx++; // point to last filled 77 | 78 | if ( idx > 0 ) 79 | for ( int i=0; i= 0 && cost < opened[ idx_o ].g ) 180 | { 181 | // remove neighbor from OPEN, because new path is better 182 | if ( numOpened ) opened[ idx_o ] = opened[ numOpened-1 ]; 183 | numOpened--; 184 | idx_o = -1; // BUGFIX: neighbor is no longer in OPEN, signal this so that we can re-add it. 185 | } 186 | // if neighbor in CLOSED and cost less than g(neighbor): 187 | if ( idx_c >= 0 && cost < closed[ idx_c ].g ) 188 | { 189 | // remove neighbor from CLOSED 190 | if ( numClosed ) closed[ idx_c ] = closed[ numClosed-1 ]; 191 | numClosed--; 192 | idx_c = -1; // BUGFIX: neighbour is no longer in CLOSED< signal this so that we can re-add it. 193 | } 194 | // if neighbor not in OPEN and neighbor not in CLOSED: 195 | if ( idx_c == -1 && idx_o == -1 ) 196 | { 197 | nb.ws = to[ i ]; 198 | nb.g = cost; 199 | nb.h = calc_h( nb.ws, goal ); 200 | nb.f = nb.g + nb.h; 201 | nb.actionname = actionnames[ i ]; 202 | nb.parentws = cur.ws; 203 | opened[ numOpened++ ] = nb; 204 | } 205 | if ( numOpened == MAXOPEN ) { LOGI("Opened set overflow"); return -1; } // ran out of storage for opened set 206 | } 207 | } while( true ); 208 | 209 | return -1; 210 | } 211 | 212 | 213 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # General Purpose GOAP 2 | 3 | ## Introduction 4 | GOAP, or Goal Oriented Action Planning is a powerful tool to create game AI. For all the details I will refer to [Jeff Orkin's collection of articles](http://web.media.mit.edu/~jorkin/goap.html) 5 | Deleted, [mirror here](https://web.archive.org/web/20230912145018/https://alumni.media.mit.edu/~jorkin/goap.html). 6 | In short: GOAP will let computer controlled characters (NPCs) make action plans that can achieve desired goals. It will do so in a highly maintainable, easily extendible, highly modular fashion. Naive implementation of AI code will invariably blow up for any non trivial problem. GOAP on the other hand, is robust and is unlikely to buckle under large complexity. This software implements GOAP in the C programming language. It does so in a generic fashion, which makes it suitable for many projects. 7 | 8 | ## Basics of GOAP 9 | Creating a plan for AI controlled entities comes down to the following steps: 10 | 11 | 1. Describe the repertoire of actions available. 12 | 2. Describe the current state of the world. 13 | 3. Describe the desired state of the world (goal). 14 | 15 | To describe the actions, we specify: 16 | 17 | * The preconditions for the action. 18 | * The postconditions (effects) of the action. 19 | * The cost of the action. 20 | 21 | To describe world state, we define a set of world state atoms. Each atom has a tag, and a boolean value. 22 | 23 | The planner will then be able to formulate a plan of actions that takes the world to the desired state, provided such a path exists. The plan formulated is guaranteed to be the lowest cost plan. 24 | 25 | ## Example Scenario 26 | 27 | Let us consider a planner for an AI soldier. Our soldier can perform the following actions: scout, approach, aim, shoot, load, detonatebomb, flee. 28 | 29 | * **scout** requires: *armedwithgun*. Effect: *enemyvisible*. 30 | * **approach** requires: *enemyvisible*. Effect: *nearenemy*. 31 | * **aim** requires: *enemyvisible* and *weaponloaded*. Effect: *enemylinedup*. 32 | * **shoot** requires: *enemylinedup*. Effect: *!enemyalive*. 33 | * **load** requires: *armedwithgun*. Effect: *weaponloaded*. 34 | * **detonatebomb** requires: *armedwithbomb* and *nearenemy*. Effect: *!alive* and *!enemyalive*. 35 | * **flee** requires: *enemyvisible*. Effect: *!nearenemy*. 36 | 37 | Next, we will tell the planner that currently: 38 | Enemy is not visible, we are armed with gun, our weapon is not loaded, enemy is not lined up, enemy is alive, we are armed with bomb, we are not near enemy, we are alive. 39 | 40 | Then we tell our planner what our desired world looks like. We only care about one thing: our enemy is not alive. 41 | 42 | With this, the planner can formulate a plan of actions to make this happen. For the moment, let's assume all our actions have the default cost 1 associated with it. This is what the planner will return to us: 43 | 44 | ARMEDWITHGUN,enemyvisible,nearenemy,weaponloaded,enemylinedup,ENEMYALIVE,ARMEDWITHBOMB,ALIVE, 45 | 0: scout ARMEDWITHGUN,ENEMYVISIBLE,nearenemy,weaponloaded,enemylinedup,ENEMYALIVE,ARMEDWITHBOMB,ALIVE, 46 | 1: approach ARMEDWITHGUN,ENEMYVISIBLE,NEARENEMY,weaponloaded,enemylinedup,ENEMYALIVE,ARMEDWITHBOMB,ALIVE, 47 | 2: detonatebomb ARMEDWITHGUN,ENEMYVISIBLE,NEARENEMY,weaponloaded,enemylinedup,enemyalive,ARMEDWITHBOMB,alive, 48 | 49 | Note: this notation uses lowercase if the condition is false, and uppercase if the condition is true. 50 | The first line shows the current world state, and the last line the desired world state, with all intermediate states in between. The plan to execute is: (scout, approach, detonatebomb). 51 | 52 | If we follow this plan, we have the unfortunate side effect that not only our enemy dies, but we die as well. This is easily solved by making detonatingbomb a higher cost action. But a more elegant approach would be to change our desired world state, and tell the planner that not only do we want our enemy dead, we want to ourselves alive. The planner will now create: 53 | 54 | ARMEDWITHGUN,enemyvisible,nearenemy,weaponloaded,enemylinedup,ENEMYALIVE,ARMEDWITHBOMB,ALIVE, 55 | 0: scout ARMEDWITHGUN,ENEMYVISIBLE,nearenemy,weaponloaded,enemylinedup,ENEMYALIVE,ARMEDWITHBOMB,ALIVE, 56 | 1: load ARMEDWITHGUN,ENEMYVISIBLE,nearenemy,WEAPONLOADED,enemylinedup,ENEMYALIVE,ARMEDWITHBOMB,ALIVE, 57 | 2: aim ARMEDWITHGUN,ENEMYVISIBLE,nearenemy,WEAPONLOADED,ENEMYLINEDUP,ENEMYALIVE,ARMEDWITHBOMB,ALIVE, 58 | 3: shoot ARMEDWITHGUN,ENEMYVISIBLE,nearenemy,WEAPONLOADED,ENEMYLINEDUP,enemyalive,ARMEDWITHBOMB,ALIVE, 59 | 60 | 61 | ## Example Code 62 | 63 | The entire scenario described above is implemented succinctly in these few lines of code: 64 | 65 | #include "goap.h" 66 | #include "astar.h" 67 | 68 | actionplanner_t ap; 69 | goap_actionplanner_clear( &ap ); // initializes action planner 70 | 71 | // describe repertoire of actions 72 | goap_set_pre( &ap, "scout", "armedwithgun", true ); 73 | goap_set_pst( &ap, "scout", "enemyvisible", true ); 74 | 75 | goap_set_pre( &ap, "approach", "enemyvisible", true ); 76 | goap_set_pst( &ap, "approach", "nearenemy", true ); 77 | 78 | goap_set_pre( &ap, "aim", "enemyvisible", true ); 79 | goap_set_pre( &ap, "aim", "weaponloaded", true ); 80 | goap_set_pst( &ap, "aim", "enemylinedup", true ); 81 | 82 | goap_set_pre( &ap, "shoot", "enemylinedup", true ); 83 | goap_set_pst( &ap, "shoot", "enemyalive", false ); 84 | 85 | goap_set_pre( &ap, "load", "armedwithgun", true ); 86 | goap_set_pst( &ap, "load", "weaponloaded", true ); 87 | 88 | goap_set_pre( &ap, "detonatebomb", "armedwithbomb", true ); 89 | goap_set_pre( &ap, "detonatebomb", "nearenemy", true ); 90 | goap_set_pst( &ap, "detonatebomb", "alive", false ); 91 | goap_set_pst( &ap, "detonatebomb", "enemyalive", false ); 92 | 93 | goap_set_pre( &ap, "flee", "enemyvisible", true ); 94 | goap_set_pst( &ap, "flee", "nearenemy", false ); 95 | 96 | // describe current world state. 97 | worldstate_t fr; 98 | goap_worldstate_clear( &fr ); 99 | goap_worldstate_set( &ap, &fr, "enemyvisible", false ); 100 | goap_worldstate_set( &ap, &fr, "armedwithgun", true ); 101 | goap_worldstate_set( &ap, &fr, "weaponloaded", false ); 102 | goap_worldstate_set( &ap, &fr, "enemylinedup", false ); 103 | goap_worldstate_set( &ap, &fr, "enemyalive", true ); 104 | goap_worldstate_set( &ap, &fr, "armedwithbomb", true ); 105 | goap_worldstate_set( &ap, &fr, "nearenemy", false ); 106 | goap_worldstate_set( &ap, &fr, "alive", true ); 107 | 108 | // describe desired world state. 109 | worldstate_t goal; 110 | goap_worldstate_clear( &goal ); 111 | goap_worldstate_set( &ap, &goal, "enemyalive", false ); 112 | //goap_worldstate_set( &ap, &goal, "alive", true ); // add this to avoid suicide actions in the plan. 113 | 114 | worldstate_t states[16]; 115 | const char* plan[16]; // The planner will return the action plan in this array. 116 | int plansz=16; // Size of our return buffers. 117 | const int plancost = astar_plan( &ap, fr, goal, plan, states, &plansz ); 118 | 119 | And that is it. The plan's size will be returned in *plansz* which is not necessarily the same as the plan cost. You can access the *plan* array elements for the actual steps (actions). For each action in the plan, the resulting world state is also available in the *states* array. 120 | 121 | ## Implementation Notes 122 | 123 | To build, use a C99 compliant C compiler. You can invoke with: `$ gcc -std=c99 astar.c goap.c main.c` 124 | 125 | The strength of GPGOAP is that the API is very generic: both world state and actions are solely described with C strings and booleans. No game specific enums end up in the API. With this power comes responsibility: do not make typos in the action names or atom names. They will end up representing different atoms if you do. 126 | 127 | For performance, all the tags for the world state atoms are converted to an entry in a bit field. This bit field is implemented as a 'long long int', which typically is 8 bytes or 64 bits. This means that you cannot use more than 64 atoms to describe the world and the actions to the planner. When using multiple NPCs in your game, I suggest using separate planners for them, so that if two NPCs have different action sets, you don't end up combining their atom name space and exceeding 64 tags. 128 | 129 | The only atom type supported is boolean. This means that scalars cannot be used to describe the world. I advice splitting up a scalar value with multiple booleans. E.g. fuelempy, fuellow, fuelfull to encode a single scalar fuel value. 130 | 131 | I strongly advice using lowercase names for the world state atoms. I have provided a debug function that will return a string representation of a world state that you can print out. In this representation, lowercase is used for false valued atoms, and uppercase is used for true valued atoms. 132 | 133 | ## Bugs 134 | 135 | No known bugs. 136 | 137 | ## Files 138 | 139 | * **goap.h goap.c** implements the planner. 140 | * **astar.h astar.c** implements A* search over the world state space. 141 | * **main.c** sample scenario. 142 | 143 | goap and astar are codependent unfortunately. Keeping them in separate files makes sense though, as they address two distinct parts of the system. 144 | 145 | ## Thanks 146 | 147 | * Thank you Amit Patel for [describing](http://theory.stanford.edu/~amitp/GameProgramming/ImplementationNotes.html) A* search so succinctly. 148 | * Thank you Jeff Orkin for the origins of GOAP. 149 | 150 | ## License 151 | 152 | Copyright 2012 [Abraham T. Stolk](http://stolk.org) 153 | 154 | Licensed under the Apache License, Version 2.0 (the "License"); 155 | you may not use this file except in compliance with the License. 156 | You may obtain a copy of the License at 157 | 158 | [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 159 | 160 | Unless required by applicable law or agreed to in writing, software 161 | distributed under the License is distributed on an "AS IS" BASIS, 162 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 163 | See the License for the specific language governing permissions and 164 | limitations under the License. 165 | --------------------------------------------------------------------------------