I created little decision tree, what do you think about it, what I can correct? I wanted to create a code for tree that looks in that way:
My implementation:
using System;
using System.Collections.Generic;
using System.Text;
public interface TreeNode
{
bool nodeState { get; }
bool Evaluate();
}
/// <summary>
/// Returns true when one of childs returns true
/// </summary>
public class Selector : TreeNode
{
private List<TreeNode> childNodes;
public bool nodeState { get; private set; } = false;
public Selector(List<TreeNode> childNodes) { this.childNodes = childNodes; }
public bool Evaluate()
{
foreach (TreeNode node in childNodes)
if (node.Evaluate())
{
nodeState = true;
return true;
}
nodeState = false;
return false;
}
}
/// <summary>
/// Returns true when all childs return true
/// </summary>
public class Sequence : TreeNode
{
private List<TreeNode> childNodes;
public bool nodeState { get; private set; } = false;
public Sequence(List<TreeNode> childNodes) { this.childNodes = childNodes; }
public bool Evaluate()
{
foreach (TreeNode node in childNodes)
if (!node.Evaluate())
{
nodeState = false;
return false;
}
nodeState = true;
return true;
}
}
/// <summary>
/// Has only one child, negate it
/// </summary>
public class Inverter : TreeNode
{
private TreeNode nodeToInvert;
public bool nodeState { get; private set; } = false;
public Inverter(TreeNode nodeToInvert) { this.nodeToInvert = nodeToInvert; }
public bool Evaluate()
{
nodeState = !nodeToInvert.Evaluate();
return !nodeToInvert.Evaluate();
}
}
/// <summary>
/// Leaf of tree, returns delegate of bool function that is setted in it's constuctor
/// </summary>
public class ActionNode : TreeNode
{
public delegate bool ActionNodeDelegate();
private ActionNodeDelegate action;
public bool nodeState { get; private set; } = false;
public ActionNode(ActionNodeDelegate action)
{
this.action = action;
}
public bool Evaluate()
{
nodeState = action();
return action();
}
}
public static class DecisionTree
{
public static void Test()
{
while (true)
{
AITree tree = new AITree();
Console.ReadKey();
Console.Clear();
}
}
}
public class AITree
{
private float playerDistanceFromEnemy;
private int playerPower;
private ActionNode IsInAttackRange;
private ActionNode IsVisible;
private ActionNode EstimatePlayerPower;
private Sequence Attack;
private Inverter Patrol;
private Sequence Escape;
private Selector Root;
bool PlayerIsInAttackRange() => playerDistanceFromEnemy < 5;
bool PlayerIsVisible() => playerDistanceFromEnemy < 8;
bool PlayerIsTooPowerful() => playerPower > 3;
public AITree()
{
Random rnd = new Random();
playerDistanceFromEnemy = (float)rnd.Next(10, 100) / 10;
playerPower = rnd.Next(1, 6);
IsInAttackRange = new ActionNode(PlayerIsInAttackRange);
IsVisible = new ActionNode(PlayerIsVisible);
EstimatePlayerPower = new ActionNode(PlayerIsTooPowerful);
Attack = new Sequence(new List<TreeNode> { IsInAttackRange, IsVisible }); // Attack only when player is visible and is in attack range
Patrol = new Inverter(Attack); // Patrol only when not attacking
Escape = new Sequence(new List<TreeNode> { IsVisible, EstimatePlayerPower }); // Escape when player is visible and player is too powerful
Root = new Selector(new List<TreeNode> { Escape, Patrol, Attack }); // Escape has the biggest priority
Root.Evaluate();
ShowCommunicats();
}
private void ShowCommunicats()
{
StringBuilder sb = new StringBuilder();
Console.WriteLine($"Player distance: {playerDistanceFromEnemy}, Player power: {playerPower}");
sb.AppendLine();
Console.WriteLine(Patrol.nodeState ? "enemy will patrol" : "enemy will not patrol");
Console.WriteLine(Escape.nodeState ? "enemy escapes" : "enemy will not escape");
Console.WriteLine(IsVisible.nodeState ? "enemy see player" : "enemy dont see player");
Console.WriteLine(IsInAttackRange.nodeState ? "player is in the enemy attack distance" : "player is too far to hit");
Console.WriteLine(Attack.nodeState ? "enemy attacks" : "enemy will not attack");
}
}