Behavior Trees

From Unvanquished
(Redirected from Behavior-trees)
Jump to: navigation, search

Introduction

Bot behaviors are decided individually, at each "frame" (read: server's main loop passage), starting from a root node (I am not sure when exactly evaluation ends). An overview can be found in the Wikipedia article.

Syntax Reference

Behavior trees use a specific syntax, which obey following rules (guessed from actual implementation and some C++ readings):

  • Each node or leaf returns either STATUS_SUCCESS, STATUS_FAILURE or STATUS_RUNNING.
  • at most one node, leaf, block start or block end per line
  • empty lines are accepted
  • comments can either:
  • start with // and go to the end of line
  • start with /* and go to next */, including out of current line (TODO: verify).
  • comments can not be nested
  • actions and conditions taking parameters must enclose those withing parentheses, separated with commas (i.e. roamInRadius( E_H_REACTOR, 500 ))
  • if an action or a condition does not need parameters, parentheses are optional
  • spaces and tabulations are meaningless
  • strings start and end with a double quote "
  • keywords are reserved (TODO: verify, but even if wrong, better avoid their use)
  • values can't be stored or modified (additions, subtractions, multiplications, etc)
  • no user-defined function (but files can be included, and C++ functions can be written in the game logic and be used in the behavior tree)
  • each possible path should (TODO: verify, even if I fail to understand why one would break that willingly) end by one or more action

Note:

It is not possible to do mathematical operations such as additions, multiplications, divisions, ...

Keywords

There are several keywords:

  • behavior
  • action
  • sequence
  • selector
  • concurrent
  • decorator
  • condition

The behavior keyword

behavior NAME

Include named behavior at current place. It is unsure if this is just an inclusion of if the subtree is parsed and stored individually.

The action keyword

action NAME
action NAME()
action NAME( param )
action NAME( param1, param2, ..., paramN )

Leaves of the tree, they trigger an action from the bot.

List of actions

Titles provide a link to more detailed pages. TODO: have something more time-resilient (one page per call, with it's changelog, maybe?)

0.52

  • activateUpgrade
  • aimAtGoal
  • alternateStrafe
  • buy
  • changeGoal
  • classDodge
  • deactivateUpgrade
  • equip
  • evolve
  • evolveTo
  • fight
  • fireWeapon
  • flee
  • heal
  • jump
  • moveInDir
  • moveTo
  • moveToGoal
  • repair
  • resetStuckTime
  • roam
  • roamInRadius
  • rush
  • say
  • strafeDodge
  • suicide

Creating new actions

Actions are exported in the AIActionMap_s AIActions[] C array. Actions always return a status. Actions can take a variable number of parameters (they have minimum and maximum parameter numbers). Parameter types are not described by API.

The concurrent keyword

concurrent
{
	NODE_1
	NODE_2
	...
	NODE_N
}

Concurrent nodes evaluate each children until one returns STATUS_FAILURE. Differences with sequence:

  • STATUS_RUNNING does not trigger a return
  • Returns either STATUS_SUCCESS except if one child failed STATUS_FAILURE.

Notes:

  • if 2 tasks were previously in STATUS_RUNNING and the 1st returns STATUS_FAILURE, concurrent returns immediately.

The sequence keyword

sequence
{
	NODE_1
	NODE_2
	...
 	NODE_N
}

Sequential nodes evaluate each children one after the other, as long as they return STATUS_SUCCESS. Return last child's status.

Differences with concurrent:

  • Sequence breaks if a child returns STATUS_RUNNING
  • Starts at last STATUS_RUNNING, if any

The selector keyword

selector
{
	NODE_1
	NODE_2
	...
	NODE_N
}

Selector nodes evaluate each children one after the other, as long as they return STATUS_FAILURE. Return last child's status. Does not restart from last STATUS_RUNNING.

The decorator keyword

decorator TYPE( VALUE )
{
	NODE_1
	NODE_2
	...
	NODE_N
}

Decorator nodes alters their children's behavior. Decorator types:

  • return
  • timer

The return keyword

UNSURE Force children nodes to return a specific value

The timer keyword

On encountering a timer( N ) node, STATUS_FAILURE is immediately returned if the timer's child node has already returned STATUS_FAILURE within the last N milliseconds.

The node may run more often than every N milliseconds if it keeps returning success.

The condition keyword

 condition EXPRESSION
 {
 	NODE
 }
 condition EXPRESSION
 {
 }

Conditions return either STATUS_FAILURE if EXPRESSION is evaluated as false, STATUS_SUCCESS if there is no child, or child's status.

Note:

  • only executes a single child.
  • EXPRESSION can include functions calls.

Operators

Operators are listed in the AIOpMap_s conditionOps[] array. operators sorted by precedence (1st have higher priority):

  • "! "
  • "< "
  • "<="
  • "> "
  • ">="
  • "=="
  • "!="
  • "&&"
  • "||"

Functions

Return value is "boxed" (AIBox<T>()) in the C++ code. Conditions are exported in the AIConditionMap_s conditionFuncs[] array.

List of functions (as of 0.52)

{ "alienMomentum",     VALUE_INT,   alienMomentum,     0 }, 
{ "humanMomentum",     VALUE_INT,   humanMomentum,     0 }, 
{ "alertedToEnemy",    VALUE_INT,   alertedToEnemy,    0 }, 
{ "baseRushScore",     VALUE_FLOAT, baseRushScore,     0 }, 
{ "buildingIsDamaged", VALUE_INT,   buildingIsDamaged, 0 }, 
{ "canEvolveTo",       VALUE_INT,   botCanEvolveTo,    1 }, 
{ "class",             VALUE_INT,   botClass,          0 }, 
{ "cvarFloat",         VALUE_FLOAT, cvarFloat,         1 }, 
{ "cvarInt",           VALUE_INT,   cvarInt,           1 }, 
{ "directPathTo",      VALUE_INT,   directPathTo,      1 }, 
{ "distanceTo",        VALUE_FLOAT, distanceTo,        1 }, 
{ "goalBuildingType",  VALUE_INT,   goalBuildingType,  0 }, 
{ "goalIsDead",        VALUE_INT,   goalDead,          0 }, 
{ "goalTeam",          VALUE_INT,   goalTeam,          0 }, 
{ "goalType",          VALUE_INT,   goalType,          0 }, 
{ "haveUpgrade",       VALUE_INT,   haveUpgrade,       1 }, 
{ "haveWeapon",        VALUE_INT,   haveWeapon,        1 }, 
{ "healScore",         VALUE_FLOAT, healScore,         0 }, 
{ "inAttackRange",     VALUE_INT,   inAttackRange,     1 }, 
{ "isVisible",         VALUE_INT,   isVisible,         1 }, 
{ "percentAmmo",       VALUE_FLOAT, percentAmmo,       0 }, 
{ "percentHealth",     VALUE_FLOAT, percentHealth,     1 }, 
{ "random",            VALUE_FLOAT, randomChance,      0 }, 
{ "skill",             VALUE_INT,   botSkill,          0 }, 
{ "stuckTime",         VALUE_INT,   stuckTime,         0 }, 
{ "team",              VALUE_INT,   botTeam,           0 }, 
{ "teamateHasWeapon",  VALUE_INT,   teamateHasWeapon,  1 }, 
{ "weapon",            VALUE_INT,   currentWeapon,     0 },

Pre-defined symbols

Some values are pre-defined and usable as action's or function's parameters.

Those are exported by calling a macro named 'D' (yes, I know). List of exported symbols:

  • human upgrades (including medkit)
  • human weapons (excluding blaster)
  • team names (aliens, humans, and none)
  • alien buildings
  • human buildings
  • E_GOAL
  • E_ENEMY
  • E_DAMAGEDBUILDING
  • E_SELF
  • classes (human ones, alien ones, and PCL_NONE)
  • moves (forward, backward, right, left)
  • some say commands (all, team, area, area_team)
  • task/check status names (running, success, failure)

Notes

Parameters needs to be (un)wrapped in C++ before being accessed.

Parameters can be of the following (C) types:

  • float
  • int (probably better assume 32 bit signed integers)
  • string (probably better assume null-terminated)
  • double (Unboxed only)
  • token (Boxed only, either a TT_STRING, a float or an int...)

To provide a value to an action or check, the C++ code must UnBox (AIUnBox<T>()) it.