Files
PixelDefense/engine/breeze/ai/behaviour_tree.c

606 lines
18 KiB
C

#include "behaviour_tree.h"
#include "../memory/memory.h"
#include "../util/object_pool.h"
//#define GET_NODE(idx) ((BzAIBTNode *) bzObjectPoolGetObject(bt->nodePool, idx))
struct BzBTNode {
BzBTNode *parent;
// Children
BzBTNode *first;
BzBTNode *last;
// Siblings
BzBTNode *prev;
BzBTNode *next;
BzBTNodeType type;
const char *name;
union {
struct {
i32 n;
} repeat;
struct {
f32 ms;
} delay;
struct {
BzBTActionFn fn;
} action;
} as;
};
struct BzBTNodeState {
const BzBTNode *node;
BzBTNodeState *next;
BzBTNodeState *prev;
union {
struct {
BzBTNode *running;
} composite;
struct {
i32 iter;
} repeat;
struct {
f32 elapsed;
} delay;
} as;
};
size_t bzBTGetNodeSize() {
return sizeof(BzBTNode);
}
size_t bzBTGetNodeStateSize() {
return sizeof(BzBTNodeState);
}
const char *bzBTNodeTypeToStr(BzBTNodeType type) {
switch (type) {
case BZ_BT_COMP_SELECTOR:
return "SELECTOR";
case BZ_BT_COMP_SEQUENCE:
return "SEQUENCE";
case BZ_BT_COMP_PARALLEL_SELECTOR:
return "P_SELECTOR";
case BZ_BT_COMP_PARALLEL_SEQUENCE:
return "P_SEQUENCE";
case BZ_BT_DECOR_DUMMY:
return "DUMMY";
case BZ_BT_DECOR_SUCCESS:
return "RETURN_SUCCESS";
case BZ_BT_DECOR_FAIL:
return "RETURN_FAIL";
case BZ_BT_DECOR_INVERT:
return "INVERT";
case BZ_BT_DECOR_UNTIL_SUCCESS:
return "UNTIL_SUCCESS";
case BZ_BT_DECOR_UNTIL_FAIL:
return "UNTIL_FAIL";
case BZ_BT_DECOR_REPEAT:
return "REPEAT";
case BZ_BT_DECOR_DELAY:
return "DELAY";
case BZ_BT_ACTION:
return "ACTION";
}
}
static BzBTNode *bzBTNodeMake(BzObjectPool *nodePool, BzBTNode *parent, BzBTNodeType type) {
BZ_ASSERT(nodePool);
BZ_ASSERT(bzObjectPoolGetObjectSize(nodePool) == bzBTGetNodeSize());
BzBTNode *node = bzObjectPool(nodePool);
bzMemSet(node, 0, sizeof(*node));
node->type = type;
if (parent && parent->last) {
parent->last->next = node;
node->prev = parent->last;
parent->last = node;
} else if (parent) {
parent->first = node;
parent->last = node;
}
node->parent = parent;
return node;
}
BzBTNode *bzBTMakeRoot(BzObjectPool *nodePool) {
return bzBTNodeMake(nodePool, NULL, BZ_BT_DECOR_DUMMY);
}
void bzBTDestroyRoot(BzObjectPool *nodePool, BzBTNode *node) {
BZ_ASSERT(node);
BzBTNode *pNode = node;
while (pNode) {
BzBTNode *next = pNode->next;
bzBTDestroyRoot(nodePool, pNode);
pNode = next;
}
bzObjectPoolRelease(nodePool, node);
}
BzBTNode *bzBTCompSelector(BzObjectPool *nodePool, BzBTNode *parent, bool parallel) {
BzBTNodeType type = parallel ?
BZ_BT_COMP_PARALLEL_SELECTOR :
BZ_BT_COMP_SELECTOR;
return bzBTNodeMake(nodePool, parent, type);
}
BzBTNode *bzBTCompSequence(BzObjectPool *nodePool, BzBTNode *parent, bool parallel) {
BzBTNodeType type = parallel ?
BZ_BT_COMP_PARALLEL_SEQUENCE :
BZ_BT_COMP_SEQUENCE;
return bzBTNodeMake(nodePool, parent, type);
}
BzBTNode *bzBTDecorDummy(BzObjectPool *nodePool, BzBTNode *parent) {
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_DUMMY);
}
BzBTNode *bzBTDecorSuccess(BzObjectPool *nodePool, BzBTNode *parent) {
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_SUCCESS);
}
BzBTNode *bzBTDecorFail(BzObjectPool *nodePool, BzBTNode *parent) {
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_FAIL);
}
BzBTNode *bzBTDecorInvert(BzObjectPool *nodePool, BzBTNode *parent) {
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_INVERT);
}
BzBTNode *bzBTDecorUntilSuccess(BzObjectPool *nodePool, BzBTNode *parent) {
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_UNTIL_SUCCESS);
}
BzBTNode *bzBTDecorUntilFail(BzObjectPool *nodePool, BzBTNode *parent) {
return bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_UNTIL_FAIL);
}
BzBTNode *bzBTDecorRepeat(BzObjectPool *nodePool, BzBTNode *parent, i32 n) {
BzBTNode *node = bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_REPEAT);
node->as.repeat.n = n;
return node;
}
BzBTNode *bzBTDecorDelay(BzObjectPool *nodePool, BzBTNode *parent, f32 ms) {
BzBTNode *node = bzBTNodeMake(nodePool, parent, BZ_BT_DECOR_DELAY);
node->as.delay.ms = ms;
return node;
}
BzBTNode *bzBTAction(BzObjectPool *nodePool, BzBTNode *parent, BzBTActionFn fn) {
BzBTNode *node = bzBTNodeMake(nodePool, parent, BZ_BT_ACTION);
node->as.action.fn = fn;
return node;
}
void bzBTNodeSetName(BzBTNode *node, const char *name) {
node->name = name;
}
const char *bzBTNodeGetName(const BzBTNode *node) {
return node->name;
}
i32 bzBTDecorGetRepeat(const BzBTNode *node) {
BZ_ASSERT(node->type == BZ_BT_DECOR_REPEAT);
return node->as.repeat.n;
}
f32 bzBTDecorGetDelay(const BzBTNode *node) {
BZ_ASSERT(node->type == BZ_BT_DECOR_DELAY);
return node->as.delay.ms;
}
BzBTActionFn bzBTActionGetFn(const BzBTNode *node) {
BZ_ASSERT(node->type == BZ_BT_ACTION);
return node->as.action.fn;
}
BzBTNodeType bzBTGetNodeType(const BzBTNode *node) {
return node->type;
}
BzBTNode *bzBTNodeChild(const BzBTNode *node) {
return node->first;
}
BzBTNode *bzBTNodeNext(const BzBTNode *node) {
return node->next;
}
const BzBTNodeState *bzBTNodeStateNext(const BzBTNodeState *state) {
BZ_ASSERT(state);
return state->next;
}
bool bzBTNodeMatchesState(const BzBTNode *node, const BzBTNodeState *state) {
return state && state->node == node;
}
BzBTNode *bzBTCompStateGetRunningChild(const BzBTNodeState *state) {
BZ_ASSERT(state->node);
BzBTNodeType type = state->node->type;
bool isComposite = type == BZ_BT_COMP_SELECTOR ||
type == BZ_BT_COMP_PARALLEL_SELECTOR ||
type == BZ_BT_COMP_SEQUENCE ||
type == BZ_BT_COMP_PARALLEL_SEQUENCE;
BZ_ASSERT(isComposite);
return state->as.composite.running;
}
i32 bzBTRepeatStateGetIter(const BzBTNodeState *state) {
BZ_ASSERT(state->node && state->node->type == BZ_BT_DECOR_REPEAT);
return state->as.repeat.iter;
}
f32 bzBTDelayStateGetElapsed(const BzBTNodeState *state) {
BZ_ASSERT(state->node && state->node->type == BZ_BT_DECOR_DELAY);
return state->as.delay.elapsed;
}
BzBTState bzBTCreateState(const BzBTStateDesc *desc) {
BZ_ASSERT(desc->pool);
BZ_ASSERT(bzObjectPoolGetObjectSize(desc->pool) == bzBTGetNodeStateSize());
BZ_ASSERT(desc->root);
return (BzBTState) {
.root = desc->root,
._first = NULL,
._last = NULL,
.nodeStatePool = desc->pool,
.userData = desc->userData
};
}
void bzBTDestroyState(BzBTState *state) {
BzBTNodeState *pNodeState = state->_first;
while (pNodeState) {
BzBTNodeState *next = pNodeState->next;
bzObjectPoolRelease(state->nodeStatePool, pNodeState);
pNodeState = next;
}
bzMemSet(state, 0, sizeof(*state));
}
typedef struct BzBTExecState {
BzBTNodeState *first;
BzBTNodeState *last;
void *userData;
} BzBTExecState;
typedef struct BzBTExecStatus {
BzBTStatus status;
BzBTExecState state;
} BzBTExecStatus;
BzBTNodeState *execStatePopFront(BzBTExecState *state) {
BZ_ASSERT(state->first);
BzBTNodeState *popped = state->first;
if (state->first)
state->first = state->first->next;
if (state->first == NULL)
state->last = NULL;
popped->prev = NULL;
popped->next = NULL;
return popped;
}
void execStatePushBack(BzBTExecState *state, BzBTNodeState *nodeState) {
BZ_ASSERT(state);
nodeState->next = NULL;
nodeState->prev = state->last;
if (state->last) {
state->last->next = nodeState;
} else {
state->first = nodeState;
state->last = nodeState;
}
state->last = nodeState;
}
void execStateClear(BzBTExecState *state, BzObjectPool *pool) {
BzBTNodeState *pState = state->first;
while (pState) {
BzBTNodeState *next = pState->next;
bzObjectPoolRelease(pool, pState);
pState = next;
}
state->first = NULL;
state->last = NULL;
}
void execStateMerge(BzBTExecState *state, BzBTExecState *toMerge) {
BzBTNodeState *pState = toMerge->first;
while (pState) {
BzBTNodeState *next = pState->next;
execStatePushBack(state, pState);
pState = next;
}
toMerge->first = NULL;
toMerge->last = NULL;
}
/**
*
* @param node
* @param state
* @return
*/
bool nodeMatchesState(const BzBTNode *node, const BzBTExecState *state) {
return state->first && state->first->node == node;
}
/**
* Return BzBTNodeState for given node, if node matches first state node.
*/
static BzBTNodeState *getNodeState(const BzBTNode *node, BzBTExecState *state) {
if (nodeMatchesState(node, state)) {
return execStatePopFront(state);
}
return NULL;
}
static BzBTNodeState *ensureValidNodeState(const BzBTNode *node, BzObjectPool *pool, BzBTNodeState *existing) {
if (existing)
return existing;
existing = bzObjectPool(pool);
existing->node = node;
existing->prev = NULL;
existing->next = NULL;
bzMemSet(&existing->as, 0, sizeof(existing->as));
return existing;
}
static void releaseNodeState(BzObjectPool *pool, BzBTNodeState *nodeState) {
bzObjectPoolRelease(pool, nodeState);
}
static inline BzBTExecStatus bzBTExecuteNode(const BzBTNode *node, f32 dt,
BzBTExecState *nodeState, BzObjectPool *statePool);
#if 0
static inline BzBTExecStatus bzBTExecuteComposite(const BzBTNode *node, f32 dt,
BzBTExecState *nodeState, BzObjectPool *statePool) {
BzBTNodeState *nextState = getNextNodeState(node, nodeState);
BzBTNode *start = node->first;
bool isParallel = node->type == BZ_BT_COMP_PARALLEL_SEQUENCE ||
node->type == BZ_BT_COMP_PARALLEL_SELECTOR;
if (!isParallel && nodeMatchesState(node, nodeState))
start = nodeState->as.composite.running;
// Always push dummy state
if (nodeMatchesState(node, nodeState)) {
bzBTStateRenew(oldState, newState, nodeState);
} else {
nodeState = bzBTStatePool(oldState, node);
bzBTStateAppend(newState, nodeState);
}
i32 numRunning = 0;
i32 numSuccessful = 0;
i32 numFailed = 0;
i32 numChildren = 0;
BzBTStatus status = BZ_BT_ERROR;
BzBTNode *child = start;
for (;child; child = child->next) {
BzBTStatus childStatus = bzBTExecuteNode(child, dt, nextState, oldState, newState);
numChildren++;
switch (childStatus) {
case BZ_BT_RUNNING:
numRunning++;
break;
case BZ_BT_SUCCESS:
numSuccessful++;
break;
case BZ_BT_FAIL:
numFailed++;
break;
default:
break;
}
switch (node->type) {
case BZ_BT_COMP_SELECTOR:
case BZ_BT_COMP_PARALLEL_SELECTOR:
if (childStatus == BZ_BT_SUCCESS)
status = BZ_BT_SUCCESS;
break;
case BZ_BT_COMP_SEQUENCE:
case BZ_BT_COMP_PARALLEL_SEQUENCE:
if (childStatus == BZ_BT_FAIL)
status = BZ_BT_FAIL;
break;
default:
break;
}
if (status == BZ_BT_FAIL || status == BZ_BT_SUCCESS)
break;
if (numRunning > 0 && !isParallel) {
status = BZ_BT_RUNNING;
break;
}
}
switch (node->type) {
case BZ_BT_COMP_SELECTOR:
case BZ_BT_COMP_PARALLEL_SELECTOR:
if (numFailed == numChildren)
status = BZ_BT_FAIL;
break;
case BZ_BT_COMP_SEQUENCE:
case BZ_BT_COMP_PARALLEL_SEQUENCE:
if (numSuccessful == numChildren)
status = BZ_BT_SUCCESS;
break;
default:
break;
}
if (status == BZ_BT_ERROR) {
bzBTStateRelease(newState, nodeState);
return BZ_BT_ERROR;
}
bool finished = status == BZ_BT_SUCCESS ||
status == BZ_BT_FAIL;
if (finished) {
// Dummy state is no longer needed
bzBTStateRelease(newState, nodeState);
} else {
BZ_ASSERT(status == BZ_BT_RUNNING);
nodeState->as.composite.running = child;
}
return status;
}
#endif
static inline BzBTExecStatus bzBTExecuteDecorator(const BzBTNode *node, f32 dt,
BzBTExecState *state, BzObjectPool *statePool) {
// Ensure decorator has only one child, if any
BZ_ASSERT(!node->first || node->first == node->last);
BzBTNodeState *nodeState = getNodeState(node, state);
BzBTExecStatus retStatus = { .status = BZ_BT_ERROR };
switch (node->type) {
case BZ_BT_DECOR_REPEAT:
nodeState = ensureValidNodeState(node, statePool, nodeState);
execStatePushBack(&retStatus.state, nodeState);
break;
case BZ_BT_DECOR_DELAY:
nodeState = ensureValidNodeState(node, statePool, nodeState);
nodeState->as.delay.elapsed += dt;
execStatePushBack(&retStatus.state, nodeState);
if (nodeState->as.delay.elapsed < node->as.delay.ms) {
retStatus.status = BZ_BT_RUNNING;
return retStatus;
}
BZ_ASSERT(nodeState == execStatePopFront(&retStatus.state));
releaseNodeState(statePool, nodeState);
break;
default:
break;
}
// Implicit success, if no children are present
BzBTExecStatus inStatus = { .status = BZ_BT_SUCCESS };
if (node->first)
inStatus = bzBTExecuteNode(node->first, dt, state, statePool);
// Propagate ERROR, RUNNING up
if (inStatus.status == BZ_BT_ERROR) {
execStateClear(&inStatus.state, statePool);
execStateClear(&retStatus.state, statePool);
retStatus.status = BZ_BT_ERROR;
return retStatus;
}
if (inStatus.status == BZ_BT_RUNNING) {
execStateMerge(&retStatus.state, &inStatus.state);
retStatus.status = BZ_BT_RUNNING;
return retStatus;
}
BZ_ASSERT(inStatus.state.first == NULL);
switch (node->type) {
case BZ_BT_DECOR_DUMMY:
case BZ_BT_DECOR_DELAY: // Delay already handled
retStatus.status = inStatus.status;
break;
case BZ_BT_DECOR_SUCCESS:
retStatus.status = BZ_BT_SUCCESS;
break;
case BZ_BT_DECOR_FAIL:
retStatus.status = BZ_BT_FAIL;
break;
case BZ_BT_DECOR_INVERT:
if (inStatus.status == BZ_BT_FAIL)
retStatus.status = BZ_BT_SUCCESS;
if (inStatus.status == BZ_BT_SUCCESS)
retStatus.status = BZ_BT_FAIL;
break;
case BZ_BT_DECOR_UNTIL_SUCCESS:
if (inStatus.status == BZ_BT_SUCCESS)
retStatus.status = BZ_BT_SUCCESS;
else
retStatus.status = BZ_BT_RUNNING;
break;
case BZ_BT_DECOR_UNTIL_FAIL:
if (inStatus.status == BZ_BT_FAIL)
retStatus.status = BZ_BT_SUCCESS;
else
retStatus.status = BZ_BT_RUNNING;
break;
case BZ_BT_DECOR_REPEAT:
BZ_ASSERT(nodeMatchesState(node, &retStatus.state));
BZ_ASSERT(nodeState);
nodeState->as.repeat.iter++;
if (nodeState->as.repeat.iter >= node->as.repeat.n) {
BZ_ASSERT(nodeState == execStatePopFront(&retStatus.state));
releaseNodeState(statePool, nodeState);
retStatus.status = inStatus.status;
break;
}
retStatus.status = BZ_BT_RUNNING;
break;
default:
break;
}
return retStatus;
}
static inline BzBTExecStatus bzBTExecuteNode(const BzBTNode *node, f32 dt,
BzBTExecState *nodeState, BzObjectPool *statePool) {
BzBTExecStatus status = { .status = BZ_BT_ERROR };
switch (node->type) {
case BZ_BT_COMP_SELECTOR:
case BZ_BT_COMP_SEQUENCE:
case BZ_BT_COMP_PARALLEL_SELECTOR:
case BZ_BT_COMP_PARALLEL_SEQUENCE:
//status = bzBTExecuteComposite(node, dt, nodeState, statePool);
break;
case BZ_BT_DECOR_DUMMY:
case BZ_BT_DECOR_SUCCESS:
case BZ_BT_DECOR_FAIL:
case BZ_BT_DECOR_INVERT:
case BZ_BT_DECOR_UNTIL_SUCCESS:
case BZ_BT_DECOR_UNTIL_FAIL:
case BZ_BT_DECOR_REPEAT:
case BZ_BT_DECOR_DELAY:
status = bzBTExecuteDecorator(node, dt, nodeState, statePool);
break;
case BZ_BT_ACTION:
BZ_ASSERT(node->as.action.fn);
status.status = node->as.action.fn(nodeState->userData, dt);
break;
}
return status;
}
BzBTStatus bzBTExecute(BzBTState *state, f32 dt) {
BZ_ASSERT(state->nodeStatePool);
BZ_ASSERT(bzObjectPoolGetObjectSize(state->nodeStatePool) == bzBTGetNodeStateSize());
BZ_ASSERT(state);
if (state->root == NULL) {
return BZ_BT_FAIL;
}
BzBTNodeState *first = state->_first;
const BzBTNode *firstNode = first ? first->node : state->root;
BzBTExecState execState = {
.first = state->_first,
.last = state->_last,
.userData = state->userData
};
BzBTExecStatus status = bzBTExecuteNode(firstNode, dt, &execState, state->nodeStatePool);
if (status.status == BZ_BT_ERROR)
execStateClear(&status.state, state->nodeStatePool);
BZ_ASSERT(execState.first == NULL && execState.last == NULL);
state->_first = status.state.first;
state->_last = status.state.last;
switch (status.status) {
case BZ_BT_SUCCESS:
state->onSuccess && state->onSuccess(state->userData, dt);
break;
case BZ_BT_FAIL:
state->onFailure && state->onFailure(state->userData, dt);
break;
case BZ_BT_ERROR:
state->onError && state->onError(state->userData, dt);
break;
default:
break;
}
return status.status;
}