This package contains Behavior Tree nodes and interpreter implementation.
npm i @btree/core
import {nodes} from '@btree/core'
const HelloBehavior = nodes.root('Hello behavior', () =>
nodes.selector([
nodes.sequence([
nodes.condition('Has admin role', (state, props) => props?.role === 'admin'),
nodes.action('Say hello to admin', () => {
console.log('Hello boss')
})
]),
nodes.action('Say hello to user', () => {
console.log('Hello user')
})
])
)
// Create instance of tree
const helloTree = HelloBehavior()
helloTree.tick() // => Hello user
helloTree.tick({role: 'admin'}) // => Hello boss
Creates a new Behavior Tree. It takes a name of the tree and a function that return any of children nodes eg. selector, sequence etc.
const AppBehavior = nodes.root('AppBehavior', () =>
nodes.selector([
// ...
])
)
Sequence is an AND, requiring all children to succeed. It will visit each child in order, starting with the first, and when that succeeds, it will go to next one. If any child fails it will immediately return failure to the parent. If the last child in the sequence succeeds, then the sequence will return success ot its parent.
The following example will:
- Check if user is logged in
- If user is not logged in - it will do nothing
- If user is logged in - it will add item to cart
nodes.sequence([
nodes.condition('Ensure user is logged in', () => /* ... */),
nodes.action('Add item to cart', () => /* ... */)
])
Selector is an OR, requiring at least one children to succeed. It will run each child in order, starting with the first, if it fails, it will run the next one, until it finds one that succeeds an returns it to the parent. Selector will fail if all children returns failure.
The following example will:
- Fetch featured posts
- If fetching featured posts fail, it will get recent posts
nodes.selector([
nodes.action('Get featured posts', () => /* ... */),
nodes.action('Get recent posts', () => /* ... */)
])
Runs all child nodes in parallel. Continues to run until a all children nodes have either failed or succeeded.
nodes.parallel([
nodes.action('Load user profile', async (state) => {
state.profile = await // ...
}),
nodes.action('Load blog posts', async (state) => {
state.posts = await // ...
})
])
Perform a logic check on current state and props.
nodes.condition('Is loading', state => state.isLoading)
Action is used to modify state and emit side effects.
nodes.action('Stop loading', (state) => {
state.isLoading = false
})
This node is used to change children status to:
Success
if children returnedFailure
Failure
if children returnedSuccess
It's useful when you have extracted node logic to const.
const isLoading = nodes.condition('Is loading', state => state.isLoading)
nodes.sequence([
nodes.invert(isLoading),
nodes.action('Content was loaded', () => {
console.log('Done')
})
])