Documentation
¶
Overview ¶
Package bt is a composable behavior tree library for Go.
Import: "github.com/ratlabs-io/bt-go" (package name bt).
A behavior tree is a hierarchy of Behavior nodes. Each tick, a node returns one of Success, Failure, or Running. Leaf nodes (Action, Condition) do work; composites (Sequence, Selector, Parallel, …) combine children; decorators (Inverter, Repeater, Named, Observing, AbortHook, …) wrap a single child.
Environment (Env) ¶
Nodes receive an Env on every Tick. Env is not a context.Context:
- env.Context() — stdlib context for cancellation/deadlines only
- blackboard via Set/Get/GetAs — mutable agent/world state
Put agent data on the blackboard, never in context.WithValue.
Abort (Halt) ¶
When a reactive parent preempts a Running child, or TreeRunner cancels, Halt is called on Haltable nodes so they can clean up (see AbortHook).
Index ¶
- func GetAs[T any](env Env, key string) (T, bool)
- func Halt(env Env, node Behavior)
- func HaltAll(env Env, nodes ...Behavior)
- func MustGet[T any](env Env, key string) T
- type AbortHook
- type Action
- type BaseDecorator
- type Behavior
- type BinarySelector
- type Blackboard
- func (bb *Blackboard) AllEntries() []string
- func (bb *Blackboard) Clear()
- func (bb *Blackboard) Delete(key string)
- func (bb *Blackboard) Entries() []string
- func (bb *Blackboard) Get(key string) (interface{}, bool)
- func (bb *Blackboard) Has(key string) bool
- func (bb *Blackboard) HasLocal(key string) bool
- func (bb *Blackboard) Set(key string, value interface{})
- type ChildrenProvider
- type Composite
- type Condition
- type Conditional
- type Decorator
- type Env
- type EnvOption
- type Haltable
- type Inverter
- type KeyFunc
- type MemorySelector
- type MemorySequence
- type Named
- type NodeVisualizer
- type Observing
- type Parallel
- type ParallelPolicy
- type Repeater
- type RunStatus
- type RunnerOption
- type Selector
- type Sequence
- type StatusRecorder
- type Switch
- type TickObserver
- type TreeRunner
- type TreeVisualizer
- type UntilFailure
- type UntilSuccess
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GetAs ¶
GetAs reads key from env's blackboard and type-asserts to T. ok is false if the key is missing or the value is not of type T.
Types ¶
type AbortHook ¶
type AbortHook struct {
BaseDecorator
OnAbort func(env Env)
// contains filtered or unexported fields
}
AbortHook runs a callback when the node is Halted while still considered active (last tick returned Running). Use it for cleanup when a reactive parent preempts a long-running branch (stop pathing, clear target, etc.).
func NewAbortHook ¶
NewAbortHook wraps child with an abort callback.
type Action ¶
type Action struct {
// contains filtered or unexported fields
}
Action is a leaf node that runs a user-supplied function.
type BaseDecorator ¶
type BaseDecorator struct {
Child Behavior
}
BaseDecorator holds the child pointer shared by concrete decorators.
func (*BaseDecorator) GetChild ¶
func (d *BaseDecorator) GetChild() Behavior
GetChild returns the decorated child.
func (*BaseDecorator) HaltChild ¶
func (d *BaseDecorator) HaltChild(env Env)
HaltChild aborts the child if it is Haltable. Embedded types may call this from Halt.
func (*BaseDecorator) SetChild ¶
func (d *BaseDecorator) SetChild(child Behavior)
SetChild sets the decorated child.
type Behavior ¶
type Behavior interface {
// Tick executes one step of the node and returns its status.
Tick(env Env) RunStatus
}
Behavior is implemented by every node in a behavior tree.
type BinarySelector ¶
BinarySelector chooses between two branches based on a condition behavior.
If Condition returns Success, IfTrue is ticked; otherwise IfFalse is ticked. Condition statuses other than Success (including Running) select IfFalse. For a boolean leaf, pass a *Condition.
func NewBinarySelector ¶
func NewBinarySelector(condition, ifTrue, ifFalse Behavior) *BinarySelector
NewBinarySelector creates a BinarySelector.
func (*BinarySelector) Tick ¶
func (node *BinarySelector) Tick(env Env) RunStatus
Tick evaluates Condition and runs the matching branch.
type Blackboard ¶
type Blackboard struct {
// contains filtered or unexported fields
}
Blackboard is a hierarchical, thread-safe key-value store for tree state.
Child blackboards can see parent entries (Get/Has walk upward). Writes and deletes apply only to the local blackboard, so a child can shadow a parent key without mutating it.
func NewBlackboard ¶
func NewBlackboard() *Blackboard
NewBlackboard creates an empty root blackboard.
func NewBlackboardWithParent ¶
func NewBlackboardWithParent(parent *Blackboard) *Blackboard
NewBlackboardWithParent creates a blackboard that falls back to parent on miss.
func (*Blackboard) AllEntries ¶
func (bb *Blackboard) AllEntries() []string
AllEntries returns keys visible from this blackboard, including parents. Local keys shadow parent keys of the same name (counted once).
func (*Blackboard) Clear ¶
func (bb *Blackboard) Clear()
Clear removes all local entries. Parents are unaffected.
func (*Blackboard) Delete ¶
func (bb *Blackboard) Delete(key string)
Delete removes a key from this blackboard only.
func (*Blackboard) Entries ¶
func (bb *Blackboard) Entries() []string
Entries returns keys set on this blackboard (not parents).
func (*Blackboard) Get ¶
func (bb *Blackboard) Get(key string) (interface{}, bool)
Get retrieves a value, checking this blackboard then parents.
func (*Blackboard) Has ¶
func (bb *Blackboard) Has(key string) bool
Has reports whether key exists on this blackboard or any parent.
func (*Blackboard) HasLocal ¶
func (bb *Blackboard) HasLocal(key string) bool
HasLocal reports whether key is set on this blackboard (ignoring parents).
func (*Blackboard) Set ¶
func (bb *Blackboard) Set(key string, value interface{})
Set stores a value on this blackboard only.
type ChildrenProvider ¶
type ChildrenProvider interface {
GetChildren() []Behavior
}
ChildrenProvider is implemented by nodes that expose multiple children. Used by tooling (visualization) without type-switching every composite.
type Composite ¶
type Composite struct {
Children []Behavior
}
Composite is embedded by multi-child control nodes. Children are public so callers and tools (e.g. visualizers) can inspect the tree.
func (*Composite) GetChildren ¶
GetChildren returns the composite's child nodes.
type Condition ¶
type Condition struct {
// contains filtered or unexported fields
}
Condition is a leaf that maps a boolean check to Success or Failure. It never returns Running.
func NewCondition ¶
NewCondition creates a Condition from checkFunc. If checkFunc is nil, Tick returns Failure.
type Conditional ¶
Conditional runs Action only when Condition succeeds. If the condition fails, Conditional returns Failure without ticking Action. Condition must be non-nil; a nil Action is treated as Failure when selected.
func NewConditional ¶
func NewConditional(condition *Condition, action Behavior) *Conditional
NewConditional creates a Conditional with the given condition and action.
func (*Conditional) Tick ¶
func (c *Conditional) Tick(env Env) RunStatus
Tick checks the condition, then optionally runs the action.
type Env ¶
type Env interface {
// Context returns the stdlib context used for cancellation and deadlines.
// It is never used as a value bag for agent state.
Context() context.Context
// Blackboard returns the hierarchical store for agent/world state.
Blackboard() *Blackboard
// Set stores a value on the blackboard (convenience for Blackboard().Set).
Set(key string, value interface{})
// Get reads a value from the blackboard hierarchy.
Get(key string) (value interface{}, ok bool)
// Delete removes a key from this blackboard only (not parents).
Delete(key string)
// Has reports whether key exists on this blackboard or any parent.
Has(key string) bool
}
Env is the per-tick environment passed to every node.
It is intentionally not a context.Context. The two concerns are separate:
- Context() — stdlib context.Context for cancellation and deadlines only
- Blackboard — mutable agent/world state (hierarchical key-value store)
Do not put agent state in context.WithValue. Use the blackboard (or the convenience Set/Get/Delete/Has methods, which write through to it).
Env does not implement context.Context (has-a, not is-a).
type EnvOption ¶
type EnvOption func(*env)
EnvOption configures an Env at construction time.
func WithBlackboard ¶
func WithBlackboard(bb *Blackboard) EnvOption
WithBlackboard sets the blackboard used by the env. If bb is nil, a fresh blackboard is used instead.
type Haltable ¶
type Haltable interface {
Halt(env Env)
}
Haltable is implemented by nodes that need cleanup when a parent aborts them without delivering a terminal Success/Failure tick (preemption, cancel, Reset).
Halt must be safe to call when the node is not running (no-op). Composites that track a running child should Halt that child and clear memory.
type Inverter ¶
type Inverter struct {
BaseDecorator
}
Inverter swaps Success ↔ Failure. Running is unchanged.
func NewInverter ¶
NewInverter creates an Inverter around child.
type MemorySelector ¶
type MemorySelector struct {
Composite
// contains filtered or unexported fields
}
MemorySelector is a Selector that remembers which child was Running.
Unlike the reactive Selector (NewSelector), once a child returns Running the next Tick resumes at that child and does not re-evaluate higher-priority (earlier) siblings until the remembered child finishes.
If the remembered child fails, evaluation continues with later siblings only (earlier ones are not re-tried until the whole selector returns Failure and memory is cleared). Call Reset to clear memory without ticking; Halt aborts.
func NewMemorySelector ¶
func NewMemorySelector(children ...Behavior) *MemorySelector
NewMemorySelector creates a memory Selector with the given children.
func (*MemorySelector) Halt ¶
func (s *MemorySelector) Halt(env Env)
Halt aborts the remembered child (if any) and clears memory.
func (*MemorySelector) Reset ¶
func (s *MemorySelector) Reset()
Reset clears resume state so the next Tick starts at the first child. It does not Halt the current child; call Halt when aborting mid-run.
func (*MemorySelector) RunningIndex ¶
func (s *MemorySelector) RunningIndex() int
RunningIndex returns the remembered child index, or -1 when idle.
func (*MemorySelector) Tick ¶
func (s *MemorySelector) Tick(env Env) RunStatus
Tick tries children from the remembered index. See type docs.
type MemorySequence ¶
type MemorySequence struct {
Composite
// contains filtered or unexported fields
}
MemorySequence is a Sequence that remembers which child was Running.
Unlike the reactive Sequence (NewSequence), once a child returns Running the next Tick resumes at that child and does not re-tick earlier siblings that already succeeded in this run.
Memory is cleared when the sequence returns Success or Failure (including nil-child failure). Call Reset to clear memory without ticking; Halt aborts the current child and resets.
func NewMemorySequence ¶
func NewMemorySequence(children ...Behavior) *MemorySequence
NewMemorySequence creates a memory Sequence with the given children.
func (*MemorySequence) Halt ¶
func (s *MemorySequence) Halt(env Env)
Halt aborts the child at the resume index (if any) and resets memory.
func (*MemorySequence) Reset ¶
func (s *MemorySequence) Reset()
Reset clears resume state so the next Tick starts at the first child. It does not Halt the current child; call Halt when aborting mid-run.
func (*MemorySequence) RunningIndex ¶
func (s *MemorySequence) RunningIndex() int
RunningIndex returns the child index that will be resumed on the next Tick (0 when idle / after a terminal status).
func (*MemorySequence) Tick ¶
func (s *MemorySequence) Tick(env Env) RunStatus
Tick executes children from the remembered index. See type docs.
type Named ¶
type Named struct {
BaseDecorator
Name string
}
Named wraps a child with a human-readable label for visualization and logs. Tick is a pure pass-through.
func (*Named) VisualizeNode ¶
VisualizeNode returns the custom name for TreeVisualizer.
type NodeVisualizer ¶
type NodeVisualizer interface {
VisualizeNode() string
}
NodeVisualizer lets custom nodes supply their own label for tree dumps.
type Observing ¶
type Observing struct {
BaseDecorator
// AfterTick is invoked after the child is ticked (including when child is nil → Failure).
AfterTick TickObserver
}
Observing wraps a child and reports each tick result to AfterTick. Only this node is observed — wrap additional nodes to observe deeper.
func NewObserving ¶
func NewObserving(child Behavior, after TickObserver) *Observing
NewObserving creates an Observing decorator. after may be nil (no-op).
type Parallel ¶
type Parallel struct {
Composite
// contains filtered or unexported fields
}
Parallel ticks all children each call and aggregates results by policy.
By default ticks are sequential (deterministic order, same stack) — the usual behavior-tree meaning of “parallel”: all children get a chance this frame. Use NewConcurrentParallel for one goroutine per child.
Children share the same Env. Under concurrent mode the blackboard is thread-safe; other shared mutable state in actions must be synchronized.
Every child is ticked on every Parallel.Tick (no sticky completion memory).
func NewConcurrentParallel ¶
func NewConcurrentParallel(policy ParallelPolicy, children ...Behavior) *Parallel
NewConcurrentParallel creates a Parallel that ticks each child in its own goroutine.
func NewParallel ¶
func NewParallel(policy ParallelPolicy, children ...Behavior) *Parallel
NewParallel creates a sequential Parallel node (classic BT parallel).
func (*Parallel) Concurrent ¶
Concurrent reports whether children are ticked in goroutines.
func (*Parallel) Policy ¶
func (p *Parallel) Policy() ParallelPolicy
Policy returns the aggregation policy.
type ParallelPolicy ¶
type ParallelPolicy int
ParallelPolicy defines how a Parallel node aggregates child results.
const ( // RequireOne succeeds if at least one child succeeds; fails only if all fail. RequireOne ParallelPolicy = iota // RequireAll succeeds only if all children succeed; fails if any fail. RequireAll // SuccessOnAll succeeds only if all children succeed; otherwise Running // (never Failure — failures are treated as “not yet success”). SuccessOnAll // SuccessOnOne succeeds if at least one child succeeds; otherwise Running // (never Failure). SuccessOnOne )
func (ParallelPolicy) String ¶
func (p ParallelPolicy) String() string
String returns the policy name.
type Repeater ¶
type Repeater struct {
BaseDecorator
Count int
// contains filtered or unexported fields
}
Repeater runs its child a fixed number of successful times. Each Tick advances at most one successful child completion. Failure from the child aborts and resets the counter. count <= 0 means infinite (always returns Running after a successful child tick).
func NewRepeater ¶
NewRepeater creates a Repeater. count <= 0 means repeat forever.
type RunnerOption ¶
type RunnerOption func(*TreeRunner)
RunnerOption configures a TreeRunner.
func WithCallbacks ¶
func WithCallbacks(onSuccess, onFailure, onRunning func()) RunnerOption
WithCallbacks registers hooks invoked after each tick for the returned status. Nil callbacks are treated as no-ops.
func WithTickRate ¶
func WithTickRate(rate time.Duration) RunnerOption
WithTickRate sets how often the tree is ticked. Default is 100ms (10 Hz).
type Selector ¶
type Selector struct {
Composite
// contains filtered or unexported fields
}
Selector ticks children left-to-right until one succeeds or is still running.
Semantics (reactive / restart-from-start each tick — classic priority order):
- Success from any child → Success
- Running from any child → Running
- All Failure → Failure
Earlier children have higher priority: every Tick re-evaluates from the first child, so a higher-priority branch can preempt a lower one that was Running. When preemption occurs, the abandoned child is Halted if it implements Haltable. For stick-to-running-child semantics (no preemption), use NewMemorySelector.
func NewSelector ¶
NewSelector creates a Selector with the given children, in priority order.
type Sequence ¶
type Sequence struct {
Composite
// contains filtered or unexported fields
}
Sequence ticks children left-to-right until one fails or is still running.
Semantics (reactive / restart-from-start each tick):
- Failure from any child → Failure
- Running from any child → Running
- All Success → Success
Sequence does not remember progress for control flow; each Tick starts at the first child. It does track the last Running child so that if an earlier sibling later fails (or the sequence is Halted), the abandoned child receives Halt. For resume-from-running control flow, use NewMemorySequence.
func NewSequence ¶
NewSequence creates a Sequence with the given children, in order.
type StatusRecorder ¶
type StatusRecorder struct {
// contains filtered or unexported fields
}
StatusRecorder records the status of nodes as they are ticked. It is a debugging aid, not a tree node itself.
Prefer wrapping ticks you care about:
rec := bt.NewStatusRecorder() status := rec.Tick(env, root) // records root only
For full-tree status maps, wrap individual leaves or use a custom decorator.
func NewSaveTreeSnapshot
deprecated
func NewSaveTreeSnapshot() *StatusRecorder
NewSaveTreeSnapshot is a deprecated alias for NewStatusRecorder.
Deprecated: use NewStatusRecorder.
func NewStatusRecorder ¶
func NewStatusRecorder() *StatusRecorder
NewStatusRecorder creates an empty status recorder.
func (*StatusRecorder) GetStatusMap ¶
func (s *StatusRecorder) GetStatusMap() map[Behavior]RunStatus
GetStatusMap returns the map of recorded node statuses.
func (*StatusRecorder) Tick ¶
func (s *StatusRecorder) Tick(env Env, node Behavior) RunStatus
Tick runs node, records its status, and returns that status. Only the node itself is recorded — not its descendants.
func (*StatusRecorder) Visualize ¶
func (s *StatusRecorder) Visualize(root Behavior) string
Visualize renders root with recorded statuses annotated.
type Switch ¶
Switch selects a child by a dynamic string key.
KeyFunc is evaluated every Tick. If Cases[key] exists it is ticked; otherwise Default is ticked. If neither matches, Switch returns Failure.
type TickObserver ¶
TickObserver is called after a watched node finishes a Tick.
type TreeRunner ¶
type TreeRunner struct {
// contains filtered or unexported fields
}
TreeRunner ticks a behavior tree on a fixed interval until cancelled.
func NewTreeRunner ¶
func NewTreeRunner(tree Behavior, options ...RunnerOption) *TreeRunner
NewTreeRunner returns a runner for tree with optional configuration.
func (*TreeRunner) Run ¶
func (tr *TreeRunner) Run(env Env)
Run ticks the tree at the configured rate until env.Context() is cancelled.
Cancellation stops scheduling further ticks and Halts the tree so Haltable nodes can clean up. A Tick that is already in progress is not interrupted — long-running actions should watch env.Context().Done() themselves.
func (*TreeRunner) RunOnce ¶
func (tr *TreeRunner) RunOnce(env Env) RunStatus
RunOnce ticks the tree once, fires the matching callback, and returns the status. It does not Halt afterward (the tree may still be Running).
type TreeVisualizer ¶
type TreeVisualizer struct {
// contains filtered or unexported fields
}
TreeVisualizer renders a behavior tree as indented text.
func NewTreeVisualizer ¶
func NewTreeVisualizer(root Behavior) *TreeVisualizer
NewTreeVisualizer creates a visualizer for root.
func (*TreeVisualizer) Visualize ¶
func (tv *TreeVisualizer) Visualize() string
Visualize returns a multi-line text representation of the tree.
func (*TreeVisualizer) WithNodeStatuses ¶
func (tv *TreeVisualizer) WithNodeStatuses(statuses map[Behavior]RunStatus) *TreeVisualizer
WithNodeStatuses enables status annotations from the given map.
type UntilFailure ¶
type UntilFailure struct {
BaseDecorator
}
UntilFailure ticks the child until it returns Failure. When the child fails, the decorator returns Success (the wait succeeded).
func NewUntilFailure ¶
func NewUntilFailure(child Behavior) *UntilFailure
NewUntilFailure creates an UntilFailure decorator.
func (*UntilFailure) Tick ¶
func (u *UntilFailure) Tick(env Env) RunStatus
Tick returns Success when the child fails; otherwise Running.
type UntilSuccess ¶
type UntilSuccess struct {
BaseDecorator
}
UntilSuccess ticks the child until it returns Success. Failure and Running both yield Running from the decorator.
func NewUntilSuccess ¶
func NewUntilSuccess(child Behavior) *UntilSuccess
NewUntilSuccess creates an UntilSuccess decorator.
func (*UntilSuccess) Tick ¶
func (u *UntilSuccess) Tick(env Env) RunStatus
Tick returns Success only when the child succeeds; otherwise Running.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
agent
command
Agent demonstrates reactive vs memory control flow for a simple NPC.
|
Agent demonstrates reactive vs memory control flow for a simple NPC. |
|
hello
command
Hello is a minimal bt-go example: a two-step Sequence that prints a greeting.
|
Hello is a minimal bt-go example: a two-step Sequence that prints a greeting. |