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; BzAIBTNodeState *prev;
union { union {
struct {
BzAIBTNode *running;
} composite;
struct { struct {
i32 iter; i32 iter;
} repeat; } repeat;
@@ -164,32 +167,14 @@ void bzAIBTDestroyState(BzAIBTState *state) {
bzMemSet(state, 0, sizeof(*state)); bzMemSet(state, 0, sizeof(*state));
} }
void bzAIBTStatePush(BzAIBTState *state, BzAIBTNodeState *nodeState, void bzAIBTStateAppend(BzAIBTState *state, BzAIBTNodeState *nodeState) {
const BzAIBTNodeState *desc) { nodeState->next = NULL;
BzAIBTNodeState *newState = bzObjectPool(state->nodeStatePool); nodeState->prev = state->last;
BZ_ASSERT(newState && desc); if (state->last)
*newState = *desc; state->last->next = nodeState;
else
newState->next = NULL; state->first = nodeState;
newState->prev = NULL; state->last = nodeState;
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 bzAIBTStatePop(BzAIBTState *state, BzAIBTNodeState *nodeState) { void bzAIBTStatePop(BzAIBTState *state, BzAIBTNodeState *nodeState) {
if (state->first == nodeState) state->first = nodeState->next; if (state->first == nodeState) state->first = nodeState->next;
@@ -200,74 +185,170 @@ void bzAIBTStatePop(BzAIBTState *state, BzAIBTNodeState *nodeState) {
nodeState->prev->next = next; nodeState->prev->next = next;
if (nodeState->next) if (nodeState->next)
nodeState->next->prev = prev; 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); 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, static inline BzAIBTStatus bzAIBTExecuteNode(const BzAIBTNode *node, f32 dt,
BzAIBTState *state, BzAIBTNodeState *nodeState); BzAIBTNodeState *nodeState,
/* BzAIBTState *oldState, BzAIBTState *newState);
static inline BzAIBTStatus bzAIBTExecuteComposite(const BzObjectPool *nodePool, const BzAIBTNode *node, static inline BzAIBTStatus bzAIBTExecuteComposite(const BzAIBTNode *node, f32 dt,
BzAIBTState *state, BzAIBTNodeState *nodeState) { 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) { switch (node->type) {
case BZ_AIBT_COMP_SELECTOR: case BZ_AIBT_COMP_SELECTOR:
for (BzAIBTNode *child = node->first; child; child = child->next) { case BZ_AIBT_COMP_PARALLEL_SELECTOR:
BzAIBTStatus status = bzAIBTExecuteNode(bt, child); if (numFailed == numChildren)
if (status == BZ_AIBT_SUCCESS) return status; status = BZ_AIBT_FAIL;
if (status == BZ_AIBT_RUNNING) return status; break;
}
return BZ_AIBT_FAIL;
case BZ_AIBT_COMP_SEQUENCE: case BZ_AIBT_COMP_SEQUENCE:
for (BzAIBTNode *child = node->first; child; child = child->next) { case BZ_AIBT_COMP_PARALLEL_SEQUENCE:
BzAIBTStatus status = bzAIBTExecuteNode(bt, child); if (numSuccessful == numChildren)
if (status == BZ_AIBT_FAIL) return status; status = BZ_AIBT_SUCCESS;
if (status == BZ_AIBT_RUNNING) return status; break;
}
return BZ_AIBT_SUCCESS;
default: default:
assert(false); break;
return BZ_AIBT_ERROR;
} }
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, static inline BzAIBTStatus bzAIBTExecuteDecorator(const BzAIBTNode *node, f32 dt,
BzAIBTState *state, BzAIBTNodeState *nodeState) { BzAIBTNodeState *nodeState,
// Ensure decorator has only one child BzAIBTState *oldState, BzAIBTState *newState) {
BZ_ASSERT(node->first && node->first == node->last); // Ensure decorator has only one child, if any
BzAIBTNodeState *first = nodeState; BZ_ASSERT(!node->first || node->first == node->last);
if (nodeState && first->node == node) { BzAIBTNodeState *nextState = getNextNodeState(node, nodeState);
first = first->next;
}
switch (node->type) { switch (node->type) {
case BZ_AIBT_DECOR_REPEAT: case BZ_AIBT_DECOR_REPEAT:
if (!nodeState || nodeState->node != node) { if (!nodeMatchesState(node, nodeState)) {
bzAIBTStatePush(state, nodeState, &(BzAIBTNodeState) { BzAIBTNodeState *newNodeState = bzAIBTStatePool(oldState, node);
.node = node, newNodeState->as.repeat.iter = 0;
.as.repeat.iter = 0 bzAIBTStateAppend(newState, newNodeState);
}); nodeState = newNodeState;
} else {
bzAIBTStateRenew(oldState, newState, nodeState);
} }
break; break;
case BZ_AIBT_DECOR_DELAY: case BZ_AIBT_DECOR_DELAY:
if (!nodeState || nodeState->node != node) { if (!nodeMatchesState(node, nodeState)) {
bzAIBTStatePush(state, nodeState, &(BzAIBTNodeState) { BzAIBTNodeState *newNodeState = bzAIBTStatePool(oldState, node);
.node = node, newNodeState->as.delay.elapsed = dt;
.as.delay = {0.2f} bzAIBTStateAppend(newState, newNodeState);
});
return BZ_AIBT_RUNNING; return BZ_AIBT_RUNNING;
} }
nodeState->as.delay.elapsed += 0.2f; nodeState->as.delay.elapsed += dt;
if (nodeState->as.delay.elapsed < node->as.delay.ms) { if (nodeState->as.delay.elapsed < node->as.delay.ms) {
bzAIBTStateRenew(oldState, newState, nodeState);
return BZ_AIBT_RUNNING; return BZ_AIBT_RUNNING;
} }
bzAIBTStatePop(state, nodeState);
break; break;
default: default:
break; 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) if (inStatus == BZ_AIBT_ERROR)
return BZ_AIBT_ERROR; return BZ_AIBT_ERROR;
if (inStatus == BZ_AIBT_RUNNING) if (inStatus == BZ_AIBT_RUNNING)
@@ -286,8 +367,10 @@ static inline BzAIBTStatus bzAIBTExecuteDecorator(const BzAIBTNode *node, f32 dt
status = BZ_AIBT_FAIL; status = BZ_AIBT_FAIL;
break; break;
case BZ_AIBT_DECOR_INVERT: case BZ_AIBT_DECOR_INVERT:
if (inStatus == BZ_AIBT_FAIL) status = BZ_AIBT_SUCCESS; if (inStatus == BZ_AIBT_FAIL)
if (inStatus == BZ_AIBT_SUCCESS) status = BZ_AIBT_FAIL; status = BZ_AIBT_SUCCESS;
if (inStatus == BZ_AIBT_SUCCESS)
status = BZ_AIBT_FAIL;
break; break;
case BZ_AIBT_DECOR_UNTIL_SUCCESS: case BZ_AIBT_DECOR_UNTIL_SUCCESS:
if (inStatus == BZ_AIBT_SUCCESS) if (inStatus == BZ_AIBT_SUCCESS)
@@ -305,7 +388,7 @@ static inline BzAIBTStatus bzAIBTExecuteDecorator(const BzAIBTNode *node, f32 dt
BZ_ASSERT(nodeState->node == node); BZ_ASSERT(nodeState->node == node);
nodeState->as.repeat.iter++; nodeState->as.repeat.iter++;
if (nodeState->as.repeat.iter >= node->as.repeat.n) { if (nodeState->as.repeat.iter >= node->as.repeat.n) {
bzAIBTStatePop(state, nodeState); bzAIBTStatePop(newState, nodeState);
status = inStatus; status = inStatus;
break; break;
} }
@@ -317,14 +400,15 @@ static inline BzAIBTStatus bzAIBTExecuteDecorator(const BzAIBTNode *node, f32 dt
return status; return status;
} }
static inline BzAIBTStatus bzAIBTExecuteNode(const BzAIBTNode *node, f32 dt, static inline BzAIBTStatus bzAIBTExecuteNode(const BzAIBTNode *node, f32 dt,
BzAIBTState *state, BzAIBTNodeState *nodeState) { BzAIBTNodeState *nodeState,
BzAIBTState *oldState, BzAIBTState *newState) {
BzAIBTStatus status = BZ_AIBT_ERROR; BzAIBTStatus status = BZ_AIBT_ERROR;
switch (node->type) { switch (node->type) {
case BZ_AIBT_COMP_SELECTOR: case BZ_AIBT_COMP_SELECTOR:
case BZ_AIBT_COMP_SEQUENCE: case BZ_AIBT_COMP_SEQUENCE:
case BZ_AIBT_COMP_PARALLEL_SELECTOR: case BZ_AIBT_COMP_PARALLEL_SELECTOR:
case BZ_AIBT_COMP_PARALLEL_SEQUENCE: case BZ_AIBT_COMP_PARALLEL_SEQUENCE:
//status = bzAIBTExecuteComposite(bt, node, state, nodeState); status = bzAIBTExecuteComposite(node, dt, nodeState, oldState, newState);
break; break;
case BZ_AIBT_DECOR_DUMMY: case BZ_AIBT_DECOR_DUMMY:
case BZ_AIBT_DECOR_SUCCESS: 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_UNTIL_FAIL:
case BZ_AIBT_DECOR_REPEAT: case BZ_AIBT_DECOR_REPEAT:
case BZ_AIBT_DECOR_DELAY: case BZ_AIBT_DECOR_DELAY:
status = bzAIBTExecuteDecorator(node, dt, state, nodeState); status = bzAIBTExecuteDecorator(node, dt, nodeState, oldState, newState);
break; break;
case BZ_AIBT_ACTION: case BZ_AIBT_ACTION:
BZ_ASSERT(node->as.action.fn); BZ_ASSERT(node->as.action.fn);
return node->as.action.fn(state->userData); return node->as.action.fn(oldState->userData);
} }
return status; return status;
} }
@@ -348,8 +432,24 @@ BzAIBTStatus bzAIBTExecute(BzAIBTState *state, f32 dt) {
BZ_ASSERT(bzObjectPoolGetObjectSize(state->nodeStatePool) == bzAIBTGetNodeStateSize()); BZ_ASSERT(bzObjectPoolGetObjectSize(state->nodeStatePool) == bzAIBTGetNodeStateSize());
BZ_ASSERT(state); BZ_ASSERT(state);
BZ_ASSERT(state->root); BZ_ASSERT(state->root);
BzAIBTState newState = {
.first = NULL,
.last = NULL,
};
BzAIBTNodeState *first = state->first; BzAIBTNodeState *first = state->first;
const BzAIBTNode *firstNode = first ? first->node : state->root; 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; return status;
} }

View File

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