Behaviour tree composites

This commit is contained in:
2024-01-09 20:53:21 +01:00
parent d7feba04db
commit e4cc00d3a7
2 changed files with 184 additions and 79 deletions

View File

@@ -35,6 +35,9 @@ struct BzAIBTNodeState {
BzAIBTNodeState *prev;
union {
struct {
BzAIBTNode *running;
} composite;
struct {
i32 iter;
} repeat;
@@ -164,32 +167,14 @@ void bzAIBTDestroyState(BzAIBTState *state) {
bzMemSet(state, 0, sizeof(*state));
}
void bzAIBTStatePush(BzAIBTState *state, BzAIBTNodeState *nodeState,
const BzAIBTNodeState *desc) {
BzAIBTNodeState *newState = bzObjectPool(state->nodeStatePool);
BZ_ASSERT(newState && desc);
*newState = *desc;
newState->next = NULL;
newState->prev = NULL;
if (nodeState == NULL)
nodeState = state->last;
if (nodeState) {
BzAIBTNodeState *next = nodeState->next;
nodeState->next = newState;
newState->prev = nodeState;
newState->next = next;
if (next)
next->prev = newState;
if (state->last == nodeState)
state->last = newState;
} else {
newState->prev = state->last;
state->last = newState;
if (state->first == NULL) state->first = newState;
}
void bzAIBTStateAppend(BzAIBTState *state, BzAIBTNodeState *nodeState) {
nodeState->next = NULL;
nodeState->prev = state->last;
if (state->last)
state->last->next = nodeState;
else
state->first = nodeState;
state->last = nodeState;
}
void bzAIBTStatePop(BzAIBTState *state, BzAIBTNodeState *nodeState) {
if (state->first == nodeState) state->first = nodeState->next;
@@ -200,74 +185,170 @@ void bzAIBTStatePop(BzAIBTState *state, BzAIBTNodeState *nodeState) {
nodeState->prev->next = next;
if (nodeState->next)
nodeState->next->prev = prev;
nodeState->next = NULL;
nodeState->prev = NULL;
}
void bzAIBTStateRenew(BzAIBTState *oldState, BzAIBTState *newState, BzAIBTNodeState *nodeState) {
// Pop nodeState and transfer it to the back
bzAIBTStatePop(oldState, nodeState);
bzAIBTStateAppend(newState, nodeState);
}
BzAIBTNodeState *bzAIBTStatePool(BzAIBTState *state, const BzAIBTNode *node) {
BzAIBTNodeState *nodeState = bzObjectPool(state->nodeStatePool);
nodeState->next = NULL;
nodeState->prev = NULL;
nodeState->node = node;
return nodeState;
}
void bzAIBTStateRelease(BzAIBTState *state, BzAIBTNodeState *nodeState) {
bzObjectPoolRelease(state->nodeStatePool, nodeState);
}
bool nodeMatchesState(const BzAIBTNode *node, const BzAIBTNodeState *state) {
return state && state->node == node;
}
BzAIBTNodeState *getNextNodeState(const BzAIBTNode *node, BzAIBTNodeState *nodeState) {
if (nodeState && nodeMatchesState(node, nodeState))
return nodeState->next;
return nodeState;
}
static inline BzAIBTStatus bzAIBTExecuteNode(const BzAIBTNode *node, f32 dt,
BzAIBTState *state, BzAIBTNodeState *nodeState);
/*
static inline BzAIBTStatus bzAIBTExecuteComposite(const BzObjectPool *nodePool, const BzAIBTNode *node,
BzAIBTState *state, BzAIBTNodeState *nodeState) {
BzAIBTNodeState *nodeState,
BzAIBTState *oldState, BzAIBTState *newState);
static inline BzAIBTStatus bzAIBTExecuteComposite(const BzAIBTNode *node, f32 dt,
BzAIBTNodeState *nodeState,
BzAIBTState *oldState, BzAIBTState *newState) {
BzAIBTNodeState *nextState = getNextNodeState(node, nodeState);
BzAIBTNode *start = node->first;
bool isParallel = node->type == BZ_AIBT_COMP_PARALLEL_SEQUENCE ||
node->type == BZ_AIBT_COMP_PARALLEL_SELECTOR;
if (!isParallel && nodeMatchesState(node, nodeState))
start = nodeState->as.composite.running;
// Always push dummy state
if (nodeMatchesState(node, nodeState)) {
bzAIBTStateRenew(oldState, newState, nodeState);
} else {
nodeState = bzAIBTStatePool(oldState, node);
bzAIBTStateAppend(newState, nodeState);
}
i32 numRunning = 0;
i32 numSuccessful = 0;
i32 numFailed = 0;
i32 numChildren = 0;
BzAIBTStatus status = BZ_AIBT_ERROR;
BzAIBTNode *child = start;
for (;child; child = child->next) {
BzAIBTStatus childStatus = bzAIBTExecuteNode(child, dt, nextState, oldState, newState);
numChildren++;
switch (childStatus) {
case BZ_AIBT_RUNNING:
numRunning++;
break;
case BZ_AIBT_SUCCESS:
numSuccessful++;
break;
case BZ_AIBT_FAIL:
numFailed++;
break;
default:
break;
}
switch (node->type) {
case BZ_AIBT_COMP_SELECTOR:
case BZ_AIBT_COMP_PARALLEL_SELECTOR:
if (childStatus == BZ_AIBT_SUCCESS)
status = BZ_AIBT_SUCCESS;
break;
case BZ_AIBT_COMP_SEQUENCE:
case BZ_AIBT_COMP_PARALLEL_SEQUENCE:
if (childStatus == BZ_AIBT_FAIL)
status = BZ_AIBT_FAIL;
break;
default:
break;
}
if (status == BZ_AIBT_FAIL || status == BZ_AIBT_SUCCESS)
break;
if (numRunning > 0 && !isParallel) {
status = BZ_AIBT_RUNNING;
break;
}
}
switch (node->type) {
case BZ_AIBT_COMP_SELECTOR:
for (BzAIBTNode *child = node->first; child; child = child->next) {
BzAIBTStatus status = bzAIBTExecuteNode(bt, child);
if (status == BZ_AIBT_SUCCESS) return status;
if (status == BZ_AIBT_RUNNING) return status;
}
return BZ_AIBT_FAIL;
case BZ_AIBT_COMP_PARALLEL_SELECTOR:
if (numFailed == numChildren)
status = BZ_AIBT_FAIL;
break;
case BZ_AIBT_COMP_SEQUENCE:
for (BzAIBTNode *child = node->first; child; child = child->next) {
BzAIBTStatus status = bzAIBTExecuteNode(bt, child);
if (status == BZ_AIBT_FAIL) return status;
if (status == BZ_AIBT_RUNNING) return status;
}
return BZ_AIBT_SUCCESS;
case BZ_AIBT_COMP_PARALLEL_SEQUENCE:
if (numSuccessful == numChildren)
status = BZ_AIBT_SUCCESS;
break;
default:
assert(false);
return BZ_AIBT_ERROR;
break;
}
if (status == BZ_AIBT_ERROR) {
bzAIBTStatePop(newState, nodeState);
return BZ_AIBT_ERROR;
}
bool finished = status == BZ_AIBT_SUCCESS ||
status == BZ_AIBT_FAIL;
if (finished) {
// Dummy state is no longer needed
bzAIBTStatePop(newState, nodeState);
} else {
BZ_ASSERT(status == BZ_AIBT_RUNNING);
nodeState->as.composite.running = child;
}
return status;
}
*/
static inline BzAIBTStatus bzAIBTExecuteDecorator(const BzAIBTNode *node, f32 dt,
BzAIBTState *state, BzAIBTNodeState *nodeState) {
// Ensure decorator has only one child
BZ_ASSERT(node->first && node->first == node->last);
BzAIBTNodeState *first = nodeState;
if (nodeState && first->node == node) {
first = first->next;
}
BzAIBTNodeState *nodeState,
BzAIBTState *oldState, BzAIBTState *newState) {
// Ensure decorator has only one child, if any
BZ_ASSERT(!node->first || node->first == node->last);
BzAIBTNodeState *nextState = getNextNodeState(node, nodeState);
switch (node->type) {
case BZ_AIBT_DECOR_REPEAT:
if (!nodeState || nodeState->node != node) {
bzAIBTStatePush(state, nodeState, &(BzAIBTNodeState) {
.node = node,
.as.repeat.iter = 0
});
if (!nodeMatchesState(node, nodeState)) {
BzAIBTNodeState *newNodeState = bzAIBTStatePool(oldState, node);
newNodeState->as.repeat.iter = 0;
bzAIBTStateAppend(newState, newNodeState);
nodeState = newNodeState;
} else {
bzAIBTStateRenew(oldState, newState, nodeState);
}
break;
case BZ_AIBT_DECOR_DELAY:
if (!nodeState || nodeState->node != node) {
bzAIBTStatePush(state, nodeState, &(BzAIBTNodeState) {
.node = node,
.as.delay = {0.2f}
});
if (!nodeMatchesState(node, nodeState)) {
BzAIBTNodeState *newNodeState = bzAIBTStatePool(oldState, node);
newNodeState->as.delay.elapsed = dt;
bzAIBTStateAppend(newState, newNodeState);
return BZ_AIBT_RUNNING;
}
nodeState->as.delay.elapsed += 0.2f;
nodeState->as.delay.elapsed += dt;
if (nodeState->as.delay.elapsed < node->as.delay.ms) {
bzAIBTStateRenew(oldState, newState, nodeState);
return BZ_AIBT_RUNNING;
}
bzAIBTStatePop(state, nodeState);
break;
default:
break;
}
BzAIBTStatus inStatus = bzAIBTExecuteNode(node->first, dt, state, first);
// Implicit success, if no children are present
BzAIBTStatus inStatus = BZ_AIBT_SUCCESS;
if (node->first)
inStatus = bzAIBTExecuteNode(node->first, dt, nextState, oldState, newState);
// ERROR, RUNNING are propagated up
// Propagate ERROR, RUNNING up
if (inStatus == BZ_AIBT_ERROR)
return BZ_AIBT_ERROR;
if (inStatus == BZ_AIBT_RUNNING)
@@ -286,8 +367,10 @@ static inline BzAIBTStatus bzAIBTExecuteDecorator(const BzAIBTNode *node, f32 dt
status = BZ_AIBT_FAIL;
break;
case BZ_AIBT_DECOR_INVERT:
if (inStatus == BZ_AIBT_FAIL) status = BZ_AIBT_SUCCESS;
if (inStatus == BZ_AIBT_SUCCESS) status = BZ_AIBT_FAIL;
if (inStatus == BZ_AIBT_FAIL)
status = BZ_AIBT_SUCCESS;
if (inStatus == BZ_AIBT_SUCCESS)
status = BZ_AIBT_FAIL;
break;
case BZ_AIBT_DECOR_UNTIL_SUCCESS:
if (inStatus == BZ_AIBT_SUCCESS)
@@ -305,7 +388,7 @@ static inline BzAIBTStatus bzAIBTExecuteDecorator(const BzAIBTNode *node, f32 dt
BZ_ASSERT(nodeState->node == node);
nodeState->as.repeat.iter++;
if (nodeState->as.repeat.iter >= node->as.repeat.n) {
bzAIBTStatePop(state, nodeState);
bzAIBTStatePop(newState, nodeState);
status = inStatus;
break;
}
@@ -317,14 +400,15 @@ static inline BzAIBTStatus bzAIBTExecuteDecorator(const BzAIBTNode *node, f32 dt
return status;
}
static inline BzAIBTStatus bzAIBTExecuteNode(const BzAIBTNode *node, f32 dt,
BzAIBTState *state, BzAIBTNodeState *nodeState) {
BzAIBTNodeState *nodeState,
BzAIBTState *oldState, BzAIBTState *newState) {
BzAIBTStatus status = BZ_AIBT_ERROR;
switch (node->type) {
case BZ_AIBT_COMP_SELECTOR:
case BZ_AIBT_COMP_SEQUENCE:
case BZ_AIBT_COMP_PARALLEL_SELECTOR:
case BZ_AIBT_COMP_PARALLEL_SEQUENCE:
//status = bzAIBTExecuteComposite(bt, node, state, nodeState);
status = bzAIBTExecuteComposite(node, dt, nodeState, oldState, newState);
break;
case BZ_AIBT_DECOR_DUMMY:
case BZ_AIBT_DECOR_SUCCESS:
@@ -334,11 +418,11 @@ static inline BzAIBTStatus bzAIBTExecuteNode(const BzAIBTNode *node, f32 dt,
case BZ_AIBT_DECOR_UNTIL_FAIL:
case BZ_AIBT_DECOR_REPEAT:
case BZ_AIBT_DECOR_DELAY:
status = bzAIBTExecuteDecorator(node, dt, state, nodeState);
status = bzAIBTExecuteDecorator(node, dt, nodeState, oldState, newState);
break;
case BZ_AIBT_ACTION:
BZ_ASSERT(node->as.action.fn);
return node->as.action.fn(state->userData);
return node->as.action.fn(oldState->userData);
}
return status;
}
@@ -348,8 +432,24 @@ BzAIBTStatus bzAIBTExecute(BzAIBTState *state, f32 dt) {
BZ_ASSERT(bzObjectPoolGetObjectSize(state->nodeStatePool) == bzAIBTGetNodeStateSize());
BZ_ASSERT(state);
BZ_ASSERT(state->root);
BzAIBTState newState = {
.first = NULL,
.last = NULL,
};
BzAIBTNodeState *first = state->first;
const BzAIBTNode *firstNode = first ? first->node : state->root;
BzAIBTStatus status = bzAIBTExecuteNode(firstNode, dt, state, first);
BzAIBTStatus status = bzAIBTExecuteNode(firstNode, dt, first, state, &newState);
// Release leftover states
BzAIBTNodeState *pState = state->first;
while (pState) {
BzAIBTNodeState *next = pState->next;
bzAIBTStateRelease(state, pState);
pState = next;
}
state->first = newState.first;
state->last = newState.last;
return status;
}

View File

@@ -8,7 +8,7 @@ BzAIBTNode *printBT = NULL;
BzAIBTStatus printAction(void *data) {
bzLogInfo("Hello, world!");
return BZ_AIBT_FAIL;
return BZ_AIBT_SUCCESS;
}
bool init(int *game) {
@@ -20,12 +20,16 @@ bool init(int *game) {
});
// for 1..5:
// delay 1s
// print "Hello, world!"
// seq
// delay 1s
// print "Hello, world!"
printBT = bzAIBTMakeRoot(nodePool);
BzAIBTNode *node = bzAIBTDecorRepeat(nodePool, printBT, 5);
node = bzAIBTDecorDelay(nodePool, node, 1.0f);
bzAIBTAction(nodePool, node, printAction);
BzAIBTNode *seq = bzAIBTCompSequence(nodePool, node, false);
bzAIBTDecorDelay(nodePool, seq, 1.0f);
bzAIBTAction(nodePool, seq, printAction);
BzAIBTState state = bzAIBTCreateState(&(BzAIBTStateDesc) {
.root = printBT,
@@ -39,6 +43,7 @@ bool init(int *game) {
while (status == BZ_AIBT_RUNNING) {
status = bzAIBTExecute(&state, 0.2f);
count++;
assert(status != BZ_AIBT_ERROR);
}
bzLogInfo("Iter: %d", count);