public void ContinuesAfterFailure() { // three children: failure, success, and another // index should continue after failure and tick success // index should restart after success and failure should be ticked again // a successful fallback should halt all children var failure = new ReturnXNode(NodeStatus.FAILURE); var success = new ReturnXNode(NodeStatus.SUCCESS); var other = new ReturnXNode(NodeStatus.SUCCESS); var node = new FallbackNode(new List <INode> { failure, success, other, }); var status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.SUCCESS)); Assert.That(failure.Ticks, Is.EqualTo(1)); Assert.That(success.Ticks, Is.EqualTo(1)); Assert.That(other.Ticks, Is.EqualTo(0)); Assert.That(failure.Halts, Is.EqualTo(1)); Assert.That(success.Halts, Is.EqualTo(1)); Assert.That(other.Halts, Is.EqualTo(1)); status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.SUCCESS)); Assert.That(failure.Ticks, Is.EqualTo(2)); Assert.That(success.Ticks, Is.EqualTo(2)); Assert.That(other.Ticks, Is.EqualTo(0)); Assert.That(failure.Halts, Is.EqualTo(2)); Assert.That(success.Halts, Is.EqualTo(2)); Assert.That(other.Halts, Is.EqualTo(2)); }
public void RestartsAfterRunning() { // three children: failure, running, and another // index should continue after failure and tick running // index should restart after running and failure should be ticked again // an incomplete fallback should not halt any children yet var failure = new ReturnXNode(NodeStatus.FAILURE); var running = new ReturnXNode(NodeStatus.RUNNING); var other = new ReturnXNode(NodeStatus.SUCCESS); var node = new FallbackNode(new List <INode> { failure, running, other, }); var status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.RUNNING)); Assert.That(failure.Ticks, Is.EqualTo(1)); Assert.That(running.Ticks, Is.EqualTo(1)); Assert.That(other.Ticks, Is.EqualTo(0)); Assert.That(failure.Halts, Is.EqualTo(0)); Assert.That(running.Halts, Is.EqualTo(0)); Assert.That(other.Halts, Is.EqualTo(0)); status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.RUNNING)); Assert.That(failure.Ticks, Is.EqualTo(2)); Assert.That(running.Ticks, Is.EqualTo(2)); Assert.That(other.Ticks, Is.EqualTo(0)); Assert.That(failure.Halts, Is.EqualTo(0)); Assert.That(running.Halts, Is.EqualTo(0)); Assert.That(other.Halts, Is.EqualTo(0)); }
public void NoChildrenReturnsFailure() { var node = new FallbackNode(new INode[0]); var status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.FAILURE)); }
private static bool BadKeyPredicate(FallbackNode node, IToken token, IResultAccumulator resultAccumulator) { return (node.Name?.ToLowerInvariant() == "bad-key-fallback" && token is TextToken textToken && textToken.Text.StartsWith("-")); }
private bool BadOptionFallback(FallbackNode node, IToken token, IResultAccumulator resultAccumulator) { if (token is TextToken textToken) { return(textToken.Class is KeyTextClass); } return(false); }
public FallbackNodeAcceptedTokenException( FallbackNode fallbackNode, IToken token, IResultAccumulator resultAccumulator) : base("Fallback node accepted token.") { this.FallbackNode = fallbackNode ?? throw new ArgumentNullException(nameof(fallbackNode)); this.Token = token ?? throw new ArgumentNullException(nameof(token)); this.ResultAccumulator = resultAccumulator ?? throw new ArgumentNullException(nameof(resultAccumulator)); }
public void RunningAfterHaltAfterRunningDoesNotHaltPreviouslyChildren() { // four children: flip_flop, failure, running, and other // the fallback is ticked, halted, and ticked // flip_flop will return failure on the first tick, running on the second tick // normally, after flip_flop returns running, failure and running should be halted // since the fallback is halted between the first and second tick, they should not be halted var flip_flop = new ReturnStatusFromCollectionNode(NodeStatus.FAILURE, NodeStatus.RUNNING); var failure = new ReturnXNode(NodeStatus.FAILURE); var running = new ReturnXNode(NodeStatus.RUNNING); var other = new ReturnXNode(NodeStatus.SUCCESS); var node = new FallbackNode(new List <INode> { flip_flop, failure, running, other, }); var status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.RUNNING)); Assert.That(flip_flop.Ticks, Is.EqualTo(1)); Assert.That(failure.Ticks, Is.EqualTo(1)); Assert.That(running.Ticks, Is.EqualTo(1)); Assert.That(other.Ticks, Is.EqualTo(0)); Assert.That(flip_flop.Halts, Is.EqualTo(0)); Assert.That(failure.Halts, Is.EqualTo(0)); Assert.That(running.Halts, Is.EqualTo(0)); Assert.That(other.Halts, Is.EqualTo(0)); node.Halt(); Assert.That(flip_flop.Halts, Is.EqualTo(1)); Assert.That(failure.Halts, Is.EqualTo(1)); Assert.That(running.Halts, Is.EqualTo(1)); Assert.That(other.Halts, Is.EqualTo(1)); // prime flip_flop so that it's next tick, the one from fallback, is running flip_flop.Tick(); status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.RUNNING)); Assert.That(flip_flop.Ticks, Is.EqualTo(3)); // two from fallback, one from primer Assert.That(failure.Ticks, Is.EqualTo(1)); Assert.That(running.Ticks, Is.EqualTo(1)); Assert.That(flip_flop.Halts, Is.EqualTo(1)); Assert.That(failure.Halts, Is.EqualTo(1)); Assert.That(running.Halts, Is.EqualTo(1)); Assert.That(other.Halts, Is.EqualTo(1)); }
public void RepeatedRunningDoesNotHalt() { // two children: failure and running // both nodes should not be halted var failure = new ReturnXNode(NodeStatus.FAILURE); var running = new ReturnXNode(NodeStatus.RUNNING); var node = new FallbackNode(new List <INode> { failure, running, }); node.Tick(); node.Tick(); Assert.That(failure.Ticks, Is.EqualTo(2)); Assert.That(running.Ticks, Is.EqualTo(2)); Assert.That(failure.Halts, Is.EqualTo(0)); Assert.That(running.Halts, Is.EqualTo(0)); }
public void AllChildrenFail() { // all children failing should fail a fallback // a failed fallback should halt all children var children = Enumerable .Range(0, 10) .Select(_ => new ReturnXNode(NodeStatus.FAILURE)) .ToArray(); var node = new FallbackNode(children); var status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.FAILURE)); foreach (var child in children) { Assert.That(child.Ticks, Is.EqualTo(1)); Assert.That(child.Halts, Is.EqualTo(1)); } }
public void HaltsChildrenAfterNewChildReturnsRunning() { // four children: flip_flop, failure, running, and other // flip_flop returns failure on its first tick, running on its second tick // after flip_flop returns running, failure and running should be halted // after flip_flop returns running, other should not be halted // after flip_flop returns running, flip_flop should not be halted var flip_flop = new ReturnStatusFromCollectionNode(NodeStatus.FAILURE, NodeStatus.RUNNING); var failure = new ReturnXNode(NodeStatus.FAILURE); var running = new ReturnXNode(NodeStatus.RUNNING); var other = new ReturnXNode(NodeStatus.SUCCESS); var node = new FallbackNode(new List <INode> { flip_flop, failure, running, other, }); var status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.RUNNING)); Assert.That(flip_flop.Ticks, Is.EqualTo(1)); Assert.That(failure.Ticks, Is.EqualTo(1)); Assert.That(running.Ticks, Is.EqualTo(1)); Assert.That(other.Ticks, Is.EqualTo(0)); Assert.That(flip_flop.Halts, Is.EqualTo(0)); Assert.That(failure.Halts, Is.EqualTo(0)); Assert.That(running.Halts, Is.EqualTo(0)); Assert.That(other.Halts, Is.EqualTo(0)); status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.RUNNING)); Assert.That(flip_flop.Ticks, Is.EqualTo(2)); Assert.That(failure.Ticks, Is.EqualTo(1)); Assert.That(running.Ticks, Is.EqualTo(1)); Assert.That(flip_flop.Halts, Is.EqualTo(0)); Assert.That(failure.Halts, Is.EqualTo(1)); Assert.That(running.Halts, Is.EqualTo(1)); Assert.That(other.Halts, Is.EqualTo(0)); }
public void ChildrenAfterRunningNotTicked() { // two children: running and another // other should not be ticked // a running fallback should not halt any children yet var running = new ReturnXNode(NodeStatus.RUNNING); var other = new ReturnXNode(NodeStatus.SUCCESS); var node = new FallbackNode(new List <INode> { running, other, }); var status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.RUNNING)); Assert.That(running.Ticks, Is.EqualTo(1)); Assert.That(other.Ticks, Is.EqualTo(0)); Assert.That(running.Halts, Is.EqualTo(0)); Assert.That(other.Halts, Is.EqualTo(0)); }
public void ChildrenAfterSuccessNotTicked() { // two children: success and another // other should not be ticked // a successful fallback should halt all children var success = new ReturnXNode(NodeStatus.SUCCESS); var other = new ReturnXNode(NodeStatus.SUCCESS); var node = new FallbackNode(new List <INode> { success, other, }); var status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.SUCCESS)); Assert.That(success.Ticks, Is.EqualTo(1)); Assert.That(other.Ticks, Is.EqualTo(0)); Assert.That(success.Halts, Is.EqualTo(1)); Assert.That(other.Halts, Is.EqualTo(1)); }
public void AllChildrenSucceed() { // a single successful child in a fallback should return success // a successful fallback should halt all children var children = Enumerable .Range(0, 10) .Select(_ => new ReturnXNode(NodeStatus.SUCCESS)) .ToArray(); var node = new FallbackNode(children); var status = node.Tick(); Assert.That(status, Is.EqualTo(NodeStatus.SUCCESS)); for (int i = 0; i < children.Length; i++) { var child = children[i]; var expectedTicks = i == 0 ? 1 : 0; Assert.That(child.Ticks, Is.EqualTo(expectedTicks)); Assert.That(child.Halts, Is.EqualTo(1)); } }
void GenerateBTv010() { BTNode f0 = new FallbackNode(unit); BTNode s0 = new SequenceNode(unit); f0.children.Add(s0); BTNode c0 = new IsCommandIdle(unit); s0.children.Add(c0); BTNode s1 = new SequenceNode(unit); s0.children.Add(s1); BTNode c1 = new BeingShotAt(unit); s1.children.Add(c1); BTNode f1 = new FallbackNode(unit); s1.children.Add(f1); BTNode a0 = new TakeCover(unit, emptySlot); f1.children.Add(a0); BTNode a1 = new RunToTheHills(unit); f1.children.Add(a1); BTNode s2 = new SequenceNode(unit); f0.children.Add(s2); BTNode c2 = new IsCommandHoldPosition(unit); s2.children.Add(c2); BTNode f2 = new FallbackNode(unit); s2.children.Add(f2); BTNode s3 = new SequenceNode(unit); f2.children.Add(s3); BTNode c3 = new IsEnemyNearby(unit); s3.children.Add(c3); BTNode f3 = new FallbackNode(unit); s3.children.Add(f3); BTNode a2 = new TakeCover(unit, emptySlot); f3.children.Add(a2); BTNode a3 = new PushForward(unit); f3.children.Add(a3); BTNode f4 = new FallbackNode(unit); f2.children.Add(f4); BTNode s4 = new SequenceNode(unit); f4.children.Add(s4); BTNode c4 = new AwayFromTargetLocation(unit); s4.children.Add(c4); BTNode a4 = new GoTowardsTargetLocation(unit); s4.children.Add(a4); BTNode s10 = new SequenceNode(unit); f4.children.Add(s10); BTNode a5 = new Diffuse(unit, emptySlot); s10.children.Add(a5); BTNode a10 = new MakeUnitInPosition(unit); s10.children.Add(a10); BTNode s9 = new SequenceNode(unit); f0.children.Add(s9); BTNode c9 = new IsCommandInPosition(unit); s9.children.Add(c9); BTNode s5 = new SequenceNode(unit); f0.children.Add(s5); BTNode c5 = new IsCommandMove(unit); s5.children.Add(c5); BTNode f5 = new FallbackNode(unit); s5.children.Add(f5); BTNode s6 = new SequenceNode(unit); f5.children.Add(s6); BTNode c6 = new BeingShotAt(unit); s6.children.Add(c6); BTNode f6 = new FallbackNode(unit); s6.children.Add(f6); BTNode s7 = new SequenceNode(unit); f6.children.Add(s7); BTNode c7 = new IsEnemyWeakerThanUs(unit); s7.children.Add(c7); BTNode a6 = new PushForward(unit); s7.children.Add(a6); BTNode a7 = new RunToTheHills(unit); f6.children.Add(a7); BTNode s8 = new SequenceNode(unit); f5.children.Add(s8); BTNode c8 = new NotAtTargetLocation(unit); s8.children.Add(c8); BTNode a8 = new GoTowardsTargetLocation(unit); s8.children.Add(a8); BTNode a9 = new MakeUnitIdle(unit); f5.children.Add(a9); root = f0; }
void GenerateSniperBTv100() { BTNode f0 = new FallbackNode(unit); BTNode s0 = new SequenceNode(unit); f0.children.Add(s0); BTNode c0 = new IsCommandIdle(unit); s0.children.Add(c0); BTNode s1 = new SequenceNode(unit); s0.children.Add(s1); BTNode c1 = new BeingShotAt(unit); s1.children.Add(c1); BTNode a0 = new RunToTheHills(unit); s1.children.Add(a0); BTNode s2 = new SequenceNode(unit); f0.children.Add(s2); BTNode c2 = new IsCommandMove(unit); s2.children.Add(c2); BTNode f1 = new FallbackNode(unit); s2.children.Add(f1); BTNode s3 = new SequenceNode(unit); f1.children.Add(s3); BTNode c3 = new BeingShotAt(unit); s3.children.Add(c3); BTNode sf0 = new CSF0(unit, 50f); s3.children.Add(sf0); BTNode a1 = new RunToTheHills(unit); sf0.children.Add(a1); BTNode f2 = new FallbackNode(unit); sf0.children.Add(f2); BTNode a2 = new TakeCover(unit, emptySlot); f2.children.Add(a2); BTNode a3 = new RunToTheHills(unit); f2.children.Add(a3); BTNode s4 = new SequenceNode(unit); f1.children.Add(s4); BTNode c4 = new NotAtTargetLocation(unit); s4.children.Add(c4); BTNode a4 = new GoTowardsTargetLocation(unit); s4.children.Add(a4); BTNode a5 = new MakeUnitIdle(unit); f1.children.Add(a5); root = f0; }
void GenerateMarineBTv100() { BTNode f0 = new FallbackNode(unit); BTNode s0 = new SequenceNode(unit); f0.children.Add(s0); BTNode c0 = new IsCommandIdle(unit); s0.children.Add(c0); BTNode c1 = new BeingShotAt(unit); s0.children.Add(c1); BTNode sf0 = new RSF0(unit); s0.children.Add(sf0); BTNode f1 = new FallbackNode(unit); sf0.children.Add(f1); BTNode a0 = new TakeCover(unit, emptySlot); f1.children.Add(a0); BTNode a1 = new Charge(unit); f1.children.Add(a1); BTNode a2 = new Charge(unit); sf0.children.Add(a2); BTNode s1 = new SequenceNode(unit); f0.children.Add(s1); BTNode c2 = new IsCommandMove(unit); s1.children.Add(c2); BTNode f2 = new FallbackNode(unit); s1.children.Add(f2); BTNode s2 = new SequenceNode(unit); f2.children.Add(s2); BTNode c3 = new BeingShotAt(unit); s2.children.Add(c3); BTNode sf1 = new CSF0(unit, 70f); s2.children.Add(sf1); BTNode a3 = new RunToTheHills(unit); sf1.children.Add(a3); BTNode f3 = new FallbackNode(unit); sf1.children.Add(f3); BTNode s3 = new SequenceNode(unit); f3.children.Add(s3); BTNode c4 = new Overwhelmed(unit); s3.children.Add(c4); BTNode f4 = new FallbackNode(unit); s3.children.Add(f4); BTNode a4 = new TakeCover(unit, emptySlot); f4.children.Add(a4); BTNode a5 = new RunToTheHills(unit); f4.children.Add(a5); BTNode a6 = new PushForward(unit); f3.children.Add(a6); BTNode s4 = new SequenceNode(unit); f2.children.Add(s4); BTNode w0 = new InverterNode(unit); s4.children.Add(w0); BTNode c5 = new AtTargetLocation(unit); w0.children.Add(c5); BTNode a7 = new GoTowardsTargetLocation(unit); s4.children.Add(a7); BTNode a8 = new MakeUnitIdle(unit); f2.children.Add(a8); BTNode s5 = new SequenceNode(unit); f0.children.Add(s5); BTNode c6 = new IsCommandHoldPosition(unit); s5.children.Add(c6); BTNode f5 = new FallbackNode(unit); s5.children.Add(f5); BTNode s6 = new SequenceNode(unit); f5.children.Add(s6); BTNode f6 = new FallbackNode(unit); s6.children.Add(f6); BTNode c7 = new BeingShotAt(unit); f6.children.Add(c7); BTNode c8 = new IsEnemyNearby(unit); f6.children.Add(c8); BTNode f7 = new FallbackNode(unit); s6.children.Add(f7); BTNode a9 = new TakeCover(unit, emptySlot); f7.children.Add(a9); BTNode a10 = new PushForward(unit); f7.children.Add(a10); BTNode s7 = new SequenceNode(unit); f5.children.Add(s7); BTNode w1 = new InverterNode(unit); s7.children.Add(w1); BTNode c9 = new AtTargetLocation(unit); w1.children.Add(c9); BTNode a11 = new GoTowardsTargetLocation(unit); s7.children.Add(a11); BTNode s8 = new SequenceNode(unit); f5.children.Add(s8); BTNode a12 = new Diffuse(unit, emptySlot); s8.children.Add(a12); BTNode a13 = new MakeUnitInPosition(unit); s8.children.Add(a13); BTNode s9 = new SequenceNode(unit); f0.children.Add(s9); BTNode c10 = new IsCommandInPosition(unit); s9.children.Add(c10); BTNode c11 = new BeingShotAt(unit); s9.children.Add(c11); BTNode sf2 = new CSF0(unit, 100f); s9.children.Add(sf2); BTNode a14 = new Charge(unit); sf2.children.Add(a14); BTNode a15 = new TakeCover(unit, emptySlot); s9.children.Add(a15); root = f0; }
public object[] Parse(IEnumerable <IToken> tokens) { var root = this.Root; if (root == null) { throw new NullReferenceException($"Property '{nameof(Root)}' not set."); } if (tokens == null) { throw new ArgumentNullException(nameof(tokens)); } var stream = new TokenStream(tokens); IParsingContext context = new ParsingContext(stream); var initialNodes = ParsingHelper.GetNonIdleNodes(new[] { root }); context.SetNodes(initialNodes); while (true) { var nodes = context.GetNodes(); if (stream.IsEndOfStream()) { if (nodes.Contains(EndNode.Instance)) { // met end of stream, but that is acceptable return(context.ResultAccumulator.ToArray()); } else { throw new UnexpectedEndOfClauseException(context.ResultAccumulator.ToArray()); } } var token = stream.CurrentToken; if (token is IEmptyToken) { stream.AdvanceStreamPosition(); continue; } INode winner = null; FallbackNode fallbackNode = null; var gotEnd = false; foreach (var node in nodes) { if (node is EndNode) { gotEnd = true; continue; } var acceptsToken = node.AcceptsToken(token, context.ResultAccumulator); if (acceptsToken) { if (node is FallbackNode anotherFallbackNode) { if (fallbackNode != null) { throw new NodeConcurrencyException( token, new INode[] { fallbackNode, anotherFallbackNode }, context.ResultAccumulator.ToArray()); } fallbackNode = anotherFallbackNode; } else { if (winner != null) { throw new NodeConcurrencyException( token, new[] { winner, node }, context.ResultAccumulator.ToArray()); } winner = node; } } } if (winner == null) { if (gotEnd) { if (this.WantsOnlyOneResult) { // error. stream has more tokens, but we won't want'em. throw new UnexpectedTokenException(token, context.ResultAccumulator.ToArray()); } // fine, got to end, start over. context.SetNodes(initialNodes); } else if (fallbackNode != null) { fallbackNode.Act(token, context.ResultAccumulator); // will throw, and that's what we want. } else { throw new UnexpectedTokenException(token, context.ResultAccumulator.ToArray()); } } else { var oldVersion = context.ResultAccumulator.Version; winner.Act(token, context.ResultAccumulator); if (oldVersion + 1 != context.ResultAccumulator.Version) { throw new InternalParsingLogicException("Internal error. Non sequential result accumulator versions."); } // skip context.TokenStream.AdvanceStreamPosition(); var successors = winner.ResolveLinks(); var nonIdleSuccessors = ParsingHelper.GetNonIdleNodes(successors); // next nodes context.SetNodes(nonIdleSuccessors); } } }
public virtual INode CreateNode(PseudoList item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } try { var car = item.GetCarSymbolName(); INode node; switch (car) { case "EXACT-TEXT": node = new ExactTextNode( item.GetSingleKeywordArgument <StringAtom>(":value").Value, this.ParseTextClasses(item.GetAllKeywordArguments(":classes")), _isCaseSensitive, null, this.NodeFamily, item.GetItemName()); break; case "SOME-TEXT": node = new TextNode( this.ParseTextClasses(item.GetAllKeywordArguments(":classes")), null, this.NodeFamily, item.GetItemName()); break; case "MULTI-TEXT": node = new MultiTextNode( item.GetAllKeywordArguments(":values").Cast <StringAtom>().Select(x => x.Value), this.ParseTextClasses(item.GetAllKeywordArguments(":classes")), _isCaseSensitive, null, this.NodeFamily, item.GetItemName()); break; case "EXACT-PUNCTUATION": node = new ExactPunctuationNode( item.GetSingleKeywordArgument <StringAtom>(":value").Value.Single(), null, this.NodeFamily, item.GetItemName()); break; // todo: some-number? case "SOME-INTEGER": node = new IntegerNode( null, this.NodeFamily, item.GetItemName()); break; case "FALLBACK": var name = item.GetItemName(); if (name == null) { throw new BuildingException("Fallback node must have a name."); } node = new FallbackNode( this.CreateFallbackPredicate(name), this.NodeFamily, name); break; default: return(null); } return(node); } catch (Exception ex) { throw new BuildingException($"Could not build a node from item {item}.", ex); } }