diff --git a/README.md b/README.md index b28a972..e563c85 100644 --- a/README.md +++ b/README.md @@ -29,12 +29,12 @@ A behaviour tree is created through *BehaviourTreeBuilder*. The tree is returned ... - IBehaviourTreeNode tree; + IBehaviourTreeNode tree; public void Startup() { - var builder = new BehaviourTreeBuilder(); - this.tree = BehaviourTreeBuilder + var builder = new BehaviourTreeBuilder(); + this.tree = builder .Sequence("my-sequence") .Do(t => { @@ -117,11 +117,33 @@ Runs all child nodes in parallel. Continues to run until a required number of ch }) .End() -### Selector +### Priority Selector Runs child nodes in sequence until it finds one that *succeeds*. Succeeds when it finds the first child that *succeeds*. For child nodes that *fail* it moves forward to the next child node. While a child is *running* it stays on that child node without moving forward. - .Selector("my-selector") + .PrioritySelector("my-selector") + .Do(t => + { + // Action 1. + return BehaviourTreeStatus.Failure; // Fail, move onto next child. + }); + .Do(t => + { + // Action 2. + return BehaviourTreeStatus.Success; // Success, stop here. + }) + .Do(t => + { + // Action 3. + return BehaviourTreeStatus.Success; // Doesn't get this far. + }) + .End() + +### Probability Selector + +Runs a random child node until it *succeeds* or *fails*. It returns the status of the child that was run. While a child is *running* it stays on that child node without selecting a new child. + + .ProbabilitySelector("my-selector") .Do(t => { // Action 1. @@ -184,10 +206,10 @@ Behaviour trees can be nested to any depth, for example: Separately created sub-trees can be spliced into parent trees. This makes it easy to build behaviour trees from reusable components. - private IBehaviourTreeNode CreateSubTree() + private IBehaviourTreeNode CreateSubTree() { - var builder = new BehaviourTreeBuilder(); - return BehaviourTreeBuilder + var builder = new BehaviourTreeBuilder(); + return builder .Sequence("my-sub-tree") .Do(t => { @@ -205,8 +227,8 @@ Separately created sub-trees can be spliced into parent trees. This makes it eas public void Startup() { - var builder = new BehaviourTreeBuilder(); - this.tree = BehaviourTreeBuilder + var builder = new BehaviourTreeBuilder(); + this.tree = builder .Sequence("my-parent-sequence") .Splice(CreateSubTree()) // Splice the child tree in. .Splice(CreateSubTree()) // Splice again. diff --git a/src/BehaviourTreeBuilder.cs b/src/BehaviourTreeBuilder.cs index fc8b9ac..6ff2441 100644 --- a/src/BehaviourTreeBuilder.cs +++ b/src/BehaviourTreeBuilder.cs @@ -1,36 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { + using System; + using System.Collections.Generic; + + public class BehaviourTreeBuilder : BehaviourTreeBuilder + { + + } + /// /// Fluent API for building a behaviour tree. /// - public class BehaviourTreeBuilder + public class BehaviourTreeBuilder { /// /// Last node created. /// - private IBehaviourTreeNode curNode = null; + private IBehaviourTreeNode curNode = null; /// /// Stack node nodes that we are build via the fluent API. /// - private Stack parentNodeStack = new Stack(); + private Stack> parentNodeStack = new Stack>(); /// /// Create an action node. /// - public BehaviourTreeBuilder Do(string name, Func fn) + public BehaviourTreeBuilder Do(string name, Func fn) { if (parentNodeStack.Count <= 0) { throw new ApplicationException("Can't create an unnested ActionNode, it must be a leaf node."); } - var actionNode = new ActionNode(name, fn); + var actionNode = new ActionNode(name, fn); parentNodeStack.Peek().AddChild(actionNode); return this; } @@ -38,7 +41,7 @@ public BehaviourTreeBuilder Do(string name, Func /// /// Like an action node... but the function can return true/false and is mapped to success/failure. /// - public BehaviourTreeBuilder Condition(string name, Func fn) + public BehaviourTreeBuilder Condition(string name, Func fn) { return Do(name, t => fn(t) ? BehaviourTreeStatus.Success : BehaviourTreeStatus.Failure); } @@ -46,9 +49,9 @@ public BehaviourTreeBuilder Condition(string name, Func fn) /// /// Create an inverter node that inverts the success/failure of its children. /// - public BehaviourTreeBuilder Inverter(string name) + public BehaviourTreeBuilder Inverter(string name) { - var inverterNode = new InverterNode(name); + var inverterNode = new InverterNode(name); if (parentNodeStack.Count > 0) { @@ -62,9 +65,9 @@ public BehaviourTreeBuilder Inverter(string name) /// /// Create a sequence node. /// - public BehaviourTreeBuilder Sequence(string name) + public BehaviourTreeBuilder Sequence(string name) { - var sequenceNode = new SequenceNode(name); + var sequenceNode = new SequenceNode(name); if (parentNodeStack.Count > 0) { @@ -78,9 +81,9 @@ public BehaviourTreeBuilder Sequence(string name) /// /// Create a parallel node. /// - public BehaviourTreeBuilder Parallel(string name, int numRequiredToFail, int numRequiredToSucceed) + public BehaviourTreeBuilder Parallel(string name, int numRequiredToFail, int numRequiredToSucceed) { - var parallelNode = new ParallelNode(name, numRequiredToFail, numRequiredToSucceed); + var parallelNode = new ParallelNode(name, numRequiredToFail, numRequiredToSucceed); if (parentNodeStack.Count > 0) { @@ -94,9 +97,25 @@ public BehaviourTreeBuilder Parallel(string name, int numRequiredToFail, int num /// /// Create a selector node. /// - public BehaviourTreeBuilder Selector(string name) + public BehaviourTreeBuilder PrioritySelector(string name) + { + var selectorNode = new PrioritySelectorNode(name); + + if (parentNodeStack.Count > 0) + { + parentNodeStack.Peek().AddChild(selectorNode); + } + + parentNodeStack.Push(selectorNode); + return this; + } + + /// + /// Create a selector node. + /// + public BehaviourTreeBuilder PropabilitySelector(string name) { - var selectorNode = new SelectorNode(name); + var selectorNode = new ProbabilitySelectorNode(name); if (parentNodeStack.Count > 0) { @@ -110,7 +129,7 @@ public BehaviourTreeBuilder Selector(string name) /// /// Splice a sub tree into the parent tree. /// - public BehaviourTreeBuilder Splice(IBehaviourTreeNode subTree) + public BehaviourTreeBuilder Splice(IBehaviourTreeNode subTree) { if (subTree == null) { @@ -129,7 +148,7 @@ public BehaviourTreeBuilder Splice(IBehaviourTreeNode subTree) /// /// Build the actual tree. /// - public IBehaviourTreeNode Build() + public IBehaviourTreeNode Build() { if (curNode == null) { @@ -141,7 +160,7 @@ public IBehaviourTreeNode Build() /// /// Ends a sequence of children. /// - public BehaviourTreeBuilder End() + public BehaviourTreeBuilder End() { curNode = parentNodeStack.Pop(); return this; diff --git a/src/BehaviourTreeStatus.cs b/src/BehaviourTreeStatus.cs index 1d09083..2564922 100644 --- a/src/BehaviourTreeStatus.cs +++ b/src/BehaviourTreeStatus.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { /// /// The return type when invoking behaviour tree nodes. diff --git a/src/IBehaviourTreeNode.cs b/src/IBehaviourTreeNode.cs index 9114f9b..4c6e40a 100644 --- a/src/IBehaviourTreeNode.cs +++ b/src/IBehaviourTreeNode.cs @@ -1,18 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { + public interface IBehaviourTreeNode : IBehaviourTreeNode + { } + /// /// Interface for behaviour tree nodes. /// - public interface IBehaviourTreeNode + public interface IBehaviourTreeNode { /// /// Update the time of the behaviour tree. /// - BehaviourTreeStatus Tick(TimeData time); + BehaviourTreeStatus Tick(TTickData time); } } diff --git a/src/IParentBehaviourTreeNode.cs b/src/IParentBehaviourTreeNode.cs index cf5ea86..c12b576 100644 --- a/src/IParentBehaviourTreeNode.cs +++ b/src/IParentBehaviourTreeNode.cs @@ -1,18 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { + public interface IParentBehaviourTreeNode : IParentBehaviourTreeNode + { } + /// /// Interface for behaviour tree nodes. /// - public interface IParentBehaviourTreeNode : IBehaviourTreeNode + public interface IParentBehaviourTreeNode : IBehaviourTreeNode { /// /// Add a child to the parent node. /// - void AddChild(IBehaviourTreeNode child); + void AddChild(IBehaviourTreeNode child); } } diff --git a/src/Nodes/ActionNode.cs b/src/Nodes/ActionNode.cs index b07b47c..5b676ff 100644 --- a/src/Nodes/ActionNode.cs +++ b/src/Nodes/ActionNode.cs @@ -1,14 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { + using System; + + public class ActionNode : ActionNode + { + public ActionNode(string name, Func fn) : base(name, fn) + { + } + } + /// /// A behaviour tree leaf node for running an action. /// - public class ActionNode : IBehaviourTreeNode + public class ActionNode : IBehaviourTreeNode { /// /// The name of the node. @@ -18,16 +22,16 @@ public class ActionNode : IBehaviourTreeNode /// /// Function to invoke for the action. /// - private Func fn; - + private Func fn; + - public ActionNode(string name, Func fn) + public ActionNode(string name, Func fn) { - this.name=name; - this.fn=fn; + this.name = name; + this.fn = fn; } - public BehaviourTreeStatus Tick(TimeData time) + public BehaviourTreeStatus Tick(TTickData time) { return fn(time); } diff --git a/src/Nodes/InverterNode.cs b/src/Nodes/InverterNode.cs index 4fdd233..8006559 100644 --- a/src/Nodes/InverterNode.cs +++ b/src/Nodes/InverterNode.cs @@ -1,14 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { + using System; + + public class InverterNode : InverterNode + { + public InverterNode(string name) : base(name) + { + } + } + /// /// Decorator node that inverts the success/failure of its child. /// - public class InverterNode : IParentBehaviourTreeNode + public class InverterNode : IParentBehaviourTreeNode { /// /// Name of the node. @@ -18,14 +22,14 @@ public class InverterNode : IParentBehaviourTreeNode /// /// The child to be inverted. /// - private IBehaviourTreeNode childNode; + private IBehaviourTreeNode childNode; public InverterNode(string name) { this.name = name; } - public BehaviourTreeStatus Tick(TimeData time) + public BehaviourTreeStatus Tick(TTickData time) { if (childNode == null) { @@ -50,7 +54,7 @@ public BehaviourTreeStatus Tick(TimeData time) /// /// Add a child to the parent node. /// - public void AddChild(IBehaviourTreeNode child) + public void AddChild(IBehaviourTreeNode child) { if (this.childNode != null) { diff --git a/src/Nodes/ParallelNode.cs b/src/Nodes/ParallelNode.cs index 0021841..450dd04 100644 --- a/src/Nodes/ParallelNode.cs +++ b/src/Nodes/ParallelNode.cs @@ -1,14 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { + using System.Collections.Generic; + + public class ParallelNode : ParallelNode + { + public ParallelNode(string name, int numRequiredToFail, int numRequiredToSucceed) : base(name, numRequiredToFail, numRequiredToSucceed) + { + } + } + /// /// Runs childs nodes in parallel. /// - public class ParallelNode : IParentBehaviourTreeNode + public class ParallelNode : IParentBehaviourTreeNode { /// /// Name of the node. @@ -18,7 +22,7 @@ public class ParallelNode : IParentBehaviourTreeNode /// /// List of child nodes. /// - private List children = new List(); + private List> children = new List>(); /// /// Number of child failures required to terminate with failure. @@ -37,7 +41,7 @@ public ParallelNode(string name, int numRequiredToFail, int numRequiredToSucceed this.numRequiredToSucceed = numRequiredToSucceed; } - public BehaviourTreeStatus Tick(TimeData time) + public BehaviourTreeStatus Tick(TTickData time) { var numChildrenSuceeded = 0; var numChildrenFailed = 0; @@ -65,7 +69,7 @@ public BehaviourTreeStatus Tick(TimeData time) return BehaviourTreeStatus.Running; } - public void AddChild(IBehaviourTreeNode child) + public void AddChild(IBehaviourTreeNode child) { children.Add(child); } diff --git a/src/Nodes/SelectorNode.cs b/src/Nodes/PrioritySelectorNode.cs similarity index 57% rename from src/Nodes/SelectorNode.cs rename to src/Nodes/PrioritySelectorNode.cs index de576af..452f689 100644 --- a/src/Nodes/SelectorNode.cs +++ b/src/Nodes/PrioritySelectorNode.cs @@ -1,14 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { + using System.Collections.Generic; + + public class PrioritySelectorNode : PrioritySelectorNode + { + public PrioritySelectorNode(string name) : base(name) + { + } + } /// /// Selects the first node that succeeds. Tries successive nodes until it finds one that doesn't fail. /// - public class SelectorNode : IParentBehaviourTreeNode + public class PrioritySelectorNode : IParentBehaviourTreeNode { /// /// The name of the node. @@ -18,14 +21,14 @@ public class SelectorNode : IParentBehaviourTreeNode /// /// List of child nodes. /// - private List children = new List(); //todo: optimization, bake this to an array. + private List> children = new List>(); //todo: optimization, bake this to an array. - public SelectorNode(string name) + public PrioritySelectorNode(string name) { this.name = name; } - public BehaviourTreeStatus Tick(TimeData time) + public BehaviourTreeStatus Tick(TTickData time) { foreach (var child in children) { @@ -42,7 +45,7 @@ public BehaviourTreeStatus Tick(TimeData time) /// /// Add a child node to the selector. /// - public void AddChild(IBehaviourTreeNode child) + public void AddChild(IBehaviourTreeNode child) { children.Add(child); } diff --git a/src/Nodes/ProbabilitySelectorNode.cs b/src/Nodes/ProbabilitySelectorNode.cs new file mode 100644 index 0000000..6c335c9 --- /dev/null +++ b/src/Nodes/ProbabilitySelectorNode.cs @@ -0,0 +1,49 @@ +namespace FluentBehaviourTree +{ + using System; + using System.Collections.Generic; + + public class ProbabilitySelectorNode : ProbabilitySelectorNode + { + public ProbabilitySelectorNode(string name) : base(name) + { + } + } + + public class ProbabilitySelectorNode : IParentBehaviourTreeNode + { + private readonly static Random rng = new Random(); + + /// + /// The name of the node. + /// + private readonly string name; + + private IBehaviourTreeNode selectedNode; + private BehaviourTreeStatus childStatus; + /// + /// List of child nodes. + /// + private readonly List> children = new List>(); //todo: optimization, bake this to an array. + + public ProbabilitySelectorNode(string name) + { + this.name = name; + } + + public void AddChild(IBehaviourTreeNode child) + { + children.Add(child); + } + + public BehaviourTreeStatus Tick(TTickData time) + { + if (childStatus != BehaviourTreeStatus.Running) + { + selectedNode = children[rng.Next(children.Count)]; + } + childStatus = selectedNode.Tick(time); + return childStatus; + } + } +} diff --git a/src/Nodes/SequenceNode.cs b/src/Nodes/SequenceNode.cs index e4394e8..2d842ce 100644 --- a/src/Nodes/SequenceNode.cs +++ b/src/Nodes/SequenceNode.cs @@ -1,14 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { + using System.Collections.Generic; + + public class SequenceNode : SequenceNode + { + public SequenceNode(string name) : base(name) + { + } + } + /// /// Runs child nodes in sequence, until one fails. /// - public class SequenceNode : IParentBehaviourTreeNode + public class SequenceNode : IParentBehaviourTreeNode { /// /// Name of the node. @@ -18,14 +22,14 @@ public class SequenceNode : IParentBehaviourTreeNode /// /// List of child nodes. /// - private List children = new List(); //todo: this could be optimized as a baked array. + private List> children = new List>(); //todo: this could be optimized as a baked array. public SequenceNode(string name) { this.name = name; } - public BehaviourTreeStatus Tick(TimeData time) + public BehaviourTreeStatus Tick(TTickData time) { foreach (var child in children) { @@ -42,7 +46,7 @@ public BehaviourTreeStatus Tick(TimeData time) /// /// Add a child to the sequence. /// - public void AddChild(IBehaviourTreeNode child) + public void AddChild(IBehaviourTreeNode child) { children.Add(child); } diff --git a/src/TimeData.cs b/src/TimeData.cs index 058b2f6..49b986e 100644 --- a/src/TimeData.cs +++ b/src/TimeData.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace FluentBehaviourTree +namespace FluentBehaviourTree { /// /// Represents time. Used to pass time values to behaviour tree nodes. diff --git a/src/fluent-behaviour-tree.csproj b/src/fluent-behaviour-tree.csproj index b92435e..e12f42b 100644 --- a/src/fluent-behaviour-tree.csproj +++ b/src/fluent-behaviour-tree.csproj @@ -46,7 +46,8 @@ - + + diff --git a/tests/ActionNodeTests.cs b/tests/ActionNodeTests.cs index b07f47b..7ea7557 100644 --- a/tests/ActionNodeTests.cs +++ b/tests/ActionNodeTests.cs @@ -1,12 +1,8 @@ -using FluentBehaviourTree; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Xunit; - -namespace tests +namespace tests { + using FluentBehaviourTree; + using Xunit; + public class ActionNodeTests { [Fact] @@ -15,9 +11,9 @@ public void can_run_action() var time = new TimeData(); var invokeCount = 0; - var testObject = + var testObject = new ActionNode( - "some-action", + "some-action", t => { Assert.Equal(time, t); @@ -28,7 +24,7 @@ public void can_run_action() ); Assert.Equal(BehaviourTreeStatus.Running, testObject.Tick(time)); - Assert.Equal(1, invokeCount); + Assert.Equal(1, invokeCount); } } } diff --git a/tests/BehaviourTreeBuilderTests.cs b/tests/BehaviourTreeBuilderTests.cs index 0c6143c..9bf017c 100644 --- a/tests/BehaviourTreeBuilderTests.cs +++ b/tests/BehaviourTreeBuilderTests.cs @@ -1,12 +1,9 @@ -using FluentBehaviourTree; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Xunit; - -namespace tests +namespace tests { + using FluentBehaviourTree; + using System; + using Xunit; + public class BehaviourTreeBuilderTests { BehaviourTreeBuilder testObject; @@ -50,11 +47,11 @@ public void can_create_inverter_node() var node = testObject .Inverter("some-inverter") - .Do("some-node", t =>BehaviourTreeStatus.Success) + .Do("some-node", t => BehaviourTreeStatus.Success) .End() .Build(); - Assert.IsType(node); + Assert.IsType>(node); Assert.Equal(BehaviourTreeStatus.Failure, node.Tick(new TimeData())); } @@ -83,7 +80,7 @@ public void condition_is_syntactic_sugar_for_do() .End() .Build(); - Assert.IsType(node); + Assert.IsType>(node); Assert.Equal(BehaviourTreeStatus.Failure, node.Tick(new TimeData())); } @@ -100,7 +97,7 @@ public void can_invert_an_inverter() .End() .Build(); - Assert.IsType(node); + Assert.IsType>(node); Assert.Equal(BehaviourTreeStatus.Success, node.Tick(new TimeData())); } @@ -129,7 +126,7 @@ public void can_create_a_sequence() var sequence = testObject .Sequence("some-sequence") - .Do("some-action-1", t => + .Do("some-action-1", t => { ++invokeCount; return BehaviourTreeStatus.Success; @@ -142,7 +139,7 @@ public void can_create_a_sequence() .End() .Build(); - Assert.IsType(sequence); + Assert.IsType>(sequence); Assert.Equal(BehaviourTreeStatus.Success, sequence.Tick(new TimeData())); Assert.Equal(2, invokeCount); } @@ -169,20 +166,20 @@ public void can_create_parallel() .End() .Build(); - Assert.IsType(parallel); + Assert.IsType>(parallel); Assert.Equal(BehaviourTreeStatus.Success, parallel.Tick(new TimeData())); Assert.Equal(2, invokeCount); } [Fact] - public void can_create_selector() + public void can_create_priority_selector() { Init(); var invokeCount = 0; var parallel = testObject - .Selector("some-selector") + .PrioritySelector("some-selector") .Do("some-action-1", t => { ++invokeCount; @@ -196,11 +193,46 @@ public void can_create_selector() .End() .Build(); - Assert.IsType(parallel); + Assert.IsType>(parallel); Assert.Equal(BehaviourTreeStatus.Success, parallel.Tick(new TimeData())); Assert.Equal(2, invokeCount); } + [Fact] + public void can_create_probability_selector() + { + Init(); + + var invokeCount = 0; + + var propability = testObject + .PropabilitySelector("some-selector") + .Do("some-action-1", t => + { + ++invokeCount; + return BehaviourTreeStatus.Success; + }) + .Do("some-action-2", t => + { + ++invokeCount; + return BehaviourTreeStatus.Success; + }) + .Do("some-action-3", t => + { + ++invokeCount; + return BehaviourTreeStatus.Success; + }) + .End() + .Build(); + + Assert.IsType>(propability); + for (int i = 0; i < 100; i++) + { + Assert.Equal(BehaviourTreeStatus.Success, propability.Tick(new TimeData())); + } + Assert.Equal(100, invokeCount); + } + [Fact] public void can_splice_sub_tree() { @@ -220,7 +252,7 @@ public void can_splice_sub_tree() var tree = testObject .Sequence("parent-tree") - .Splice(spliced) + .Splice(spliced) .End() .Build(); @@ -252,5 +284,16 @@ public void splicing_an_unnested_sub_tree_throws_exception() .Splice(spliced); }); } + + [Fact] + public void splicing_null_throws_exception() + { + Init(); + + Assert.Throws(() => + { + testObject.Splice(null); + }); + } } } diff --git a/tests/InverterNodeTests.cs b/tests/InverterNodeTests.cs index c362eb7..e37d0d7 100644 --- a/tests/InverterNodeTests.cs +++ b/tests/InverterNodeTests.cs @@ -1,13 +1,10 @@ -using FluentBehaviourTree; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Xunit; - -namespace tests +namespace tests { + using FluentBehaviourTree; + using Moq; + using System; + using Xunit; + public class InverterNodeTests { InverterNode testObject; @@ -93,7 +90,7 @@ public void adding_more_than_a_single_child_throws_exception() testObject.AddChild(mockChildNode1.Object); var mockChildNode2 = new Mock(); - Assert.Throws(() => + Assert.Throws(() => testObject.AddChild(mockChildNode2.Object) ); } diff --git a/tests/ParallelNodeTests.cs b/tests/ParallelNodeTests.cs index ac36360..8a83567 100644 --- a/tests/ParallelNodeTests.cs +++ b/tests/ParallelNodeTests.cs @@ -1,13 +1,9 @@ -using FluentBehaviourTree; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Xunit; - -namespace tests +namespace tests { + using FluentBehaviourTree; + using Moq; + using Xunit; + public class ParallelNodeTests { ParallelNode testObject; diff --git a/tests/SelectorNodeTests.cs b/tests/PrioritySelectorNodeTests.cs similarity index 91% rename from tests/SelectorNodeTests.cs rename to tests/PrioritySelectorNodeTests.cs index 97a5d60..b54f22b 100644 --- a/tests/SelectorNodeTests.cs +++ b/tests/PrioritySelectorNodeTests.cs @@ -1,20 +1,16 @@ -using FluentBehaviourTree; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Xunit; - -namespace tests +namespace tests { - public class SelectorNodeTests + using FluentBehaviourTree; + using Moq; + using Xunit; + + public class PrioritySelectorNodeTests { - SelectorNode testObject; + PrioritySelectorNode testObject; void Init() { - testObject = new SelectorNode("some-selector"); + testObject = new PrioritySelectorNode("some-selector"); } [Fact] diff --git a/tests/ProbabilitySelectorNodeTests.cs b/tests/ProbabilitySelectorNodeTests.cs new file mode 100644 index 0000000..d854a9e --- /dev/null +++ b/tests/ProbabilitySelectorNodeTests.cs @@ -0,0 +1,52 @@ +namespace tests +{ + using FluentBehaviourTree; + using Moq; + using Xunit; + + public class ProbabilitySelectorNodeTests + { + ProbabilitySelectorNode testObject; + + void Init() + { + testObject = new ProbabilitySelectorNode("some-selector"); + } + + [Fact] + public void runs_the_first_node_if_it_succeeds() + { + Init(); + + var time = new TimeData(); + + var mockChild1 = new Mock(); + mockChild1 + .Setup(m => m.Tick(time)) + .Returns(BehaviourTreeStatus.Success); + + var mockChild2 = new Mock(); + mockChild2 + .Setup(m => m.Tick(time)) + .Returns(BehaviourTreeStatus.Running); + + var mockChild3 = new Mock(); + mockChild3 + .Setup(m => m.Tick(time)) + .Returns(BehaviourTreeStatus.Failure); + + testObject.AddChild(mockChild1.Object); + testObject.AddChild(mockChild2.Object); + testObject.AddChild(mockChild3.Object); + + for (int i = 1; i < 100; i++) + { + Assert.InRange(testObject.Tick(time), BehaviourTreeStatus.Success, BehaviourTreeStatus.Running); + } + mockChild1.Verify(m => m.Tick(time), Times.Between(0, 33, Range.Inclusive)); + mockChild2.Verify(m => m.Tick(time), Times.Between(33, 100, Range.Inclusive)); + mockChild3.Verify(m => m.Tick(time), Times.Between(0, 33, Range.Inclusive)); + } + } +} + diff --git a/tests/SequenceNodeTests.cs b/tests/SequenceNodeTests.cs index 76bd000..08fab2f 100644 --- a/tests/SequenceNodeTests.cs +++ b/tests/SequenceNodeTests.cs @@ -1,13 +1,9 @@ -using FluentBehaviourTree; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Xunit; - -namespace tests +namespace tests { + using FluentBehaviourTree; + using Moq; + using Xunit; + public class SequenceNodeTests { SequenceNode testObject; @@ -16,7 +12,7 @@ void Init() { testObject = new SequenceNode("some-sequence"); } - + [Fact] public void can_run_all_children_in_order() { diff --git a/tests/tests.csproj b/tests/tests.csproj index 282ea3c..6c3c7e7 100644 --- a/tests/tests.csproj +++ b/tests/tests.csproj @@ -54,8 +54,9 @@ + - +