behaviortree 0.5.0

This models the components of a behaviour tree


To use this package, put the following dependency into your project's dependencies section:

dub.json
dub.sdl

README

This repository is a library for creating a behavior tree.

Inspired by

[Towards a unified behavior trees framework for robot control, A. Marzinotto and M. Colledanchise and C. Smith and P. Ögren] (https://doi.org/10.1109/ICRA.2014.6907656)

and

[How Behavior Trees Modularize Hybrid Control Systems and Generalize Sequential Behavior Compositions, the Subsumption Architecture, and Decision Trees, Colledanchise, M; Ögren, P] (https://doi.org/10.1109/TRO.2016.2633567)

The behavior tree simplifies and modularize complex state machine description by redefining nodes as tasks. Doing so, the representation becomes a tree, whereas the state machine is generally a more complex graph.

The basic idea is the following:

There is a predefined well-known tree-internal ternary language. The decisions inside behavior tree are based on this language. As a consequence, the importer of this package has to adopt the Ternary type inside his own code.

There are three kinds of nodes: root, intermediates, leafs.

A rooted, directed tree is defined, where the root has only one child. Leaf nodes have no children but the user objects. User objects overload the function call operator. During the call of the root, all inputs are passed down to the leaves of the tree and then further on, to the user defined objects as input params of the opCall method. The result of the opCall method is of Ternary type, analogue to the cited papers.

Example usage:

import behaviortree; 
import std.traits; 
enum PedestrianTrafficLight {Green, Red}
enum CarTrafficLight {Green, Yellow, RedYellow, Red}

void proc(T, U...)(auto ref T trafficLight, auto ref U args) pure
{
	static if(is(T == Green))
		procGreen(args); 
	else static if(is(T == Red))
		procRed(args); 
	else
		assert(0); 
}
static void procGreen(U...)(auto ref U args)
{
	enum index = 0; 
	static if(is(U[index] == PedestrianTrafficLight))
		args[index] = U[index].Red; 
	else
		args[index] = U[index].Yellow;
}
static void procRed(U...)(auto ref U args)
{
	enum index = 0; 
	static if(is(U[index] == PedestrianTrafficLight))
		args[index] = U[index].Green; 
	else
		args[index] = U[index].RedYellow;
}

struct Green
{
	auto opCall(T)(ref T trafficLight, size_t time)
	{
		final switch(trafficLight)
		{
			foreach(i, member; EnumMembers!T)
			{
				
				case member: 
					static if(__traits(allMembers, T)[i] == __traits(identifier, typeof(this)))
					{
						if(time < 30)
							return Ternary.no; 
						else
							return Ternary.unknown;
					}
					else
					{
						return Ternary.no; 
					}
			}
		}
	}
}

struct Yellow
{
	auto opCall(T)(ref T trafficLight, size_t time)
	{
		final switch(trafficLight)
		{
			foreach(i, member; EnumMembers!T)
			{
				
				case member: 
					static if(__traits(allMembers, T)[i] == __traits(identifier, typeof(this)))
					{
						if(time < 10)
							return Ternary.no; 
						else
						{
							trafficLight = T.Red; 
							return Ternary.yes;
							}
					}
					else
					{
						return Ternary.no; 
					}
			}
		}
	}
}

struct Red
{
	auto opCall(T)(ref T trafficLight, size_t time)
	{
		final switch(trafficLight)
		{
			foreach(i, member; EnumMembers!T)
			{
				
				case member: 
					static if(__traits(allMembers, T)[i] == _traits(identifier, typeof(this)))
					{
						if(time < 30)
							return Ternary.no; 
						else
							return Ternary.unknown; 
					}
					else
					{
						return Ternary.no; 
					}
			}
		}
	}
}

struct RedYellow
{
	auto opCall(T)(ref T trafficLight, size_t time)
	{
		final switch(trafficLight)
		{
			foreach(i, member; EnumMembers!T)
			{
				
				case member: 
						static if(__traits(allMembers, T)[i] == _traits(identifier, typeof(this)))
					{
						if(time < 5)
							return Ternary.no; 
						else
						{
							trafficLight = T.Green; 
							return Ternary.yes; 
						}
					}
					else
					{
						return Ternary.no; 
					}
			}
		}
	}
}

PedestrianTrafficLight pedestrianTrafficLight; 
CarTrafficLight carTrafficLight; 
Green green; 
Red red; 
Yellow yellow; 
RedYellow redYellow; 

auto greenAction = Action!(Green, proc)(green); 
auto redAction = Action!(Red, proc)(red); 
auto yellowAction = Action!(Yellow, proc)(yellow); 
auto redYellowAction = Action!(RedYellow, proc)(redYellow); 

auto selector = Selector!(typeof(greenAction), typeof(redAction), typeof(yellowAction), typeof(redYellowAction))
	(greenAction, redAction, yellowAction, redYellowAction); 

auto root = Root!(typeof(selector))(selector); 
	
assert(pedestrianTrafficLight == PedestrianTrafficLight.Green);
root(pedestrianTrafficLight, 20); 
assert(pedestrianTrafficLight == PedestrianTrafficLight.Green);
root(pedestrianTrafficLight, 30);
assert(pedestrianTrafficLight == PedestrianTrafficLight.Red);
root(pedestrianTrafficLight, 20);
assert(pedestrianTrafficLight == PedestrianTrafficLight.Red);
root(pedestrianTrafficLight, 40);
assert(pedestrianTrafficLight == PedestrianTrafficLight.Green);

assert(carTrafficLight == CarTrafficLight.Green);
root(carTrafficLight, 20); 
assert(carTrafficLight == CarTrafficLight.Green);
root(carTrafficLight, 30);
assert(carTrafficLight == CarTrafficLight.Yellow);
root(carTrafficLight, 5);
assert(carTrafficLight == CarTrafficLight.Yellow);
root(carTrafficLight, 15);
assert(carTrafficLight == CarTrafficLight.Red);
root(carTrafficLight, 20);
assert(carTrafficLight == CarTrafficLight.Red);
root(carTrafficLight, 30);
assert(carTrafficLight == CarTrafficLight.RedYellow);
root(carTrafficLight, 1);
assert(carTrafficLight == CarTrafficLight.RedYellow);
root(carTrafficLight, 5);
assert(carTrafficLight == CarTrafficLight.Green);

See unittests for more examples.

Copyright: Copyright (c) 2016- Alexander Orlov. All rights reserved.

License: https://opensource.org/licenses/MIT, MIT License

Author: Alexander Orlov, sascha.orlov@gmail.com

Authors:Alexander Orlov

Dependencies: none

Versions:
0.5.0 2017-Jun-13
0.4.1 2017-Jun-07
0.3.1 2017-May-30
0.3.0 2017-May-30
0.2.0 2017-May-04
Show all 8 versions
Stats:

statistics are temporarily disabled.