Skip to content

Commit

Permalink
refactor(#36): lucia package
Browse files Browse the repository at this point in the history
  • Loading branch information
DelgadoPanadero authored Nov 21, 2021
1 parent 7a1b46b commit 3c4ea93
Show file tree
Hide file tree
Showing 49 changed files with 36,995 additions and 47,604 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Hi! My name is LucIA, I'm a LuxAI Gammer based in Behavioral Ttrees. I'm here to show you how I am programmed and to get you started on behavioural trees.

<p align="center">
<img width="400" src="avatar/400px/lucia_avatar_400.jpg">
<img width="400" src="img/avatar/400px/lucia_avatar_400.jpg">
</p>

Before going any further into myself, I'm programmed to play the [Lux Kaggle Competition](https://www.lux-ai.org/). You can read all competition rules, this may be a bit of a learning curve and you can skip it if you are only interested _in me_ but make sure to read it before modify my behaviour.
Expand Down
Empty file removed __init__.py
Empty file.
174 changes: 174 additions & 0 deletions doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@

# What's a behaviour tree?

A behaviour tree is a model of a plan execution. They describe a series of tasks performed under rules. One main advantage is that the behaviour can be very complex only by using simple tasks, and another one is that the behaviour is completely predictable given the stage of rules already set.

For instance we can define the logic of an unit worker for the game as follows:

![image](worker.png)


# How to build a behavior tree

### Task Nodes

These nodes are the leaf nodes and it is where the logic of our model is implemented. This nodes does not have any control over the flow from any state of the model to another but just accions are finally executed. This tasks can return either `True` if the acction has been executed successfully or `False` otherwise.

![task](../img/task_nodes.jpg)

## Control Nodes

There are nodes in the tree called _control flow nodes_. This nodes is where the flow of states are defined. They are made by using a tree structure of two kind of nodes

### Selector node

A selector is used to find and execute the first child that does not fail. The children are executed in order from left to right. Basically, a selector selects one task or procedure in order from left to right until one is a success. If every node fails then the tree will no pick any task to perform. It returns `True` if ANY of its chiildren node return `True` else it returns `False`.


<p align="center">
<img src="../img/selector_node.jpg">
</p>

In this selector which you'll find in the action that the worker will do:

- First it will try to build a city (using a sequence of Tasks explained in the example bellow).
- If if fails to build it will try to pillage.
- If it fails too, it will try to move, and if it fails too, the Selector will return `False`(otherwise `True`).

### Sequence node

Sequence nodes are used to find and execute the first child that has not yet succeeded. The children are executed in order from left to right. The difference in behaviour with the selector is that one fails no other task from the sequence is performed. It returns `True` if ALL of its children node return `True` else it returns `False`.

<p align="center">
<img src="../img/sequence_node.jpg">
</p>

In this sequence the actions will be performed as follows:

- First it will check if the cargo of the unit has space left and if it is full, the sequence ends and returns `False`
- If it continues, it willk check if a city is needd to be build and if it returns `True` the sequence will continue.
- With the to previous nodes have been succeded, the last node will try to build a city. If it succeded, the Sequence will return `True` (otherwise `False`).

## Decorador nodes

Other kind of nodes are the decorator nodes. These nodes has only one child and are used to modity the output or the execution of its son. In the project we only use an inverter node, which changes the output of the son node from `True` to `False` and `False` into `True`.

<p align="center">
<img src="../img/inverter_node.png">
</p>

This decorator changes the output of its son. If the resourced have been researched, it will the output from `True` to `False` and the otherway arround.

## Blackboard

The nodes are used to execute the actions of the state machine and does not output any result to another note. However we might need to store states in a memory handler to reuse and share information from one node to another. This is made through a memento object called Blackboard.


# Create your own behavior trees



### BH_Trees module

All the objects required for building a behavior tree are in teh directory `bh_trees`. There we have the following classes:

```
bh_trees/
├── __init__.py
├── nodes
│ ├── class Sequence()
│ ├── class Selector()
│ ├── class Inverter()
│ └── class Task()
├── tools.py
│ └── def recursive_build()
└── blackboard.py
└── class BlackBoard()
```

* **Sequence**, **Selector**, **Inverter**: These are the structural and decorator nodes used by the tree.
* **Task**: The task node is used as a parent class to all the custom tasks defined from the project. It has a method `run()` where all the children nodes are executed and a blackboard attribute
* **BlackBoard**: This class is used to store the state of the tree. It is a singleton class so all the nodes share the same instante to read and uptade the information


### Create a Task

To create a custom task we have to create a new class that inherit from the Task class. The new class has to respect the `run()` function from Task and the atributte with the blackboard. For instance a node can be programmed as follows

```python
from bh_trees import Task


class BuildCityTile(Task):

def __init__(self):
super (BuildCityTile, self).__init__()

def run(self):
object = self._blackboard.get_value('object')
game_map = self._blackboard.get_value('map')

# Condition if it can build
if object.can_build(game_map):
build = object.build_city()
self._blackboard.append_values(actions=build)
return True
return False

```

## Create a graph

To create a behavior tree graph we have to define it as a nested dict. For instance, the worker tree graph can be defined as


```python
graph = {
Sequence(): {
Sequence(): {
CanAct(): {}
},
Selector(): { # Action selector of the example
BuildWorker(): {},
Sequence(): { # Research sequence of the example
Inverter(): {
IsResourceResearched(): {}
},
Research(): {}
}
}
}
}
```

once the graph variable is defined, we can build de graph using the following function

```python
from bh_trees import recursive_build

worker_bh_tree = recursive_build(graph)
```


### Create the Blackboard

Before running the tree we have to instanciate the BlackBoard class

```python
blackboard = set_values(actions = [],
id = self._id,
map = self._map,
turn = self._turn,
width = self._width,
height = self._height,
player = self._player,
n_units = self._n_units,
units_map = self._units_map
)

```

The task nodes can access this class for reading, writting and append new data.


[To know more here's Wikipedia](https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)).
Binary file added doc/city.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/worker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes
Binary file added img/game.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/inverter_node.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/selector_node.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/sequence_node.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/task_nodes.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions lucia/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .lucia import LucIA
from .worker import *
from .city import *
from .bh_trees import *
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions city/create_city.py → lucia/city/create_city.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from .tasks import IsEnoughtFuel
from .tasks import IsResourceResearched

from bh_trees import recursive_build
from bh_trees import Inverter, Sequence, Selector
from ..bh_trees import recursive_build
from ..bh_trees import Inverter, Sequence, Selector


def create_simple_city():
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from lux.game_constants import GAME_CONSTANTS

from bh_trees import Task
from ...bh_trees import Task


class BuildWorker(Task):
Expand Down
2 changes: 1 addition & 1 deletion worker/tasks/can_act.py → lucia/city/tasks/can_act.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bh_trees import Task
from ...bh_trees import Task


class CanAct(Task):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bh_trees import Task
from ...bh_trees import Task

from lux.game_constants import GAME_CONSTANTS

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bh_trees import Task
from ...bh_trees import Task


class IsResourceResearched(Task):
Expand Down
2 changes: 1 addition & 1 deletion city/tasks/research.py → lucia/city/tasks/research.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bh_trees import Task
from ...bh_trees import Task


class Research(Task):
Expand Down
10 changes: 5 additions & 5 deletions lucia.py → lucia/lucia.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from bh_trees import BlackBoard
from city import create_simple_city
from city import create_night_city
from worker import create_simple_worker
from worker import create_night_worker
from .bh_trees import BlackBoard
from .city import create_simple_city
from .city import create_night_city
from .worker import create_simple_worker
from .worker import create_night_worker


class LucIA():
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions worker/create_worker.py → lucia/worker/create_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from .tasks import FindNearestResource
from .tasks import BuildCityTile

from bh_trees import recursive_build
from bh_trees import Inverter, Sequence, Selector
from ..bh_trees import recursive_build
from ..bh_trees import Inverter, Sequence, Selector


def create_simple_worker():
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bh_trees import Task
from ...bh_trees import Task


class BuildCityTile(Task):
Expand Down
2 changes: 1 addition & 1 deletion city/tasks/can_act.py → lucia/worker/tasks/can_act.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bh_trees import Task
from ...bh_trees import Task


class CanAct(Task):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import math

from bh_trees import Task
from ...bh_trees import Task


class FindNearestCity(Task):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from lux.constants import Constants

from bh_trees import Task
from ...bh_trees import Task


class FindNearestEmpty(Task):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import math

from lux.constants import Constants
from bh_trees import Task
from ...bh_trees import Task


class FindNearestResource(Task):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bh_trees import Task
from ...bh_trees import Task


class IsCargoFull(Task):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bh_trees import Task
from ...bh_trees import Task


class IsCityNeeded(Task):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import random

from bh_trees import Task
from ...bh_trees import Task
from lux.constants import Constants


Expand Down
6 changes: 3 additions & 3 deletions worker/tasks/pillage.py → lucia/worker/tasks/pillage.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bh_trees import Task
from ...bh_trees import Task


class Pillage(Task):
Expand All @@ -9,7 +9,7 @@ def _init_(self):

def run(self):
map = self._blackboard.get_value('map')
object = self._blackboard.get_value('object')
object = self._blackboard.get_value('object')

if self.is_in_resource(object, map):
pillage = object.pillage()
Expand All @@ -20,4 +20,4 @@ def run(self):


def is_in_resource(self, object, map):
return map.get_cell_by_pos(object.pos).resource is not None
return map.get_cell_by_pos(object.pos).resource is not None
37 changes: 0 additions & 37 deletions main.py

This file was deleted.

1 change: 0 additions & 1 deletion run.sh

This file was deleted.

Loading

0 comments on commit 3c4ea93

Please sign in to comment.