# Behavior Trees

## What is a tree?

A behavior tree allows for a complex set of requirements and tasks to be defined in such a way that a decision can be made based on an infinite number of states. \
&#x20;  Behavior Trees are used in AI, robotics, mechanical control systems, etc. We use them with [SyncAgent's ](https://docs.perigee.software/core-modules/event-sources/scheduled-logic/sync-agent)for fine grain execution control.&#x20;

## An example, please?

Let's break that down to something we can all follow, like programming the AI of a video game.

The AI needs to do **two** things:

* Check for enemies in the area, If there are enemies in the area, then:
  * Check if they want to attack, or can attack
  * Only if the above statement is true, move into combat range
* Check for non combat activities after checking that there is no immediate danger in the area
  * Am I hungry?
  * Am I thirsty?

The **two** state checks (enemy check, non combat check) are known as a **`list of fallbacks`**. The reason for this is that every subsequent child node in a **`Fallback`** is executed in order until one succeeds.

As almost an opposite to **`Fallback`** nodes - **`Sequence`** nodes only execute their children in order if the previous one succeeds.

{% hint style="success" %}
Use **`Fallback`** nodes for an ordered list of items to execute, where a failure moves onto the next node\
\
Use **`Sequence`** if the children need to succeed to proceed to the next node
{% endhint %}

The demo below uses simple bool variables to control state, but in a real scenario, these would be callback to check within a radius of the AI, or pull it's current hunger/thirst levels.

```csharp
bool Enemies = true;
bool Attackable = true;

bool Hungry = false;
bool Thirsty = true;

var btt = new BehaviorTree("AI").AddFallback("Activities",
    new Fallback("Check for enemies",
        new Sequence("Any enemies around?",
            new Leaf("Enemy Checker", (l) => { Console.WriteLine("Enemy Checker"); return Enemies ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Enemy Attackable?", (l) => { Console.WriteLine("Enemy Attackable?"); return Attackable ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Move in for combat", (l) => { Console.WriteLine("Move in for combat"); return NodeStatus.SUCCESS; }))),

     new Fallback("Non Combat Activities",
        new Sequence("Am I hungry?",
            new Leaf("Hungry test", (l) => { Console.WriteLine("Hungry test"); return Hungry ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Hungry - eat food", (l) => { Console.WriteLine("Hungry - eat food"); return NodeStatus.SUCCESS; })),
        new Sequence("Am I Thirsty?",
            new Leaf("Thirsty test", (l) => { Console.WriteLine("Thirsty test"); return Thirsty ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Thirsty - drink", (l) => { Console.WriteLine("Thirsty - drink"); return NodeStatus.SUCCESS; }))
    )
);
```

With that code in place, let's print and run our tree:

```csharp
Console.WriteLine(btt.Print());
var status = BTTickEngine.RunOnce(btt);
Console.WriteLine($"Status: {Enum.GetName(status)}");
```

#### Results:

If you run the code as shown above, you'll notice the last node to be executed is: \ <mark style="color:red;">**Move in for combat**</mark>.&#x20;

The reason for this is because `"Check for Enemies"` takes priority over `"Non Combat Activities"`.

#### Change the results

Let's set **`bool Enemies = false;`** and run again. The last node to run this time is:

<mark style="color:red;">**Thirsty - drink**</mark>.

That's because our fallback node didn't have any enemies to check for, and our Thirsty bool is true.

This is what makes behavior trees so powerful. Simple state management changes the outcome of what it decides to do. Perigee has a built in SDK to make working with Behavior Trees quite easy.

## SDK

### The Print() Command

The print produces a nice breakdown of the tree itself. Showing nodes and their children

```
AI (BehaviorTree)
    Activities (Fallback)
        Check for enemies (Fallback)
            Any enemies around? (Sequence)
                Enemy Checker (Leaf)
                Enemy Attackable? (Leaf)
                Move in for combat (Leaf)
        Non Combat Activities (Fallback)
            Am I hungry? (Sequence)
                Hungry test (Leaf)
                Hungry - eat food (Leaf)
            Am I Thirsty? (Sequence)
                Thirsty test (Leaf)
                Thirsty - drink (Leaf)
```

### The BTTickEngine

The tick engine runs a tree until success or failure. The run code is effectively processing the tree until it's no longer **`running`**:

```csharp
//Use the built in TickEngine
var status = BTTickEngine.RunOnce(btt);

//Or run it manually
NodeStatus st = NodeStatus.RUNNING;
while (st == NodeStatus.RUNNING)
{
    st = btt.Process();
    Task.Delay(10).Wait();
}
```

## Nodes and Types

The three main types are Fallback, Sequence, and Leaf. Leaf nodes are "special" nodes as they are the only nodes that contain a callback for code to be executed to determine the node state.

### Node States

The three states are:

* Success - It's completed.
* Processing - It's still working on it, try again...
* Failure - It failed, stop.

### Fallback&#x20;

*(Sometimes called a "Selector Node")*

Fallback nodes process children nodes in order until a child node is a success, then fall out of the loop.

### Sequence

Processes all children in order, if a child fails, it will return a failure and start back at the first child on next rerun (tick)

### Leaf

Execute code to determine what status a node is in.

## Node Operations

There are several special operations that can be performed on nodes.&#x20;

