public void TestScriptB() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestRunningAction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <TestState>("ParserTests\\testScriptB.ygg"); Assert.AreEqual(0, context.Errors.Count); Assert.AreEqual(3, context.TopmostNodeCount); var root = context.Instantiate("root"); Assert.IsNotNull(root); var manager = new BehaviourTree(root); AssertScriptB(manager); manager.Reset(); AssertScriptB(manager); }
public void DynamicTestScript() { var config = new YggParserConfig(); config.ReplaceObjectStateWithDynamic = true; config.ScriptNamespaces.Add(typeof(Result).Namespace); config.ReferenceAssemblyPaths.Add(typeof(Node).Assembly.Location); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestDynamicFunction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <object>("ParserTests\\testScriptC.ygg"); Assert.AreEqual(0, context.Errors.Count); Assert.AreEqual(1, context.TopmostNodeCount); var root = context.Instantiate("root"); Assert.IsNotNull(root); dynamic state = new ExpandoObject(); var manager = new BehaviourTree(root); manager.Update(state); Assert.AreEqual(1UL, manager.TickCount); Assert.AreEqual(Result.Success, manager.Result); Assert.AreEqual(1, state.A); }
public void Setup() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestRunningAction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var scriptPath = Path.Combine(Environment.CurrentDirectory, "BenchmarkScripts\\testScriptB.ygg"); var context = parser.BuildFromFiles <TestState>(scriptPath); if (context.Errors.Count > 0) { throw new Exception(context.Errors[0].Message); } _tree = new BehaviourTree(context.Instantiate("root")); _state = new TestState(); // Warmup. while (_tree.TickCount == 0) { _tree.Update(_state); } _state = new TestState(); }
public void Start() { // You need to pass the "netstandard" assembly reference when in Unity. // This is not necessary on a standalone .NET Core 2.2 console program. // You can also pass more references and namespace usings for the C# snippets if needed. var netstandard = AppDomain.CurrentDomain.GetAssemblies().First(n => n.GetName().Name == "netstandard").Location; var config = new YggParserConfig(); config.ReferenceAssemblyPaths.Add(netstandard); // You choose which currently loaded assemblies are searched for types deriving from the Node class. config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(ExampleCustomAction).Assembly.GetName().Name); // Create the compiler and parser. They can be reused for multiple parsings. // The only state maintained by a parser instance is the initial config passed on its constructor. var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); // The parser gives back a behaviour tree definition, which can be used to instantiate the tree multiple times (or any node within by its GUID). // The definition also keeps track of errors that might have happened during parsing, and other debug information. // Node GUIDs can be assigned manually on the script files themselves. If they don't have one, the parser assigns a random one. They must be unique // withing the context of all the files parsed. The same goes for 'TypeDefs'. You can reference TypeDefs between files. var scriptFilePath = Path.Combine(Application.streamingAssetsPath, "exampleScript.ygg"); var definition = parser.BuildFromFiles <ExampleState>(scriptFilePath); // The errors often have useful information to debug them. In this example there should be none. if (definition.Errors.Count > 0) { throw new Exception("Errors while parsing script files."); } // You can instantiate any node defined on the parsed scripts by passing their GUID. // This creates an instance of that node and all its descendants. // This also calls Initialize() on the instantiated nodes (in a depth-first traversal, if that matters to you). // The script files themselves have no explicit root, so you must instantiate the tree using the desired root node's GUID. var root = definition.Instantiate("root"); // The behaviour tree object serves as a manager that handles the node's execution and async continuations. // The behaviour tree is what keeps the "continuation state" of the tree. This means you can reuse a single "root" node instance // on multiple behaviour trees that get updated separately without issues. This is only valid for the node types that Yggdrasil comes // with by default. If you create new node types inheriting from the Node class, you must make sure they don't keep any state themselves. _tree = new BehaviourTree(root); // Some example state object passed to the tree's nodes. Kept as a field here to allocate it only once. _state = new ExampleState(); // You can pre-pool some coroutines. Otherwise, the first run of the tree will allocate as necessary. // They get recycled automatically. The amount of coroutines needed depends on how deep an async Coroutine method stack call can get during execution. // Unless the nodes use multiple nested async Coroutine methods, this means only one Coroutine<Result> per node on the deepest possible branch of the tree. // Parallel and Interrupt nodes can complicate the math, since they create multiple simultaneously active branches. // Pre-pooling yourself like this is optional. It'll create as necessary during execution. Yggdrasil.Coroutines.Coroutine.Pool.PrePool(20); Yggdrasil.Coroutines.Coroutine <Result> .Pool.PrePool(20); }
public void RepeatedTypeDefTest() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestRunningAction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <TestState>("ParserTests\\repeatedTypeDefTest.ygg"); Assert.IsTrue(context.Errors.Count > 0); }
public void MalformedCSharpTest() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(TestRunningAction).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <TestState>("ParserTests\\malformedCSharpTest.ygg"); Assert.IsTrue(context.Errors.Count > 0); Assert.IsTrue(context.Errors.Any(e => e.Diagnostics.Count > 0)); }
public void DynamicFunctionCompilationTest() { const string textA = @"state.A >= state.B || state.C <= state.D"; const string textB = @"state.A >= state.B || state.C >= state.D"; const string textC = @"state.FirstName != state.SecondName && state.FirstName == state.ThirdName"; const string textD = @" dynamic state = new ExpandoObject(); state.A = 1; state.B = 2; state.C = 3; state.D = 4; state.E = 5; return state;"; var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); var property = typeof(TestDynamicConditionDouble).GetProperty("Conditional"); var propertySingle = typeof(TestDynamicConditionSingle).GetProperty("Conditional"); var conditionA = new TestDynamicConditionDouble(); var conditionB = new TestDynamicConditionDouble(); var conditionC = new TestDynamicConditionDouble(); var conditionD = new TestDynamicConditionSingle(); var dA = new ScriptedFunctionDefinition { Guid = Guid.NewGuid().ToString().Replace("-", ""), FunctionProperty = property, FunctionText = textA, ReplaceObjectWithDynamic = true }; var dB = new ScriptedFunctionDefinition { Guid = Guid.NewGuid().ToString().Replace("-", ""), FunctionProperty = property, FunctionText = textB, ReplaceObjectWithDynamic = true }; var dC = new ScriptedFunctionDefinition { Guid = Guid.NewGuid().ToString().Replace("-", ""), FunctionProperty = property, FunctionText = textC, ReplaceObjectWithDynamic = true }; var dD = new ScriptedFunctionDefinition { Guid = Guid.NewGuid().ToString().Replace("-", ""), FunctionProperty = propertySingle, FunctionText = textD, ReplaceObjectWithDynamic = true }; var compiler = new YggCompiler(); var definitions = new List <ScriptedFunctionDefinition> { dA, dB, dC, dD }; var compilation = compiler.Compile <object>(config.ScriptNamespaces, config.ReferenceAssemblyPaths, definitions); Assert.AreEqual(0, compilation.Errors.Count); Assert.AreEqual(4, compilation.GuidFunctionMap.Count); Assert.AreEqual(1, compilation.GuidFunctionMap[dA.Guid].Count); Assert.AreEqual(1, compilation.GuidFunctionMap[dB.Guid].Count); Assert.AreEqual(1, compilation.GuidFunctionMap[dC.Guid].Count); Assert.AreEqual(1, compilation.GuidFunctionMap[dD.Guid].Count); dynamic state = new ExpandoObject(); state.A = 1; state.B = 2; state.C = 3; state.D = 4; state.E = 5; state.FirstName = "dimitri"; state.SecondName = "edelgard"; state.ThirdName = "dimitri"; compilation.GuidFunctionMap[dA.Guid][0].SetFunctionPropertyValue(conditionA); Assert.IsNotNull(conditionA.Conditional); Assert.IsTrue(conditionA.Conditional(state)); compilation.GuidFunctionMap[dB.Guid][0].SetFunctionPropertyValue(conditionB); Assert.IsNotNull(conditionB.Conditional); Assert.IsFalse(conditionB.Conditional(state)); compilation.GuidFunctionMap[dC.Guid][0].SetFunctionPropertyValue(conditionC); Assert.IsNotNull(conditionC.Conditional); Assert.IsTrue(conditionC.Conditional(state)); compilation.GuidFunctionMap[dD.Guid][0].SetFunctionPropertyValue(conditionD); Assert.IsNotNull(conditionD.Conditional); var result = conditionD.Conditional(); Assert.IsNotNull(result); Assert.AreEqual(1, result.A); Assert.AreEqual(2, result.B); Assert.AreEqual(3, result.C); Assert.AreEqual(4, result.D); Assert.AreEqual(5, result.E); }
public void FunctionCompilationTest() { const string textA = @"state.A >= state.B || state.C <= state.D"; const string textB = @"state.A >= state.B || state.C >= state.D"; const string textC = @"state.FirstName != state.SecondName && state.FirstName == state.ThirdName"; var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(TestState).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); var conditionA = new Condition(); var conditionB = new Condition(); var conditionC = new Condition(); var property = typeof(Condition).GetProperty("Conditional"); var dA = new ScriptedFunctionDefinition { Guid = Guid.NewGuid().ToString().Replace("-", ""), FunctionProperty = property, FunctionText = textA }; var dB = new ScriptedFunctionDefinition { Guid = Guid.NewGuid().ToString().Replace("-", ""), FunctionProperty = property, FunctionText = textB }; var dC = new ScriptedFunctionDefinition { Guid = Guid.NewGuid().ToString().Replace("-", ""), FunctionProperty = property, FunctionText = textC }; var compiler = new YggCompiler(); var definitions = new List <ScriptedFunctionDefinition> { dA, dB, dC }; var compilation = compiler.Compile <TestState>(config.ScriptNamespaces, config.ReferenceAssemblyPaths, definitions); Assert.AreEqual(0, compilation.Errors.Count); Assert.AreEqual(3, compilation.GuidFunctionMap.Count); Assert.AreEqual(1, compilation.GuidFunctionMap[dA.Guid].Count); Assert.AreEqual(1, compilation.GuidFunctionMap[dB.Guid].Count); Assert.AreEqual(1, compilation.GuidFunctionMap[dC.Guid].Count); var state = new TestState(); state.A = 1; state.B = 2; state.C = 3; state.D = 4; state.E = 5; state.FirstName = "dimitri"; state.SecondName = "edelgard"; state.ThirdName = "dimitri"; compilation.GuidFunctionMap[dA.Guid][0].SetFunctionPropertyValue(conditionA); Assert.IsNotNull(conditionA.Conditional); Assert.IsTrue(conditionA.Conditional(state)); compilation.GuidFunctionMap[dB.Guid][0].SetFunctionPropertyValue(conditionB); Assert.IsNotNull(conditionB.Conditional); Assert.IsFalse(conditionB.Conditional(state)); compilation.GuidFunctionMap[dC.Guid][0].SetFunctionPropertyValue(conditionC); Assert.IsNotNull(conditionC.Conditional); Assert.IsTrue(conditionC.Conditional(state)); }
public void ScriptStructureTest() { var config = new YggParserConfig(); config.NodeTypeAssemblies.Add(typeof(Node).Assembly.GetName().Name); config.NodeTypeAssemblies.Add(typeof(ParameterizedTestNode).Assembly.GetName().Name); var compiler = new YggCompiler(); var parser = new YggParser(config, compiler); var context = parser.BuildFromFiles <TestState>("ParserTests\\testScriptA.ygg"); Assert.AreEqual(0, context.Errors.Count); Assert.AreEqual(4, context.TopmostNodeCount); // MyCustomTypeA var nodeA = context.Instantiate("A"); Assert.AreEqual("A", nodeA.Guid); Assert.AreEqual(typeof(Filter), nodeA.GetType()); Assert.AreEqual(1, nodeA.Children.Count); var nodeB = nodeA.Children[0]; Assert.AreEqual("B", nodeB.Guid); Assert.AreEqual(typeof(Sequence), nodeB.GetType()); Assert.AreEqual(3, nodeB.Children.Count); var nodeC = nodeB.Children[0]; Assert.AreEqual("C", nodeC.Guid); Assert.AreEqual(typeof(Condition), nodeC.GetType()); Assert.AreEqual(0, nodeC.Children.Count); var nodeD = nodeB.Children[1]; Assert.AreEqual("D", nodeD.Guid); Assert.AreEqual(typeof(Condition), nodeD.GetType()); Assert.AreEqual(0, nodeD.Children.Count); var nodeE = nodeB.Children[2]; Assert.AreEqual("E", nodeE.Guid); Assert.AreEqual(typeof(Inverter), nodeE.GetType()); Assert.AreEqual(1, nodeE.Children.Count); var nodeF = nodeE.Children[0]; Assert.AreEqual("F", nodeF.Guid); Assert.AreEqual(typeof(Condition), nodeF.GetType()); Assert.AreEqual(0, nodeF.Children.Count); // MyCustomTypeB var nodeG = context.Instantiate("G"); Assert.AreEqual("G", nodeG.Guid); Assert.AreEqual(typeof(Sequence), nodeG.GetType()); Assert.AreEqual(2, nodeG.Children.Count); var nodeH = nodeG.Children[0]; Assert.AreEqual("H", nodeH.Guid); Assert.AreEqual(typeof(Filter), nodeH.GetType()); CheckMyCustomTypeA(nodeH); var nodeI = nodeG.Children[1]; Assert.AreEqual("I", nodeI.Guid); Assert.AreEqual(typeof(Filter), nodeI.GetType()); CheckMyCustomTypeA(nodeI); // Third node. var nodeJ = context.Instantiate("J"); Assert.AreEqual("J", nodeJ.Guid); Assert.AreEqual(typeof(Inverter), nodeJ.GetType()); Assert.AreEqual(1, nodeJ.Children.Count); var nodeK = nodeJ.Children[0]; Assert.AreEqual("K", nodeK.Guid); Assert.AreEqual(typeof(Sequence), nodeK.GetType()); CheckMyCustomTypeB(nodeK); // Parameterized node. var nodeL = context.Instantiate("L"); Assert.AreEqual("L", nodeL.Guid); Assert.AreEqual(typeof(ParameterizedTestNode), nodeL.GetType()); Assert.AreEqual(7, nodeL.Children.Count); var parameterizedNode = (ParameterizedTestNode)nodeL; Assert.AreEqual(1, parameterizedNode.PropertyA); Assert.AreEqual(2, parameterizedNode.FieldA); Assert.AreEqual("hello", parameterizedNode.PropertyB); Assert.AreEqual("goodbye", parameterizedNode.FieldB); Assert.AreEqual(3, parameterizedNode.PropertyC); Assert.AreEqual(4, parameterizedNode.FieldC); Assert.AreEqual(3, parameterizedNode.ArrayPropertyA.Count); Assert.AreEqual("one", parameterizedNode.ArrayPropertyA[0].PropertyA); Assert.AreEqual("two", parameterizedNode.ArrayPropertyA[1].PropertyA); Assert.AreEqual("three", parameterizedNode.ArrayPropertyA[2].PropertyA); }