Pacman Bot Behavior Tree Demo
This is a demo of a behavior tree for a game playing bot. It has no practical use for Vultron, rather it's an introduction to how to use the behavior tree framework we've built.
We're providing this as an example to show how to build a behavior tree that can be used to implement some context-aware behavior.
It implements a simplified Pacman game with a bot that eats pills and avoids ghosts.
Behaviors
If no ghosts are nearby, Pacman eats one pill per tick. But if a ghost is nearby, Pacman will chase it if it is scared, otherwise he will avoid it. If he successfully avoids a ghost, he will eat a pill. The game ends when Pacman has eaten all the pills or has been eaten by a ghost.
Scoring
Scoring is as follows:
- 10 points per pill
- 200 points for the first ghost, 400 for the second, 800 for the third, 1600 for the fourth
There are 240 pills on the board. The max score is
Differences from the real thing
If the game exceeds 1000 ticks, Pacman gets bored and quits (but statistically that should never happen).
We did not model power pellets, fruit, or the maze. Ghosts just randomly get scared and then randomly stop being scared. Ghosts and pills are just counters. Ghost location is just a random "nearby" check.
The Behavior Tree
The tree structure is shown below.
graph TD
MaybeEatPills_1["→ MaybeEatPills"]
MaybeChaseOrAvoidGhost_2["? MaybeChaseOrAvoidGhost"]
MaybeEatPills_1 --> MaybeChaseOrAvoidGhost_2
NoMoreGhosts_3["#8645; NoMoreGhosts"]
MaybeChaseOrAvoidGhost_2 --> NoMoreGhosts_3
GhostsRemain_4(["#11052; GhostsRemain"])
NoMoreGhosts_3 --> GhostsRemain_4
NoGhostClose_5["#8645; NoGhostClose"]
MaybeChaseOrAvoidGhost_2 --> NoGhostClose_5
GhostClose_6["#127922; GhostClose"]
NoGhostClose_5 --> GhostClose_6
ChaseOrAvoidGhost_7["? ChaseOrAvoidGhost"]
MaybeChaseOrAvoidGhost_2 --> ChaseOrAvoidGhost_7
ChaseIfScared_8["→ ChaseIfScared"]
ChaseOrAvoidGhost_7 --> ChaseIfScared_8
GhostsScared_9(["#11052; GhostsScared"])
ChaseIfScared_8 --> GhostsScared_9
ChaseGhost_10["#127922; ChaseGhost"]
ChaseIfScared_8 --> ChaseGhost_10
CaughtGhost_11["→ CaughtGhost"]
ChaseIfScared_8 --> CaughtGhost_11
DecrGhostCount_12["#9648; DecrGhostCount"]
CaughtGhost_11 --> DecrGhostCount_12
ScoreGhost_13["#9648; ScoreGhost"]
CaughtGhost_11 --> ScoreGhost_13
IncrGhostScore_14["#9648; IncrGhostScore"]
CaughtGhost_11 --> IncrGhostScore_14
GhostsScared_15(["#11052; GhostsScared"])
ChaseOrAvoidGhost_7 --> GhostsScared_15
AvoidGhost_16["#127922; AvoidGhost"]
ChaseOrAvoidGhost_7 --> AvoidGhost_16
EatPill_17["#9648; EatPill"]
MaybeEatPills_1 --> EatPill_17
Legend:
Symbol | Meaning |
---|---|
? | FallbackNode |
→ | SequenceNode |
⇅ | Invert |
▰ | ActionNode |
⬬ | ConditionNode |
🎲 | Fuzzer node (randomly succeeds or fails some percentage of the time) |
Demo Output
Example
# if vultron package is installed
# run the demo
$ vultrabot --pacman
# print tree and exit
$ vultrabot --pacman --print-tree
# if vultron package is not installed
$ python -m vultron.bt.base.demo.pacman
When the tree is run, it will look something like this:
=== Tick 1 ===
(>) >_MaybeEatPills_1
| (?) ?_MaybeChaseOrAvoidGhost_2
| | (^) ^_NoMoreGhosts_3
| | | (c) c_GhostsRemain_4
| | | | = SUCCESS
| | | = FAILURE
| | (^) ^_NoGhostClose_5
| | | (z) z_GhostClose_6
| | | | = SUCCESS
| | | = FAILURE
| | (?) ?_ChaseOrAvoidGhost_7
| | | (>) >_ChaseIfScared_8
| | | | (c) c_GhostsScared_9
| | | | | = FAILURE
| | | | = FAILURE
| | | (c) c_GhostsScared_15
| | | | = FAILURE
| | | (z) z_AvoidGhost_16
| | | | = SUCCESS
| | | = SUCCESS
| | = SUCCESS
| (a) a_EatPill_17
| | = SUCCESS
| = SUCCESS
=== Tick 2 ===
(>) >_MaybeEatPills_1
| (?) ?_MaybeChaseOrAvoidGhost_2
| | (^) ^_NoMoreGhosts_3
| | | (c) c_GhostsRemain_4
| | | | = SUCCESS
| | | = FAILURE
| | (^) ^_NoGhostClose_5
| | | (z) z_GhostClose_6
| | | | = FAILURE
| | | = SUCCESS
| | = SUCCESS
| (a) a_EatPill_17
| | = SUCCESS
| = SUCCESS
=== Tick 3 ===
Ghosts are scared!
(>) >_MaybeEatPills_1
| (?) ?_MaybeChaseOrAvoidGhost_2
| | (^) ^_NoMoreGhosts_3
| | | (c) c_GhostsRemain_4
| | | | = SUCCESS
| | | = FAILURE
| | (^) ^_NoGhostClose_5
| | | (z) z_GhostClose_6
| | | | = SUCCESS
| | | = FAILURE
| | (?) ?_ChaseOrAvoidGhost_7
| | | (>) >_ChaseIfScared_8
| | | | (c) c_GhostsScared_9
| | | | | = SUCCESS
| | | | (z) z_ChaseGhost_10
| | | | | = SUCCESS
| | | | (>) >_CaughtGhost_11
| | | | | (a) a_DecrGhostCount_12
Clyde was caught!
| | | | | | = SUCCESS
| | | | | (a) a_ScoreGhost_13
| | | | | | = SUCCESS
| | | | | (a) a_IncrGhostScore_14
Ghost score is now 400
| | | | | | = SUCCESS
| | | | | = SUCCESS
| | | | = SUCCESS
| | | = SUCCESS
| | = SUCCESS
| (a) a_EatPill_17
| | = SUCCESS
| = SUCCESS
=== Tick 4 ===
(>) >_MaybeEatPills_1
| (?) ?_MaybeChaseOrAvoidGhost_2
| | (^) ^_NoMoreGhosts_3
| | | (c) c_GhostsRemain_4
| | | | = SUCCESS
| | | = FAILURE
| | (^) ^_NoGhostClose_5
| | | (z) z_GhostClose_6
| | | | = SUCCESS
| | | = FAILURE
| | (?) ?_ChaseOrAvoidGhost_7
| | | (>) >_ChaseIfScared_8
| | | | (c) c_GhostsScared_9
| | | | | = FAILURE
| | | | = FAILURE
| | | (c) c_GhostsScared_15
| | | | = FAILURE
| | | (z) z_AvoidGhost_16
| | | | = SUCCESS
| | | = SUCCESS
| | = SUCCESS
| (a) a_EatPill_17
| | = SUCCESS
| = SUCCESS
=== Tick 5 ===
(>) >_MaybeEatPills_1
| (?) ?_MaybeChaseOrAvoidGhost_2
| | (^) ^_NoMoreGhosts_3
| | | (c) c_GhostsRemain_4
| | | | = SUCCESS
| | | = FAILURE
| | (^) ^_NoGhostClose_5
| | | (z) z_GhostClose_6
| | | | = SUCCESS
| | | = FAILURE
| | (?) ?_ChaseOrAvoidGhost_7
| | | (>) >_ChaseIfScared_8
| | | | (c) c_GhostsScared_9
| | | | | = FAILURE
| | | | = FAILURE
| | | (c) c_GhostsScared_15
| | | | = FAILURE
| | | (z) z_AvoidGhost_16
| | | | = FAILURE
| | | = FAILURE
| | = FAILURE
| = FAILURE
Pacman died! He was eaten by Pinky!
Final score: 240
Ticks: 5
Dots Remaining: 236
Ghosts Remaining: 3 (Blinky, Pinky, Inky)
Demo Code
vultron.bt.base.demo.pacman
This is a demo of the bt tree library. It is a stub implementation of a bot that plays Pacman.
decr_ghost_count(obj)
decrements the ghost count
Source code in vultron/bt/base/demo/pacman.py
95 96 97 98 99 |
|
ghosts_remain(obj)
checks if there are any ghosts remaining.
Source code in vultron/bt/base/demo/pacman.py
111 112 113 |
|
ghosts_scared(obj)
checks if a ghost is scared.
Source code in vultron/bt/base/demo/pacman.py
122 123 124 |
|
inc_ghost_score(obj)
increments the score for the next ghost.
Source code in vultron/bt/base/demo/pacman.py
74 75 76 77 78 |
|