Writing SequenceNodes
Overview
This tutorial demonstrates a couple of simple careBT SequenceNodes
. For the demos
the ActionNode
(AddTwoNumbersAction
) implemented in the previous tutorial is reused
and two additional ActionNodes
are implemented. The first one (CreateRandomNumberAction
)
generates a random numer and provides it as output parameter. The second one (PrintNumberAction
)
prints the, as input parameter, provided number on standard output.
Create the ActionNodes and the SimpleSequence1
This first example called SimpleSequence1
contains four child nodes.
The first two nodes generate two random numbers, the third one adds them and the last one prints the result.
Create a file named simple_sequence.py
with following content.
Or use the provided file: simple_sequence.py
1import random
2
3from carebt import ActionNode
4from carebt import NodeStatus
5from carebt import SequenceNode
6from carebt.examples.action_with_params import AddTwoNumbersAction
7
8
9########################################################################
10
11
12class CreateRandomNumberAction(ActionNode):
13 """The `CreateRandomNumberAction` example node.
14
15 The `CreateRandomNumberAction` creates a random number between 1 and 10
16 and binds it to the output parameter.
17
18 Output Parameters
19 -----------------
20 ?number : int
21 The randomly generated number
22
23 """
24
25 def __init__(self, bt_runner):
26 super().__init__(bt_runner, '=> ?number')
27
28 def on_tick(self) -> None:
29 self._number = random.randint(1, 10)
30 print(f'CreateRandomNumberAction: number = {self._number}')
31 self.set_status(NodeStatus.SUCCESS)
32
33########################################################################
34
35
36class PrintNumberAction(ActionNode):
37 """The `PrintNumberAction` example node.
38
39 The `PrintNumberAction` prints the, as input parameter provided, number on
40 standard output.
41
42 Input Parameters
43 ----------------
44 ?number : int
45 The number to print
46
47 """
48
49 def __init__(self, bt_runner):
50 super().__init__(bt_runner, '?number')
51
52 def on_tick(self) -> None:
53 print(f'PrintNumberAction: number = {self._number}')
54 self.set_status(NodeStatus.SUCCESS)
55
56########################################################################
57
58
59class SimpleSequence1(SequenceNode):
60 """The `SimpleSequence1` example node.
61
62 The `SimpleSequence` runs the nodes `CreateRandomNumberAction`,
63 `CreateRandomNumberAction`, `AddTwoNumbersAction` and `PrintNumberAction`
64 in a sequence. The first two nodes create random numbers, the third one
65 adds them together and the last one prints the result. Furthermore, the
66 result is returned by the ouput parameter *?c*.
67
68 Output Parameters
69 -----------------
70 ?c : int
71 The result
72
73 """
74
75 def __init__(self, bt_runner):
76 super().__init__(bt_runner, '=> ?c')
77
78 def on_init(self) -> None:
79 self.append_child(CreateRandomNumberAction, '=> ?a')
80 self.append_child(CreateRandomNumberAction, '=> ?b')
81 self.append_child(AddTwoNumbersAction, '?a ?b => ?c')
82 self.append_child(PrintNumberAction, '?c')
The code explained
The first statements are the includes for the Python random library, the careBT classes (ActionNode
,
NodeStatus
, SequenceNode
) and the AddTwoNumbersAction
.
import random
from carebt import ActionNode
from carebt import NodeStatus
from carebt import SequenceNode
from carebt.examples.action_with_params import AddTwoNumbersAction
The CreateRandomNumberAction
is a custom ActionNode
which generates a random number which is bound to
the output parameter ?number.
class CreateRandomNumberAction(ActionNode):
"""The `CreateRandomNumberAction` example node.
The `CreateRandomNumberAction` creates a random number between 1 and 10
and binds it to the output parameter.
Output Parameters
-----------------
?number : int
The randomly generated number
"""
def __init__(self, bt_runner):
super().__init__(bt_runner, '=> ?number')
def on_tick(self) -> None:
self._number = random.randint(1, 10)
print(f'CreateRandomNumberAction: number = {self._number}')
self.set_status(NodeStatus.SUCCESS)
The PrintNumberAction
is a custom ActionNode
which prints the provided ?number on standard output.
class PrintNumberAction(ActionNode):
"""The `PrintNumberAction` example node.
The `PrintNumberAction` prints the, as input parameter provided, number on
standard output.
Input Parameters
----------------
?number : int
The number to print
"""
def __init__(self, bt_runner):
super().__init__(bt_runner, '?number')
def on_tick(self) -> None:
print(f'PrintNumberAction: number = {self._number}')
self.set_status(NodeStatus.SUCCESS)
The SimpleSequence1
node is implemented as a Python class which inherits from SequenceNode
.
class SimpleSequence1(SequenceNode):
The class definition is followed by the Docstring documentation of the node, which also documents the interface (output parameters).
"""The `SimpleSequence1` example node.
The `SimpleSequence` runs the nodes `CreateRandomNumberAction`,
`CreateRandomNumberAction`, `AddTwoNumbersAction` and `PrintNumberAction`
in a sequence. The first two nodes create random numbers, the third one
adds them together and the last one prints the result. Furthermore, the
result is returned by the ouput parameter *?c*.
Output Parameters
-----------------
?c : int
The result
"""
The constructor (__init__
) of the SimpleSequence1
needs to call the constructor (super().__init__
)
of the SequenceNode
and passes the bt_runner and the signature as arguments. The signature defines one
output parameter called ?c.
def __init__(self, bt_runner):
super().__init__(bt_runner, '=> ?c')
The on_init
function is called rigth after the node was created. It is the place to put the code which should
be executed once, after the node was created. For a SequenceNode
the child nodes are typically added here.
def on_init(self) -> None:
self.append_child(CreateRandomNumberAction, '=> ?a')
self.append_child(CreateRandomNumberAction, '=> ?b')
self.append_child(AddTwoNumbersAction, '?a ?b => ?c')
self.append_child(PrintNumberAction, '?c')
Run the example
Start the Python interpreter and run the SimpleSequence1
node:
>>> import carebt
>>> from carebt.examples.simple_sequence import SimpleSequence1
>>> bt_runner = carebt.BehaviorTreeRunner()
>>> bt_runner.run(SimpleSequence1, '=> ?x')
CreateRandomNumberAction: number = 6
CreateRandomNumberAction: number = 5
AddTwoNumbersAction: calculating: 6 + 5 = 11
PrintNumberAction: number = 11
>>> bt_runner.run(SimpleSequence1, '=> ?x')
CreateRandomNumberAction: number = 7
CreateRandomNumberAction: number = 9
AddTwoNumbersAction: calculating: 7 + 9 = 16
PrintNumberAction: number = 16
Create the SimpleSequence2
The SimpleSequence2
is a modified version of the SimpleSequence1
. It shows how simple a different
sequence of children can be created and how the parameters can be bound across the different children.
Add the following content to simple_sequence.py
.
Or use the provided file: simple_sequence.py
1class SimpleSequence2(SequenceNode):
2 """The `SimpleSequence2` example node.
3
4 The `SimpleSequence2` demonstrates a modified version of the
5 `SimpleSequence1`. The value provided as an input parameter is
6 added to a randomly generated value. The result is then added
7 to another randomly generated value. This final result is then
8 provided as an output of the `SequenceNode`.
9
10 Input Parameters
11 ----------------
12 ?a : int
13 A number for the calculations
14
15 Output Parameters
16 -----------------
17 ?e : int
18 The result
19
20 """
21
22 def __init__(self, bt_runner):
23 super().__init__(bt_runner, '?a => ?e')
24
25 def on_init(self) -> None:
26 self.append_child(CreateRandomNumberAction, '=> ?b')
27 self.append_child(AddTwoNumbersAction, '?a ?b => ?c')
28 self.append_child(PrintNumberAction, '?c')
29 self.append_child(CreateRandomNumberAction, '=> ?d')
30 self.append_child(AddTwoNumbersAction, '?c ?d => ?e')
31 self.append_child(PrintNumberAction, '?e')
Run the example
Start the Python interpreter and run the SimpleSequence2
node:
>>> import carebt
>>> from carebt.examples.simple_sequence import SimpleSequence2
>>> bt_runner = carebt.BehaviorTreeRunner()
>>> bt_runner.run(SimpleSequence2, '5 => ?x')
CreateRandomNumberAction: number = 2
AddTwoNumbersAction: calculating: 5 + 2 = 7
PrintNumberAction: number = 7
CreateRandomNumberAction: number = 2
AddTwoNumbersAction: calculating: 7 + 2 = 9
PrintNumberAction: number = 9
>>> bt_runner.run(SimpleSequence2, '7 => ?x')
CreateRandomNumberAction: number = 1
AddTwoNumbersAction: calculating: 7 + 1 = 8
PrintNumberAction: number = 8
CreateRandomNumberAction: number = 2
AddTwoNumbersAction: calculating: 8 + 2 = 10
PrintNumberAction: number = 10
Create the SimpleSequence3
The SimpleSequence3
shows another example how custom ActionNodes
and custom SequenceNodes
can be reused
and how the parameters can be bound.
Add the following content to simple_sequence.py
.
Or use the provided file: simple_sequence.py
1class SimpleSequence3(SequenceNode):
2 """The `SimpleSequence3` example node.
3
4 The `SimpleSequence3` shows another example sequence where the two sequences
5 implemented above are reused.
6
7 """
8
9 def __init__(self, bt_runner):
10 super().__init__(bt_runner)
11
12 def on_init(self) -> None:
13 self.append_child(SimpleSequence1, '=> ?x')
14 self.append_child(PrintNumberAction, '?x')
15 self.append_child(SimpleSequence2, '?x => ?y')
16 self.append_child(PrintNumberAction, '?y')
Run the example
Start the Python interpreter and run the SimpleSequence3
node:
>>> import carebt
>>> from carebt.examples.simple_sequence import SimpleSequence3
>>> bt_runner = carebt.BehaviorTreeRunner()
>>> bt_runner.run(SimpleSequence3)
CreateRandomNumberAction: number = 5
CreateRandomNumberAction: number = 8
AddTwoNumbersAction: calculating: 5 + 8 = 13
PrintNumberAction: number = 13
PrintNumberAction: number = 13
CreateRandomNumberAction: number = 2
AddTwoNumbersAction: calculating: 13 + 2 = 15
PrintNumberAction: number = 15
CreateRandomNumberAction: number = 1
AddTwoNumbersAction: calculating: 15 + 1 = 16
PrintNumberAction: number = 16
PrintNumberAction: number = 16
>>> bt_runner.run(SimpleSequence3)
CreateRandomNumberAction: number = 1
CreateRandomNumberAction: number = 2
AddTwoNumbersAction: calculating: 1 + 2 = 3
PrintNumberAction: number = 3
PrintNumberAction: number = 3
CreateRandomNumberAction: number = 6
AddTwoNumbersAction: calculating: 3 + 6 = 9
PrintNumberAction: number = 9
CreateRandomNumberAction: number = 3
AddTwoNumbersAction: calculating: 9 + 3 = 12
PrintNumberAction: number = 12
PrintNumberAction: number = 12
Note
The SimpleSequence3
example shows how easily a behavior can be composed out of already existing
sub-behaviors, while the individual sub-behaviors can still be executed and tested separately.