예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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();
        }
예제 #4
0
    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);
    }
예제 #5
0
        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);
        }
예제 #6
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));
        }
예제 #7
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);
        }
예제 #8
0
        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));
        }
예제 #9
0
        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);
        }