// ============================================================
// Runtime lifecycle helpers
//
// TreeRunner is the small integration boundary recommended for applications.
// It owns one tree and one blackboard, exposes explicit reset semantics, and
// keeps the frame counter needed by game loops, services, and telemetry.
// ============================================================
///|
pub struct TreeRunner {
root : Node
bb : Blackboard
frame : Ref[Int]
last : Ref[Status?]
}
///|
/// Create a runner with an isolated blackboard.
pub fn TreeRunner::new(root : Node, bb : Blackboard) -> TreeRunner {
{ root, bb, frame: Ref::new(0), last: Ref::new(None) }
}
///|
/// Tick one frame and record the last observed status.
pub fn TreeRunner::tick(self : TreeRunner) -> Status {
let status = self.root.tick(self.bb)
self.frame.set(self.frame.get() + 1)
self.last.set(Some(status))
status
}
///|
/// Run at most `max_ticks` frames, returning early on a terminal status.
pub fn TreeRunner::run_until_terminal(
self : TreeRunner,
max_ticks : Int,
) -> Status {
let mut i = 0
let mut status = self.last.get().unwrap_or(Status::BTRunning)
while i < max_ticks {
status = self.tick()
i = i + 1
match status {
Status::BTRunning => ()
_ => return status
}
}
status
}
///|
/// Reset the tree and frame-local lifecycle state.
pub fn TreeRunner::reset(self : TreeRunner) -> Unit {
self.root.reset()
self.frame.set(0)
self.last.set(None)
}
///|
/// Return the number of frames ticked since the last reset.
pub fn TreeRunner::frame(self : TreeRunner) -> Int {
self.frame.get()
}
///|
/// Return the most recent status, if the runner has been ticked.
pub fn TreeRunner::last_status(self : TreeRunner) -> Status? {
self.last.get()
}
///|
/// Return the runner's isolated blackboard.
pub fn TreeRunner::blackboard(self : TreeRunner) -> Blackboard {
self.bb
}
///|
/// Return whether the last tick completed.
pub fn TreeRunner::is_terminal(self : TreeRunner) -> Bool {
match self.last.get() {
Some(Status::BTSuccess | Status::BTFailure) => true
_ => false
}
}
///|
/// Tick a tree a fixed number of times. Useful for deterministic simulations.
pub fn tick_for_frames(
root : Node,
bb : Blackboard,
frames : Int,
) -> Array[Status] {
let results : Array[Status] = []
let mut i = 0
while i < frames {
results.push(root.tick(bb))
i = i + 1
}
results
}