### Shuffle

You can shuffle a set of nodes by using this extension. The example below randomly shuffles whether the hunger check or the thirst check happens first

```csharp
new Fallback("Non Combat Activities",
        new Sequence("Am I hungry?",
            new Leaf("Hungry test", (l) => { Console.WriteLine("Hungry test"); return Hungry ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Hungry - eat food", (l) => { Console.WriteLine("Hungry - eat food"); return NodeStatus.SUCCESS; })),
        new Sequence("Am I Thirsty?",
            new Leaf("Thirsty test", (l) => { Console.WriteLine("Thirsty test"); return Thirsty ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Thirsty - drink", (l) => { Console.WriteLine("Thirsty - drink"); return NodeStatus.SUCCESS; }))
    ).Shuffle()
```

### Sort

Every node has a default **`SortOrder`** property. You can assign nodes a sort order or re-prioritize them at runtime/tree creation.&#x20;

Sort orders these in ascending order. In this example, we sort **Thirsty** above **Hungry**.

```csharp
new Fallback("Non Combat Activities",
        new Sequence("Am I hungry?", 2,
            new Leaf("Hungry test", (l) => { Console.WriteLine("Hungry test"); return Hungry ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Hungry - eat food", (l) => { Console.WriteLine("Hungry - eat food"); return NodeStatus.SUCCESS; })),
        new Sequence("Am I Thirsty?", 1,
            new Leaf("Thirsty test", (l) => { Console.WriteLine("Thirsty test"); return Thirsty ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Thirsty - drink", (l) => { Console.WriteLine("Thirsty - drink"); return NodeStatus.SUCCESS; }))
    ).Sort()
```

### Invert

Invert reverses statuses. Success become failure, and failure become success.

Even though the bool **`Hungry = false;`** - the inversion node inverts the node status to be **`SUCCESS`**.

```csharp
new Fallback("Non Combat Activities",
        new Sequence("Am I hungry?",
            new Leaf("Hungry test", (l) => { Console.WriteLine("Hungry test"); return Hungry ? NodeStatus.SUCCESS : NodeStatus.FAILURE; })
                .Invert(),
           
             new Leaf("Hungry - eat food", (l) => { Console.WriteLine("Hungry - eat food"); return NodeStatus.SUCCESS; })),
        new Sequence("Am I Thirsty?",
            new Leaf("Thirsty test", (l) => { Console.WriteLine("Thirsty test"); return Thirsty ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Thirsty - drink", (l) => { Console.WriteLine("Thirsty - drink"); return NodeStatus.SUCCESS; }))
    )
```

### Retry

Retry does as it sounds, it retries the node X number of times if the node returns a FAILURE status:

You'll see `"Hungry test"` print 3 times (original + 2 retries).

```csharp
new Fallback("Non Combat Activities",
        new Sequence("Am I hungry?",
            new Leaf("Hungry test", (l) => { Console.WriteLine("Hungry test"); return Hungry ? NodeStatus.SUCCESS : NodeStatus.FAILURE; })
                .Retry(2),
            new Leaf("Hungry - eat food", (l) => { Console.WriteLine("Hungry - eat food"); return NodeStatus.SUCCESS; })),
        new Sequence("Am I Thirsty?",
            new Leaf("Thirsty test", (l) => { Console.WriteLine("Thirsty test"); return Thirsty ? NodeStatus.SUCCESS : NodeStatus.FAILURE; }),
            new Leaf("Thirsty - drink", (l) => { Console.WriteLine("Thirsty - drink"); return NodeStatus.SUCCESS; }))
    )
```

### Forces

There is both a **`ForceFailure`** and a **`ForceSuccess`** node. These are helpful when building certain trees that require a forceful response.

## Prebuilt Leaf nodes and the fluent SDK

### Fluent SDK

There are two handy built in methods for quickly creating the two kinds of trees:

```csharp
BehaviorTree.NewSequence("Conditionals", 
    LeafNodes.NetworkAvailable, 
    LeafNodes.PingSuccess);

BehaviorTree.NewFallback("Conditionals", LeafNodes.NetworkAvailable);
```

### LeafNodes

There are multiple pre-built leaf nodes available to use. They return a Leaf Node that is already pre-coded to return the correct Node Status.

#### Network

To check if the network is available

```csharp
LeafNodes.NetworkAvailable;
```

#### PingSuccess

To check if a ping to **`8.8.8.8`** is available

```csharp
LeafNodes.PingSuccess;
```

#### PingAddress

To check if the address responds positively to a ping&#x20;

```csharp
LeafNodes.PingAddress(new byte[] { 8,8,8,8});
```

#### SQLAvailable

To check if SQL Server can open a connection

```csharp
LeafNodes.SQLAvailable("connectionString");
```

#### Agent Exists

To check if an [Agent ](https://docs.perigee.software/core-modules/event-sources/scheduled-logic/sync-agent)exists

```csharp
LeafNodes.AgentExists("AgentName", treeHandler.AgentData);
```

#### Agent Data Expired

To check if an [Agent](https://docs.perigee.software/core-modules/event-sources/scheduled-logic/sync-agent) has expired data. Returns **SUCCESS** if the data is **NOT** expired.

```csharp
LeafNodes.AgentDataExpired("AgentName", treeHandler.AgentData);
```
