1294.44 FallbackWithMemory의 의사 코드
1. 기본 의사 코드
FallbackWithMemory의 의사 코드는 current_child_idx를 시작 인덱스로 사용하여 자식을 순차 평가하고, RUNNING 자식의 인덱스를 기억하는 구조이다(Colledanchise & Ogren, 2018).
function FallbackWithMemory.tick():
for i ← current_child_idx to N-1:
status ← children[i].executeTick()
if status == SUCCESS:
haltChildren()
current_child_idx ← 0
return SUCCESS
else if status == RUNNING:
current_child_idx ← i
return RUNNING
// 모든 자식 FAILURE
current_child_idx ← 0
return FAILURE
function FallbackWithMemory.halt():
current_child_idx ← 0
haltChildren()
2. SequenceWithMemory 의사 코드와의 대칭적 비교
두 의사 코드를 나란히 배치하면, SUCCESS와 FAILURE의 역할 교환이 명확하게 드러난다.
// SequenceWithMemory // FallbackWithMemory
function tick(): function tick():
for i ← idx to N-1: for i ← idx to N-1:
s ← child[i].tick() s ← child[i].tick()
if s == FAILURE: if s == SUCCESS:
idx ← 0 idx ← 0
return FAILURE return SUCCESS
if s == RUNNING: if s == RUNNING:
idx ← i idx ← i
return RUNNING return RUNNING
idx ← 0 idx ← 0
return SUCCESS return FAILURE
구조가 완전히 동일하며, FAILURE와 SUCCESS가 교환된 것만이 유일한 차이이다.
3. Halt 및 상태 초기화를 포함한 확장 의사 코드
function FallbackWithMemory.tick():
for i ← current_child_idx to N-1:
status ← children[i].executeTick()
switch status:
case SUCCESS:
// 대안 탐색 성공 — 모든 자식 초기화
resetAllChildren()
current_child_idx ← 0
return SUCCESS
case RUNNING:
// 현재 대안 진행 중 — 인덱스 기억
current_child_idx ← i
return RUNNING
case FAILURE:
// 현재 대안 실패 — 다음 대안으로 진행
continue
case IDLE:
throw LogicError // 프로토콜 위반
// 모든 대안 소진
resetAllChildren()
current_child_idx ← 0
return FAILURE
function FallbackWithMemory.halt():
current_child_idx ← 0
for i ← 0 to N-1:
if children[i].status() != IDLE:
children[i].halt()
children[i].setStatus(IDLE)
4. SKIPPED 상태를 포함한 BehaviorTree.CPP v4 의사 코드
BehaviorTree.CPP v4에서는 SKIPPED 상태 처리가 추가된다(Faconti, 2022).
function FallbackNode.tick():
if status() == IDLE:
skipped_count ← 0
setStatus(RUNNING)
while current_child_idx < N:
child ← children[current_child_idx]
child_status ← child.executeTick()
switch child_status:
case RUNNING:
return RUNNING
case SUCCESS:
resetChildren()
current_child_idx ← 0
return SUCCESS
case FAILURE:
current_child_idx ← current_child_idx + 1
case SKIPPED:
current_child_idx ← current_child_idx + 1
skipped_count ← skipped_count + 1
case IDLE:
throw LogicError("Child returned IDLE")
// 루프 종료: 모든 자식 평가 완료
current_child_idx ← 0
resetChildren()
if skipped_count == N:
return SKIPPED
return FAILURE
4.1 SKIPPED 처리 규칙
- SKIPPED를 반환한 자식은 전조건(precondition)에 의해 실행이 건너뛰어진 것이다.
- SKIPPED는 FAILURE와 동일하게 다음 자식으로 진행한다.
- 모든 자식이 SKIPPED인 경우, Fallback도 SKIPPED를 반환한다.
- 일부만 SKIPPED이고 나머지가 FAILURE인 경우, FAILURE를 반환한다.
5. 의사 코드의 실행 추적
5.1 추적 1: 첫 번째 대안 즉시 성공
children: [ActA, ActB, ActC]
current_child_idx = 0
tick():
i=0: ActA.executeTick() → SUCCESS
resetAllChildren()
current_child_idx ← 0
→ return SUCCESS
5.2 추적 2: 비동기 대안의 다중 Tick 진행
children: [CondA, AsyncActB, ActC]
Tick 1 (current_child_idx=0):
i=0: CondA → FAILURE → continue
i=1: AsyncActB → RUNNING
current_child_idx ← 1
→ return RUNNING
Tick 2 (current_child_idx=1):
i=1: AsyncActB → RUNNING
current_child_idx = 1 (유지)
→ return RUNNING
Tick 3 (current_child_idx=1):
i=1: AsyncActB → FAILURE → continue
i=2: ActC → SUCCESS
resetAllChildren()
current_child_idx ← 0
→ return SUCCESS
5.3 추적 3: 모든 대안 소진
children: [ActA, AsyncActB]
Tick 1 (current_child_idx=0):
i=0: ActA → FAILURE → continue
i=1: AsyncActB → RUNNING
current_child_idx ← 1
→ return RUNNING
Tick 2 (current_child_idx=1):
i=1: AsyncActB → FAILURE
루프 종료 (i=2 ≥ N=2)
resetAllChildren()
current_child_idx ← 0
→ return FAILURE
5.4 추적 4: SKIPPED가 포함된 경우
children: [ActA, ActB, ActC]
Tick 1 (current_child_idx=0):
i=0: ActA → SKIPPED (precondition 미충족)
skipped_count ← 1
current_child_idx ← 1
i=1: ActB → FAILURE
current_child_idx ← 2
i=2: ActC → SUCCESS
resetChildren()
current_child_idx ← 0
→ return SUCCESS
6. 의사 코드와 C++ 구현의 대응
| 의사 코드 | BehaviorTree.CPP v4 C++ |
|---|---|
for i ← current_child_idx to N-1 | while(current_child_idx_ < children_count) |
children[i].executeTick() | current_child->executeTick() |
current_child_idx ← i | current_child_idx_ 유지 (이미 해당 값) |
current_child_idx ← 0 | current_child_idx_ = 0 |
haltChildren() | resetChildren() |
skipped_count | skipped_count_ 멤버 변수 |
의사 코드의 for 루프와 실제 구현의 while 루프는 동일한 동작을 수행한다. while 루프에서는 current_child_idx_를 직접 증가시키는 방식으로 루프 진행을 제어한다.
참고 문헌
- Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
- Faconti, D. (2022). BehaviorTree.CPP documentation and API reference. https://www.behaviortree.dev/