From 5347df3a3ebd4c7da3f81f52f4cee0b1466d0a96 Mon Sep 17 00:00:00 2001 From: SilasPeters Date: Thu, 28 Mar 2024 12:20:22 +0100 Subject: [PATCH 01/19] wip: overgave aan Thijs --- Aplib.Core/Desire/FirstOfGoalStructure.cs | 34 ++++++++++++ Aplib.Core/Desire/GoalStructure.cs | 35 ++++++++++++ Aplib.Core/Desire/Goals/Goal.cs | 18 ++++--- Aplib.Core/Desire/PrimitiveGoalStructure.cs | 25 +++++++++ Aplib.Core/Desire/RepeatGoalStructure.cs | 20 +++++++ Aplib.Core/Desire/SequentialGoalStructure.cs | 57 ++++++++++++++++++++ Aplib.Tests/Desire/GoalStructureTests.cs | 8 +++ Aplib.Tests/Desire/GoalTests.cs | 10 ++-- 8 files changed, 195 insertions(+), 12 deletions(-) create mode 100644 Aplib.Core/Desire/FirstOfGoalStructure.cs create mode 100644 Aplib.Core/Desire/GoalStructure.cs create mode 100644 Aplib.Core/Desire/PrimitiveGoalStructure.cs create mode 100644 Aplib.Core/Desire/RepeatGoalStructure.cs create mode 100644 Aplib.Core/Desire/SequentialGoalStructure.cs create mode 100644 Aplib.Tests/Desire/GoalStructureTests.cs diff --git a/Aplib.Core/Desire/FirstOfGoalStructure.cs b/Aplib.Core/Desire/FirstOfGoalStructure.cs new file mode 100644 index 00000000..2ac61b47 --- /dev/null +++ b/Aplib.Core/Desire/FirstOfGoalStructure.cs @@ -0,0 +1,34 @@ +using Aplib.Core.Belief; +using Aplib.Core.Desire.Goals; +using System.Collections.Generic; + +namespace Aplib.Core.Desire +{ + public class FirstOfGoalStructure : SequentialGoalStructure + { + protected Goal? _currentGoal; + + /// + public override Goal? DetermineCurrentGoal(IBeliefSet beliefSet) + { + _currentGoal ??= GetNextGoal(beliefSet); // Should only happen the first time this method is called + if (_currentGoal is null) return null; // Can be the case when no next goal can be found + + switch (_currentGoal!.GetState(beliefSet)) + { + case GoalState.Success: + return null; + case GoalState.Failure: + _currentGoal = GetNextGoal(beliefSet); + return DetermineCurrentGoal(beliefSet); + + default: + return _currentGoal; + } + } + + /// + public FirstOfGoalStructure(IList children) : base(children) + { } + } +} diff --git a/Aplib.Core/Desire/GoalStructure.cs b/Aplib.Core/Desire/GoalStructure.cs new file mode 100644 index 00000000..fe6c69a3 --- /dev/null +++ b/Aplib.Core/Desire/GoalStructure.cs @@ -0,0 +1,35 @@ +using Aplib.Core.Belief; +using Aplib.Core.Desire.Goals; +using System.Collections.Generic; + +namespace Aplib.Core.Desire +{ + public abstract class GoalStructure + { + protected IList _children; + protected Goal? _currentGoal; + public GoalStructureState GoalStructureState { get; set; } = GoalStructureState.Unfinished; // TODO + + protected GoalStructure(IList children) + { + _children = children; + } + + public void Interrupt() + { + // Premature optimisation: + // foreach (var processedChild in GetAllProcessedChildren()) + // processedChild.Interrupt(); + foreach (GoalStructure child in _children) + child.Interrupt(); + + ProcessInterrupt(); + } + + public abstract Goal? DetermineCurrentGoal(IBeliefSet beliefSet); + + protected abstract void ProcessInterrupt(); + } + + public enum GoalStructureState { Unfinished, Success, Failure } +} diff --git a/Aplib.Core/Desire/Goals/Goal.cs b/Aplib.Core/Desire/Goals/Goal.cs index 0fca6422..93543a6e 100644 --- a/Aplib.Core/Desire/Goals/Goal.cs +++ b/Aplib.Core/Desire/Goals/Goal.cs @@ -16,15 +16,15 @@ public class Goal /// The abstract definition of what is means to test the Goal's heuristic function. Returns , as /// they represent how close we are to matching the heuristic function, and if the goal is completed. /// - /// - public delegate Heuristics HeuristicFunction(BeliefSet beliefSet); + /// + public delegate Heuristics HeuristicFunction(IBeliefSet beliefSet); /// /// Gets the of the current state of the game. /// /// If no heuristics have been calculated yet, they will be calculated first. - public virtual Heuristics CurrentHeuristics(BeliefSet beliefSet) + public virtual Heuristics CurrentHeuristics(IBeliefSet beliefSet) => _currentHeuristics ??= _heuristicFunction.Invoke(beliefSet); /// @@ -48,7 +48,7 @@ public virtual Heuristics CurrentHeuristics(BeliefSet beliefSet) /// The concrete implementation of this Goal's . Used to test whether this goal is /// completed. /// - /// + /// protected HeuristicFunction _heuristicFunction; /// @@ -86,7 +86,7 @@ public Goal(Tactic tactic, HeuristicFunction heuristicFunction, string name, str /// Creates a new goal which works with boolean-based . /// /// The tactic used to approach this goal. - /// The heuristic function (or specifically predicate) which defines whether a goal is reached + /// The heuristic function (or specifically predicate) which defines whether a goal is reached. /// The name of this goal, used to quickly display this goal in several contexts. /// The description of this goal, used to explain this goal in several contexts. /// @@ -107,8 +107,12 @@ public Goal(Tactic tactic, Func predicate, string name, string description /// . When the distance of the heuristics is smaller than , /// the goal is considered to be completed. /// - /// A boolean representing whether the goal is considered to be completed. + /// An enum representing whether the goal is complete and if so, with what result. /// - public bool IsCompleted(BeliefSet beliefSet) => CurrentHeuristics(beliefSet).Distance < _epsilon; + public GoalState GetState(IBeliefSet beliefSet) => CurrentHeuristics(beliefSet).Distance < _epsilon + ? GoalState.Success + : GoalState.Unfinished; // TODO what about failure? } + + public enum GoalState { Unfinished, Success, Failure } } diff --git a/Aplib.Core/Desire/PrimitiveGoalStructure.cs b/Aplib.Core/Desire/PrimitiveGoalStructure.cs new file mode 100644 index 00000000..065c7aea --- /dev/null +++ b/Aplib.Core/Desire/PrimitiveGoalStructure.cs @@ -0,0 +1,25 @@ +using Aplib.Core.Belief; +using Aplib.Core.Desire.Goals; +using static Aplib.Core.Desire.Goals.GoalState; + +namespace Aplib.Core.Desire +{ + public class PrimitiveGoalStructure : GoalStructure + { + /// + public PrimitiveGoalStructure(Goal goal) : base(children: System.Array.Empty()) + { + // We use _currentGoal as 'the' goal. It will never change hereafter. + _currentGoal = goal; + } + + public override Goal? DetermineCurrentGoal(IBeliefSet beliefSet) => _currentGoal!.GetState(beliefSet) switch + { + Unfinished => _currentGoal, + _ => null + }; + + /// + protected override void ProcessInterrupt() { /* Do nothing */ } + } +} diff --git a/Aplib.Core/Desire/RepeatGoalStructure.cs b/Aplib.Core/Desire/RepeatGoalStructure.cs new file mode 100644 index 00000000..e5d8ab2e --- /dev/null +++ b/Aplib.Core/Desire/RepeatGoalStructure.cs @@ -0,0 +1,20 @@ +using Aplib.Core.Belief; +using Aplib.Core.Desire.Goals; +using static Aplib.Core.Desire.Goals.GoalState; + +namespace Aplib.Core.Desire +{ + public class RepeatGoalStructure : PrimitiveGoalStructure + { + /// + public RepeatGoalStructure(Goal goal) : base(goal) + { } + + /// + public override Goal? DetermineCurrentGoal(IBeliefSet beliefSet) => _currentGoal.GetState(beliefSet) switch + { + Unfinished or Failure => _currentGoal, + Success => null + }; + } +} diff --git a/Aplib.Core/Desire/SequentialGoalStructure.cs b/Aplib.Core/Desire/SequentialGoalStructure.cs new file mode 100644 index 00000000..dfc51886 --- /dev/null +++ b/Aplib.Core/Desire/SequentialGoalStructure.cs @@ -0,0 +1,57 @@ +using Aplib.Core.Belief; +using Aplib.Core.Desire.Goals; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Aplib.Core.Desire +{ + public class SequentialGoalStructure : GoalStructure, IDisposable + { + protected readonly IEnumerator _childrenEnumerator; // TODO autoproperty? + + + /// + public override Goal? DetermineCurrentGoal(IBeliefSet beliefSet) => GetNextGoal(beliefSet); + + /// + /// Returns the goal from the first child which returns a goal. + /// + /// + /// TODO + protected Goal? GetNextGoal(IBeliefSet beliefSet) + { + // throw new NotImplementedException("Why move to the next one immediatly?"); + // if (!_childrenEnumerator.MoveNext()) + // return null; + // return _childrenEnumerator.Current!.DetermineCurrentGoal(beliefSet) ?? GetNextGoal(beliefSet); + + Goal? currentGoal = _childrenEnumerator.Current!.DetermineCurrentGoal(beliefSet); // TODO what happens once Current is undefined? + if (currentGoal is not null) return currentGoal; + // TODO what if the previous goal becomes uncompleted due to e.g. enemies? + + return _childrenEnumerator.MoveNext() ? DetermineCurrentGoal(beliefSet) : null; + } + + /// + protected override void ProcessInterrupt() => _childrenEnumerator.Reset(); + + /// + public SequentialGoalStructure(IList children) : base(children) + { + if (!children.Any()) + throw new ArgumentException("Collection of children is empty", nameof(children)); + _childrenEnumerator = _children.GetEnumerator(); + } + + /// + public void Dispose() + { + _childrenEnumerator.Dispose(); + + // Tell the Garbage Collector that it does not need to run the finalizer of this class anymore + // https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1816#rule-description + GC.SuppressFinalize(this); + } + } +} diff --git a/Aplib.Tests/Desire/GoalStructureTests.cs b/Aplib.Tests/Desire/GoalStructureTests.cs new file mode 100644 index 00000000..eed1a1cc --- /dev/null +++ b/Aplib.Tests/Desire/GoalStructureTests.cs @@ -0,0 +1,8 @@ +using FluentAssertions; + +namespace Aplib.Tests.Desire; + +public class GoalStructureTests +{ + +} diff --git a/Aplib.Tests/Desire/GoalTests.cs b/Aplib.Tests/Desire/GoalTests.cs index 09522ed2..8f806bc8 100644 --- a/Aplib.Tests/Desire/GoalTests.cs +++ b/Aplib.Tests/Desire/GoalTests.cs @@ -66,7 +66,7 @@ public void Goal_WhenReached_ReturnsAsCompleted() // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - bool isCompleted = goal.IsCompleted(beliefSet); + bool isCompleted = goal.GetState(beliefSet); // Assert isCompleted.Should().Be(true); @@ -86,7 +86,7 @@ public void Goal_WhenNotReached_DoesNotReturnAsCompleted() // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - bool isCompleted = goal.IsCompleted(beliefSet); + bool isCompleted = goal.GetState(beliefSet); // Assert isCompleted.Should().Be(false); @@ -105,7 +105,7 @@ public void Goal_WhereEvaluationIsPerformed_DoesNotInfluenceBelieveSet() // Act Goal goal = new TestGoalBuilder().Build(); - _ = goal.IsCompleted(beliefSet); + _ = goal.GetState(beliefSet); // Assert beliefSet.MyBelief.Updated.Should().Be(false); @@ -135,8 +135,8 @@ public void GoalConstructor_WhereHeuristicFunctionTypeDiffers_HasEqualBehaviour( // Act MyBeliefSet beliefSet = new(); - bool goalBooleanEvaluation = goalBoolean.IsCompleted(beliefSet); - bool goalNonBooleanEvaluation = goalNonBoolean.IsCompleted(beliefSet); + bool goalBooleanEvaluation = goalBoolean.GetState(beliefSet); + bool goalNonBooleanEvaluation = goalNonBoolean.GetState(beliefSet); // Assert goalBooleanEvaluation.Should().Be(goalNonBooleanEvaluation); From 9294ba43c0ac09930e5f221016584c1d7044961a Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sat, 30 Mar 2024 01:46:50 +0100 Subject: [PATCH 02/19] feat: implement GoalStructure --- Aplib.Core/Desire/FirstOfGoalStructure.cs | 62 +++++++++---- Aplib.Core/Desire/GoalStructure.cs | 67 +++++++++++--- Aplib.Core/Desire/GoalStructureState.cs | 23 +++++ Aplib.Core/Desire/PrimitiveGoalStructure.cs | 33 ++++--- Aplib.Core/Desire/RepeatGoalStructure.cs | 39 ++++++-- Aplib.Core/Desire/SequentialGoalStructure.cs | 95 +++++++++++++++----- 6 files changed, 247 insertions(+), 72 deletions(-) create mode 100644 Aplib.Core/Desire/GoalStructureState.cs diff --git a/Aplib.Core/Desire/FirstOfGoalStructure.cs b/Aplib.Core/Desire/FirstOfGoalStructure.cs index 2ac61b47..63e204c6 100644 --- a/Aplib.Core/Desire/FirstOfGoalStructure.cs +++ b/Aplib.Core/Desire/FirstOfGoalStructure.cs @@ -1,34 +1,64 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; +using System; using System.Collections.Generic; namespace Aplib.Core.Desire { - public class FirstOfGoalStructure : SequentialGoalStructure + /// + /// Represents a goal structure that will complete if any of its children complete. + /// + /// + /// The children of this goal structure will be executed in the order they are given. + /// + /// The beliefset of the agent. + public class FirstOfGoalStructure : GoalStructure, IDisposable + where TBeliefSet : IBeliefSet { - protected Goal? _currentGoal; + protected IEnumerator> _childrenEnumerator { get; set; } - /// - public override Goal? DetermineCurrentGoal(IBeliefSet beliefSet) + public FirstOfGoalStructure(IList> children) : base(children) { - _currentGoal ??= GetNextGoal(beliefSet); // Should only happen the first time this method is called - if (_currentGoal is null) return null; // Can be the case when no next goal can be found + _childrenEnumerator = children.GetEnumerator(); + _childrenEnumerator.MoveNext(); + _currentGoalStructure = _childrenEnumerator.Current; + } + + public override Goal? GetCurrentGoal(TBeliefSet beliefSet) + { + if (State == GoalStructureState.Success) return null; - switch (_currentGoal!.GetState(beliefSet)) + switch (_currentGoalStructure.State) { - case GoalState.Success: + case GoalStructureState.Unfinished: + return _currentGoalStructure.GetCurrentGoal(beliefSet); + case GoalStructureState.Success: + State = GoalStructureState.Success; return null; - case GoalState.Failure: - _currentGoal = GetNextGoal(beliefSet); - return DetermineCurrentGoal(beliefSet); - + case GoalStructureState.Failure: default: - return _currentGoal; + break; + } + + if (_childrenEnumerator.MoveNext()) + { + _currentGoalStructure = _childrenEnumerator.Current!; + return _currentGoalStructure.GetCurrentGoal(beliefSet); } + + State = GoalStructureState.Failure; + return null; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); } - /// - public FirstOfGoalStructure(IList children) : base(children) - { } + protected virtual void Dispose(bool disposing) + { + _childrenEnumerator.Dispose(); + } } } diff --git a/Aplib.Core/Desire/GoalStructure.cs b/Aplib.Core/Desire/GoalStructure.cs index fe6c69a3..ded82100 100644 --- a/Aplib.Core/Desire/GoalStructure.cs +++ b/Aplib.Core/Desire/GoalStructure.cs @@ -4,32 +4,73 @@ namespace Aplib.Core.Desire { - public abstract class GoalStructure + /// + /// Describes a structure of goals that need to be fulfilled. + /// + /// + /// + /// + public abstract class GoalStructure + where TBeliefSet : IBeliefSet { - protected IList _children; - protected Goal? _currentGoal; - public GoalStructureState GoalStructureState { get; set; } = GoalStructureState.Unfinished; // TODO + /// + /// The children of the goal structure. + /// + protected IList> _children; - protected GoalStructure(IList children) + /// + /// The goalstructure that is currently being fulfilled. + /// + protected GoalStructure? _currentGoalStructure; + + /// + /// Gets or sets the state of the goal structure. + /// + /// + /// By default, the state is set to . + /// However, this can be changed by the goal structure itself. + /// + public GoalStructureState State { get; protected set; } = GoalStructureState.Unfinished; // TODO + + /// + /// Initializes a new instance of the class. + /// + /// The children of the goal structure. + protected GoalStructure(IList> children) { _children = children; } + /// + /// Interrupts the current goal structure. + /// public void Interrupt() { - // Premature optimisation: - // foreach (var processedChild in GetAllProcessedChildren()) - // processedChild.Interrupt(); - foreach (GoalStructure child in _children) + foreach (GoalStructure child in _children) child.Interrupt(); ProcessInterrupt(); } - public abstract Goal? DetermineCurrentGoal(IBeliefSet beliefSet); + /// + /// Gets the current goal using the given . + /// + /// The belief set of the agent. + /// The current goal to be fulfilled. + public abstract Goal? GetCurrentGoal(TBeliefSet beliefSet); - protected abstract void ProcessInterrupt(); + /// + /// Processes the interrupt thrown by the . + /// + /// + /// + /// A goal structure may want to do something when it is interrupted. + /// This method is always called when another goal structure interrupts this one. + /// + /// + /// The default implementation does nothing. + /// + /// + protected virtual void ProcessInterrupt() { } } - - public enum GoalStructureState { Unfinished, Success, Failure } } diff --git a/Aplib.Core/Desire/GoalStructureState.cs b/Aplib.Core/Desire/GoalStructureState.cs new file mode 100644 index 00000000..22c9500e --- /dev/null +++ b/Aplib.Core/Desire/GoalStructureState.cs @@ -0,0 +1,23 @@ +namespace Aplib.Core.Desire +{ + /// + /// Represents the state of a . + /// + public enum GoalStructureState + { + /// + /// Represents a goal structure that is not yet completed. + /// + Unfinished, + + /// + /// Represents a goal structure that has been successfully completed. + /// + Success, + + /// + /// Represents a goal structure that has failed to complete. + /// + Failure + } +} diff --git a/Aplib.Core/Desire/PrimitiveGoalStructure.cs b/Aplib.Core/Desire/PrimitiveGoalStructure.cs index 065c7aea..db4c5e91 100644 --- a/Aplib.Core/Desire/PrimitiveGoalStructure.cs +++ b/Aplib.Core/Desire/PrimitiveGoalStructure.cs @@ -4,22 +4,33 @@ namespace Aplib.Core.Desire { - public class PrimitiveGoalStructure : GoalStructure + /// + /// Represents a goal structure that will complete if any of its children complete. + /// + /// + /// This is the most primitive goal structure. It is used to represent a single goal that is not part of a larger structure. + /// This goal structure will only return the goal it was created with if the goal is not yet finished. + /// + /// The beliefset of the agent. + public class PrimitiveGoalStructure : GoalStructure + where TBeliefSet : IBeliefSet { - /// - public PrimitiveGoalStructure(Goal goal) : base(children: System.Array.Empty()) + private readonly Goal _goal; + + /// + /// Initializes a new instance of the class. + /// + /// The goal to fulfill. + public PrimitiveGoalStructure(Goal goal) : base(children: System.Array.Empty>()) { - // We use _currentGoal as 'the' goal. It will never change hereafter. - _currentGoal = goal; + _goal = goal; } - public override Goal? DetermineCurrentGoal(IBeliefSet beliefSet) => _currentGoal!.GetState(beliefSet) switch + /// + public override Goal? GetCurrentGoal(TBeliefSet beliefSet) => _goal!.GetState(beliefSet) switch { - Unfinished => _currentGoal, - _ => null + Unfinished => _goal, + _ => null }; - - /// - protected override void ProcessInterrupt() { /* Do nothing */ } } } diff --git a/Aplib.Core/Desire/RepeatGoalStructure.cs b/Aplib.Core/Desire/RepeatGoalStructure.cs index e5d8ab2e..4ae7762a 100644 --- a/Aplib.Core/Desire/RepeatGoalStructure.cs +++ b/Aplib.Core/Desire/RepeatGoalStructure.cs @@ -1,20 +1,43 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; -using static Aplib.Core.Desire.Goals.GoalState; +using System; +using System.Collections.Generic; +using static Aplib.Core.Desire.GoalStructureState; namespace Aplib.Core.Desire { - public class RepeatGoalStructure : PrimitiveGoalStructure + /// + /// Represents a goal structure that will complete if any of its children complete. + /// + /// + /// This structure will repeatedly execute the goal it was created with until the goal is finished. + /// + /// The beliefset of the agent. + public class RepeatGoalStructure : GoalStructure + where TBeliefSet : IBeliefSet { - /// - public RepeatGoalStructure(Goal goal) : base(goal) - { } + /// + /// Initializes a new instance of the class. + /// + /// The goalstructure to repeat + public RepeatGoalStructure(GoalStructure goalStructure) : base( + children: new List> { goalStructure }) + { + _currentGoalStructure = goalStructure; + } /// - public override Goal? DetermineCurrentGoal(IBeliefSet beliefSet) => _currentGoal.GetState(beliefSet) switch + public override Goal? GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.State switch { - Unfinished or Failure => _currentGoal, - Success => null + Unfinished or Failure => _currentGoalStructure.GetCurrentGoal(beliefSet), + Success => FinishRepeat(), + _ => throw new NotImplementedException() }; + + private Goal? FinishRepeat() + { + State = Success; + return null; + } } } diff --git a/Aplib.Core/Desire/SequentialGoalStructure.cs b/Aplib.Core/Desire/SequentialGoalStructure.cs index dfc51886..e00233fa 100644 --- a/Aplib.Core/Desire/SequentialGoalStructure.cs +++ b/Aplib.Core/Desire/SequentialGoalStructure.cs @@ -6,40 +6,80 @@ namespace Aplib.Core.Desire { - public class SequentialGoalStructure : GoalStructure, IDisposable + /// + /// Represents a sequential goal structure. + /// + /// + /// This class is a specific type of goal structure where goals are processed sequentially. + /// All goals must be completed in order for the goal structure to be completed. + /// + /// The type of belief set that this goal structure operates on. + public class SequentialGoalStructure : GoalStructure, IDisposable + where TBeliefSet : IBeliefSet { - protected readonly IEnumerator _childrenEnumerator; // TODO autoproperty? - + private IEnumerator> _childrenEnumerator { get; set; } /// - public override Goal? DetermineCurrentGoal(IBeliefSet beliefSet) => GetNextGoal(beliefSet); - - /// - /// Returns the goal from the first child which returns a goal. - /// - /// - /// TODO - protected Goal? GetNextGoal(IBeliefSet beliefSet) + public override Goal? GetCurrentGoal(TBeliefSet beliefSet) { - // throw new NotImplementedException("Why move to the next one immediatly?"); - // if (!_childrenEnumerator.MoveNext()) - // return null; - // return _childrenEnumerator.Current!.DetermineCurrentGoal(beliefSet) ?? GetNextGoal(beliefSet); + // Check if the current goal is completed + if (IsCurrentGoalCompleted()) return null; + + // Check if the previous goals are still completed + IEnumerator> enumerator = _children.GetEnumerator(); + while (enumerator.Current != _childrenEnumerator.Current) + { + if (enumerator.Current!.State == GoalStructureState.Success) continue; - Goal? currentGoal = _childrenEnumerator.Current!.DetermineCurrentGoal(beliefSet); // TODO what happens once Current is undefined? + State = GoalStructureState.Unfinished; + + // If the goal is not completed, retry the goal and reset the enumerator. + _childrenEnumerator = enumerator; + return enumerator.Current.GetCurrentGoal(beliefSet); + } + + Goal? currentGoal = + _childrenEnumerator.Current!.GetCurrentGoal(beliefSet); // TODO what happens once Current is undefined? if (currentGoal is not null) return currentGoal; - // TODO what if the previous goal becomes uncompleted due to e.g. enemies? - return _childrenEnumerator.MoveNext() ? DetermineCurrentGoal(beliefSet) : null; + // If the goal is completed, check if all the goals are completed + if (!_childrenEnumerator.MoveNext() && _childrenEnumerator.Current.State == GoalStructureState.Success) + { + State = GoalStructureState.Success; + return null!; + } + + // We have tried all the goals, but the last one is not completed. Or something else went wrong. + State = GoalStructureState.Failure; + return null!; + } + + private bool IsCurrentGoalCompleted() + { + switch (_currentGoalStructure!.State) + { + case GoalStructureState.Success: + _childrenEnumerator.MoveNext(); + _currentGoalStructure = _childrenEnumerator.Current; + break; + case GoalStructureState.Failure: + State = GoalStructureState.Failure; + return true; + case GoalStructureState.Unfinished: + default: + break; + } + + return false; } /// protected override void ProcessInterrupt() => _childrenEnumerator.Reset(); /// - public SequentialGoalStructure(IList children) : base(children) + public SequentialGoalStructure(IList> children) : base(children) { - if (!children.Any()) + if (children.Count <= 0) throw new ArgumentException("Collection of children is empty", nameof(children)); _childrenEnumerator = _children.GetEnumerator(); } @@ -47,11 +87,18 @@ public SequentialGoalStructure(IList children) : base(children) /// public void Dispose() { - _childrenEnumerator.Dispose(); - - // Tell the Garbage Collector that it does not need to run the finalizer of this class anymore - // https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1816#rule-description + Dispose(true); GC.SuppressFinalize(this); } + + /// + /// Disposes the enumerator. + /// + /// Whether the object is being disposed. + protected virtual void Dispose(bool disposing) + { + if (!disposing) return; + _childrenEnumerator.Dispose(); + } } } From 67a09b70e30b488e6b4ecef2ba1ed71014e8463c Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sat, 30 Mar 2024 16:35:14 +0100 Subject: [PATCH 03/19] chore: fix .editorconfig --- .editorconfig | 72 +++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/.editorconfig b/.editorconfig index 74388033..01cce039 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,12 +1,12 @@ # Remove the line below if you want to inherit .editorconfig settings from higher directories root = true -# XMLDOC settings -xmldoc_indent_size = 0 - # C# files [*.cs] +# XMLDoc settings +resharper_xmldoc_indent_size = 0 + #### Core EditorConfig Options #### # Indentation and spacing @@ -107,7 +107,7 @@ csharp_style_conditional_delegate_call = true # Modifier preferences csharp_prefer_static_local_function = true -csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_preferred_modifier_order = public, private, protected, internal, file, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async csharp_style_prefer_readonly_struct = true csharp_style_prefer_readonly_struct_member = true @@ -195,6 +195,10 @@ csharp_preserve_single_line_statements = true # Naming rules +dotnet_naming_rule.private_members_with_underscore.severity = warning +dotnet_naming_rule.private_members_with_underscore.symbols = private_fields +dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore + dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i @@ -203,64 +207,58 @@ dotnet_naming_rule.types_should_be_pascal_case.severity = warning dotnet_naming_rule.types_should_be_pascal_case.symbols = types dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.classes_should_be_pascal_case.severity = warning +dotnet_naming_rule.classes_should_be_pascal_case.symbols = classes +dotnet_naming_rule.classes_should_be_pascal_case.style = pascal_case + dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case -dotnet_naming_rule.private_members_with_underscore.severity = warning -dotnet_naming_rule.private_members_with_underscore.symbols = private_fields -dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore +dotnet_naming_rule.public_fields_should_be_pascal_case.severity = warning +dotnet_naming_rule.public_fields_should_be_pascal_case.symbols = public_fields +dotnet_naming_rule.public_fields_should_be_pascal_case.style = pascal_case # Symbol specifications dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum, field dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.classes.applicable_kinds = class, struct, enum +dotnet_naming_symbols.classes.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.classes.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.non_field_members.required_modifiers = -dotnet_naming_symbols.private_fields.applicable_kinds = field, property -dotnet_naming_symbols.private_fields.applicable_accessibilities = private +dotnet_naming_symbols.public_fields.applicable_kinds = field, property +dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal, protected_internal + +dotnet_naming_symbols.private_fields.applicable_kinds = field, property +dotnet_naming_symbols.private_fields.applicable_accessibilities = protected, private_protected, private # Naming styles -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case + dotnet_naming_style.prefix_underscore.capitalization = camel_case dotnet_naming_style.prefix_underscore.required_prefix = _ -[*.{cs,vb}] -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_operator_placement_when_wrapping = beginning_of_line -tab_width = 4 -indent_size = 4 -end_of_line = crlf - # Use underscores for private fields -dotnet_naming_symbols.private_fields.applicable_kinds = field -dotnet_naming_symbols.private_fields.applicable_accessibilities = internal, protected_internal, private - -dotnet_naming_style.prefix_underscore.capitalization = camel_case -dotnet_naming_style.prefix_underscore.required_prefix = _ - -dotnet_naming_rule.private_fields_with_underscore.symbols = private_fields -dotnet_naming_rule.private_fields_with_underscore.style = prefix_underscore +dotnet_naming_rule.private_fields_with_underscore.symbols = private_fields +dotnet_naming_rule.private_fields_with_underscore.style = prefix_underscore dotnet_naming_rule.private_fields_with_underscore.severity = warning \ No newline at end of file From 4cd40d5c5ecb4cb937f282ccb1bf71f07ec294b8 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sat, 30 Mar 2024 23:02:29 +0100 Subject: [PATCH 04/19] feat: add interfaces for easy testing --- Aplib.Core/Desire/Goals/IGoal.cs | 31 ++++++++++++++++++++ Aplib.Core/Desire/IGoalStructure.cs | 44 ++++++++++++++++++++++++++++ Aplib.Core/Interruptable.cs | 36 +++++++++++++++++++++++ Aplib.Core/InterruptableEventArgs.cs | 20 +++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 Aplib.Core/Desire/Goals/IGoal.cs create mode 100644 Aplib.Core/Desire/IGoalStructure.cs create mode 100644 Aplib.Core/Interruptable.cs create mode 100644 Aplib.Core/InterruptableEventArgs.cs diff --git a/Aplib.Core/Desire/Goals/IGoal.cs b/Aplib.Core/Desire/Goals/IGoal.cs new file mode 100644 index 00000000..c26c4ffa --- /dev/null +++ b/Aplib.Core/Desire/Goals/IGoal.cs @@ -0,0 +1,31 @@ +using Aplib.Core.Belief; +using Aplib.Core.Intent.Tactics; + +namespace Aplib.Core.Desire.Goals +{ + public interface IGoal + { + /// + /// The used to achieve this , which is executed during every + /// iteration + /// of the BDI cycle. + /// + Tactic Tactic { get; } + + /// + /// Gets the of the current state of the game. + /// + /// If no heuristics have been calculated yet, they will be calculated first. + Heuristics CurrentHeuristics(IBeliefSet beliefSet); + + /// + /// Tests whether the goal has been achieved, bases on the and the + /// . When the distance of the heuristics is smaller than + /// , + /// the goal is considered to be completed. + /// + /// An enum representing whether the goal is complete and if so, with what result. + /// + GoalState GetState(IBeliefSet beliefSet); + } +} diff --git a/Aplib.Core/Desire/IGoalStructure.cs b/Aplib.Core/Desire/IGoalStructure.cs new file mode 100644 index 00000000..975780f3 --- /dev/null +++ b/Aplib.Core/Desire/IGoalStructure.cs @@ -0,0 +1,44 @@ +using Aplib.Core.Belief; +using Aplib.Core.Desire.Goals; +using System; + +namespace Aplib.Core.Desire +{ + public interface IGoalStructure : IInterruptable + where TBeliefSet : IBeliefSet + { + /// + /// Gets or sets the state of the goal structure. + /// + /// + /// By default, the state is set to . + /// However, this can be changed by the goal structure itself. + /// + GoalStructureState State { get; } + + /// + event EventHandler> OnInterrupt; + + /// + event EventHandler> OnReinstate; + + /// + void Interrupt(TBeliefSet beliefSet); + + /// + void Reinstate(TBeliefSet beliefSet); + + /// + /// Updates the state of the goal structure. + /// + /// The belief set of the agent. + void UpdateState(TBeliefSet beliefSet); + + /// + /// Gets the current goal using the given . + /// + /// The belief set of the agent. + /// The current goal to be fulfilled. + IGoal? GetCurrentGoal(TBeliefSet beliefSet); + } +} diff --git a/Aplib.Core/Interruptable.cs b/Aplib.Core/Interruptable.cs new file mode 100644 index 00000000..d13426a5 --- /dev/null +++ b/Aplib.Core/Interruptable.cs @@ -0,0 +1,36 @@ +using Aplib.Core.Belief; +using Aplib.Core.Desire; +using System; + +namespace Aplib.Core +{ + /// + /// Defines a class that can be interrupted and reinstated. + /// + public interface IInterruptable + where TBeliefSet : IBeliefSet + { + /// + /// Interrupts the current goal structure. + /// + /// The beliefset at the time of interrupting. + public void Interrupt(TBeliefSet beliefSet); + + /// + /// Reinstates the goal structure after an interrupt. + /// + /// The beliefset at the time of reinstating. + public void Reinstate(TBeliefSet beliefSet); + + + /// + /// Handles the interrupt event. + /// + public event EventHandler> OnInterrupt; + + /// + /// Handles the reinstate event. + /// + public event EventHandler> OnReinstate; + } +} diff --git a/Aplib.Core/InterruptableEventArgs.cs b/Aplib.Core/InterruptableEventArgs.cs new file mode 100644 index 00000000..4ec69c86 --- /dev/null +++ b/Aplib.Core/InterruptableEventArgs.cs @@ -0,0 +1,20 @@ +using Aplib.Core.Belief; +using System; + +namespace Aplib.Core.Desire +{ + public class InterruptableEventArgs : EventArgs + where TBeliefSet : IBeliefSet + { + /// + /// Initializes a new instance of the class. + /// + /// The beliefset of the agent. + public InterruptableEventArgs(TBeliefSet beliefSet) => BeliefSet = beliefSet; + + /// + /// Gets the beliefset of the agent. + /// + public TBeliefSet BeliefSet { get; } + } +} From 32b1c94bbc1b60c28dbdc9c9e2dc08a526750f90 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sat, 30 Mar 2024 23:05:40 +0100 Subject: [PATCH 05/19] feat: simplify goal structure --- Aplib.Core/Desire/IGoalStructure.cs | 30 ++++++++++++----------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/Aplib.Core/Desire/IGoalStructure.cs b/Aplib.Core/Desire/IGoalStructure.cs index 975780f3..c29a7ce2 100644 --- a/Aplib.Core/Desire/IGoalStructure.cs +++ b/Aplib.Core/Desire/IGoalStructure.cs @@ -1,9 +1,15 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; -using System; namespace Aplib.Core.Desire { + /// + /// Represents a goal structure. + /// + /// + /// A goal structure is structure of predicates that must be fulfilled in order to complete a test. + /// + /// public interface IGoalStructure : IInterruptable where TBeliefSet : IBeliefSet { @@ -16,29 +22,17 @@ public interface IGoalStructure : IInterruptable /// GoalStructureState State { get; } - /// - event EventHandler> OnInterrupt; - - /// - event EventHandler> OnReinstate; - - /// - void Interrupt(TBeliefSet beliefSet); - - /// - void Reinstate(TBeliefSet beliefSet); - /// - /// Updates the state of the goal structure. + /// Gets the current goal using the given . /// /// The belief set of the agent. - void UpdateState(TBeliefSet beliefSet); + /// The current goal to be fulfilled. + IGoal? GetCurrentGoal(TBeliefSet beliefSet); /// - /// Gets the current goal using the given . + /// Updates the state of the goal structure. /// /// The belief set of the agent. - /// The current goal to be fulfilled. - IGoal? GetCurrentGoal(TBeliefSet beliefSet); + void UpdateState(TBeliefSet beliefSet); } } From 4e6d858d7b5e4f9fae818f07078827f050d38e29 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sat, 30 Mar 2024 23:10:32 +0100 Subject: [PATCH 06/19] feat: make Goal and Goalstructure inherit from interfaces. --- Aplib.Core/Desire/GoalStructure.cs | 82 +++++++++++++++--------------- Aplib.Core/Desire/Goals/Goal.cs | 68 ++++++++++++------------- 2 files changed, 74 insertions(+), 76 deletions(-) diff --git a/Aplib.Core/Desire/GoalStructure.cs b/Aplib.Core/Desire/GoalStructure.cs index ded82100..e532980f 100644 --- a/Aplib.Core/Desire/GoalStructure.cs +++ b/Aplib.Core/Desire/GoalStructure.cs @@ -1,5 +1,6 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; +using System; using System.Collections.Generic; namespace Aplib.Core.Desire @@ -7,70 +8,67 @@ namespace Aplib.Core.Desire /// /// Describes a structure of goals that need to be fulfilled. /// - /// - /// - /// - public abstract class GoalStructure - where TBeliefSet : IBeliefSet + public abstract class GoalStructure : IGoalStructure where TBeliefSet : IBeliefSet { - /// - /// The children of the goal structure. - /// - protected IList> _children; + /// + public event EventHandler>? OnInterrupt; - /// - /// The goalstructure that is currently being fulfilled. - /// - protected GoalStructure? _currentGoalStructure; + /// + public event EventHandler>? OnReinstate; /// /// Gets or sets the state of the goal structure. /// /// - /// By default, the state is set to . - /// However, this can be changed by the goal structure itself. + /// By default, the state is set to . + /// However, this can be changed by the goal structure. /// - public GoalStructureState State { get; protected set; } = GoalStructureState.Unfinished; // TODO + public GoalStructureState State { get; protected set; } = GoalStructureState.Unfinished; /// - /// Initializes a new instance of the class. + /// The children of the goal structure. /// - /// The children of the goal structure. - protected GoalStructure(IList> children) - { - _children = children; - } + protected readonly IEnumerable> _children; /// - /// Interrupts the current goal structure. + /// The goalstructure that is currently being fulfilled. /// - public void Interrupt() - { - foreach (GoalStructure child in _children) - child.Interrupt(); + protected IGoalStructure? _currentGoalStructure; - ProcessInterrupt(); - } + /// + /// Initializes a new instance of the class. + /// + /// The children of the goal structure. + protected GoalStructure(IEnumerable> children) => _children = children; /// - /// Gets the current goal using the given . + /// Gets the current goal using the given . /// /// The belief set of the agent. /// The current goal to be fulfilled. - public abstract Goal? GetCurrentGoal(TBeliefSet beliefSet); + public abstract IGoal? GetCurrentGoal(TBeliefSet beliefSet); + + /// + public void Interrupt(TBeliefSet beliefSet) + { + OnInterrupt?.Invoke(this, new InterruptableEventArgs(beliefSet)); + + foreach (IGoalStructure child in _children) + child.Interrupt(beliefSet); + } + + /// + public void Reinstate(TBeliefSet beliefSet) + { + OnReinstate?.Invoke(this, new InterruptableEventArgs(beliefSet)); + foreach (IGoalStructure child in _children) + child.Reinstate(beliefSet); + } /// - /// Processes the interrupt thrown by the . + /// Updates the state of the goal structure. /// - /// - /// - /// A goal structure may want to do something when it is interrupted. - /// This method is always called when another goal structure interrupts this one. - /// - /// - /// The default implementation does nothing. - /// - /// - protected virtual void ProcessInterrupt() { } + /// The belief set of the agent. + public abstract void UpdateState(TBeliefSet beliefSet); } } diff --git a/Aplib.Core/Desire/Goals/Goal.cs b/Aplib.Core/Desire/Goals/Goal.cs index 93543a6e..b3c7bab8 100644 --- a/Aplib.Core/Desire/Goals/Goal.cs +++ b/Aplib.Core/Desire/Goals/Goal.cs @@ -6,26 +6,23 @@ namespace Aplib.Core.Desire.Goals { /// /// A goal effectively combines a heuristic function with a tactic, and aims to meet the heuristic function by - /// applying the tactic. Goals are combined in a , and are used to prepare tests or do + /// applying the tactic. Goals are combined in a , and are used to prepare tests or do /// the testing. /// - /// - public class Goal + /// + public class Goal : IGoal { /// - /// The abstract definition of what is means to test the Goal's heuristic function. Returns , as + /// The abstract definition of what is means to test the Goal's heuristic function. Returns , as /// they represent how close we are to matching the heuristic function, and if the goal is completed. /// - /// + /// public delegate Heuristics HeuristicFunction(IBeliefSet beliefSet); - /// - /// Gets the of the current state of the game. + /// The description used to describe the current goal during debugging, logging, or general overviews. /// - /// If no heuristics have been calculated yet, they will be calculated first. - public virtual Heuristics CurrentHeuristics(IBeliefSet beliefSet) - => _currentHeuristics ??= _heuristicFunction.Invoke(beliefSet); + public string Description { get; } /// /// The name used to display the current goal during debugging, logging, or general overviews. @@ -33,47 +30,44 @@ public virtual Heuristics CurrentHeuristics(IBeliefSet beliefSet) public string Name { get; } /// - /// The description used to describe the current goal during debugging, logging, or general overviews. + /// The used to achieve this , which is executed during every + /// iteration + /// of the BDI cycle. /// - public string Description { get; } + public Tactic Tactic { get; } /// - /// The goal is considered to be completed, when the distance of the is below + /// The goal is considered to be completed, when the distance of the is below /// this value. /// protected double _epsilon { get; } /// - /// The concrete implementation of this Goal's . Used to test whether this goal is + /// The concrete implementation of this Goal's . Used to test whether this goal is /// completed. /// - /// + /// protected HeuristicFunction _heuristicFunction; /// - /// The used to achieve this , which is executed during every iteration - /// of the BDI cycle. - /// - public Tactic Tactic { get; private set; } - - /// - /// The backing field of . + /// The backing field of . /// private Heuristics? _currentHeuristics; /// - /// Creates a new goal which works with . + /// Creates a new goal which works with . /// /// The tactic used to approach this goal. /// The heuristic function which defines whether a goal is reached /// The name of this goal, used to quickly display this goal in several contexts. /// The description of this goal, used to explain this goal in several contexts. /// - /// The goal is considered to be completed, when the distance of the is below + /// The goal is considered to be completed, when the distance of the is below /// this value. /// - public Goal(Tactic tactic, HeuristicFunction heuristicFunction, string name, string description, double epsilon = 0.005d) + public Goal(Tactic tactic, HeuristicFunction heuristicFunction, string name, string description, + double epsilon = 0.005d) { Tactic = tactic; _heuristicFunction = heuristicFunction; @@ -83,14 +77,14 @@ public Goal(Tactic tactic, HeuristicFunction heuristicFunction, string name, str } /// - /// Creates a new goal which works with boolean-based . + /// Creates a new goal which works with boolean-based . /// /// The tactic used to approach this goal. /// The heuristic function (or specifically predicate) which defines whether a goal is reached. /// The name of this goal, used to quickly display this goal in several contexts. /// The description of this goal, used to explain this goal in several contexts. /// - /// The goal is considered to be completed, when the distance of the is below + /// The goal is considered to be completed, when the distance of the is below /// this value. /// public Goal(Tactic tactic, Func predicate, string name, string description, double epsilon = 0.005d) @@ -102,17 +96,23 @@ public Goal(Tactic tactic, Func predicate, string name, string description _epsilon = epsilon; } + + /// + /// Gets the of the current state of the game. + /// + /// If no heuristics have been calculated yet, they will be calculated first. + public virtual Heuristics CurrentHeuristics(IBeliefSet beliefSet) + => _currentHeuristics ??= _heuristicFunction.Invoke(beliefSet); + /// - /// Tests whether the goal has been achieved, bases on the and the - /// . When the distance of the heuristics is smaller than , + /// Tests whether the goal has been achieved, bases on the and the + /// . When the distance of the heuristics is smaller than , /// the goal is considered to be completed. /// /// An enum representing whether the goal is complete and if so, with what result. - /// - public GoalState GetState(IBeliefSet beliefSet) => CurrentHeuristics(beliefSet).Distance < _epsilon + /// + public virtual GoalState GetState(IBeliefSet beliefSet) => CurrentHeuristics(beliefSet).Distance < _epsilon ? GoalState.Success - : GoalState.Unfinished; // TODO what about failure? + : GoalState.Unfinished; } - - public enum GoalState { Unfinished, Success, Failure } } From 9414ad82d25155729c62f5009888ea5a378f6104 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sat, 30 Mar 2024 23:18:00 +0100 Subject: [PATCH 07/19] feat: change implementation of goal structures. The goal structures have been altered so that they now first update the state, and then choose a goal. They also have a mechanism for interrupting and reinstating them. --- Aplib.Core/Desire/FirstOfGoalStructure.cs | 76 +++++++----- Aplib.Core/Desire/Goals/GoalState.cs | 23 ++++ Aplib.Core/Desire/PrimitiveGoalStructure.cs | 28 +++-- Aplib.Core/Desire/RepeatGoalStructure.cs | 32 +++-- Aplib.Core/Desire/SequentialGoalStructure.cs | 117 ++++++++++--------- 5 files changed, 170 insertions(+), 106 deletions(-) create mode 100644 Aplib.Core/Desire/Goals/GoalState.cs diff --git a/Aplib.Core/Desire/FirstOfGoalStructure.cs b/Aplib.Core/Desire/FirstOfGoalStructure.cs index 63e204c6..4e5596a3 100644 --- a/Aplib.Core/Desire/FirstOfGoalStructure.cs +++ b/Aplib.Core/Desire/FirstOfGoalStructure.cs @@ -15,50 +15,74 @@ namespace Aplib.Core.Desire public class FirstOfGoalStructure : GoalStructure, IDisposable where TBeliefSet : IBeliefSet { - protected IEnumerator> _childrenEnumerator { get; set; } + private IEnumerator> _childrenEnumerator { get; } - public FirstOfGoalStructure(IList> children) : base(children) + public FirstOfGoalStructure(IList> children) : base(children) { _childrenEnumerator = children.GetEnumerator(); _childrenEnumerator.MoveNext(); _currentGoalStructure = _childrenEnumerator.Current; + + OnReinstate += (_, args) => + { + _childrenEnumerator.Reset(); + _childrenEnumerator.MoveNext(); + + _currentGoalStructure = _childrenEnumerator.Current!; + + State = _currentGoalStructure.State == GoalStructureState.Failure + ? GoalStructureState.Unfinished + : _currentGoalStructure.State; + }; } - public override Goal? GetCurrentGoal(TBeliefSet beliefSet) - { - if (State == GoalStructureState.Success) return null; + /// + public override IGoal? GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.GetCurrentGoal(beliefSet); - switch (_currentGoalStructure.State) + /// + public override void UpdateState(TBeliefSet beliefSet) + { + // Loop through all the children until one of them is unfinished or successful. + // This loop is here to prevent tail recursion. + while (true) { - case GoalStructureState.Unfinished: - return _currentGoalStructure.GetCurrentGoal(beliefSet); - case GoalStructureState.Success: - State = GoalStructureState.Success; - return null; - case GoalStructureState.Failure: - default: - break; - } + if (State == GoalStructureState.Success) return; + _currentGoalStructure!.UpdateState(beliefSet); - if (_childrenEnumerator.MoveNext()) - { - _currentGoalStructure = _childrenEnumerator.Current!; - return _currentGoalStructure.GetCurrentGoal(beliefSet); - } + switch (_currentGoalStructure.State) + { + case GoalStructureState.Unfinished: + return; + case GoalStructureState.Success: + State = GoalStructureState.Success; + return; + } + + if (_childrenEnumerator.MoveNext()) + { + _currentGoalStructure = _childrenEnumerator.Current; + State = GoalStructureState.Unfinished; - State = GoalStructureState.Failure; - return null; + // Update the state of the new goal structure + continue; + } + + State = GoalStructureState.Failure; + return; + } } + /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) - { - _childrenEnumerator.Dispose(); - } + /// + /// Disposes of the goal structure. + /// + /// Whether we are actually disposing. + protected virtual void Dispose(bool disposing) => _childrenEnumerator.Dispose(); } } diff --git a/Aplib.Core/Desire/Goals/GoalState.cs b/Aplib.Core/Desire/Goals/GoalState.cs new file mode 100644 index 00000000..7cd0746b --- /dev/null +++ b/Aplib.Core/Desire/Goals/GoalState.cs @@ -0,0 +1,23 @@ +namespace Aplib.Core.Desire.Goals +{ + /// + /// Represents the state of a goal. + /// + public enum GoalState + { + /// + /// The goal has not yet been completed. + /// + Unfinished, + + /// + /// The goal has been completed successfully. + /// + Success, + + /// + /// The goal has failed. + /// + Failure + } +} diff --git a/Aplib.Core/Desire/PrimitiveGoalStructure.cs b/Aplib.Core/Desire/PrimitiveGoalStructure.cs index db4c5e91..5d2b90ad 100644 --- a/Aplib.Core/Desire/PrimitiveGoalStructure.cs +++ b/Aplib.Core/Desire/PrimitiveGoalStructure.cs @@ -1,5 +1,6 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; +using System; using static Aplib.Core.Desire.Goals.GoalState; namespace Aplib.Core.Desire @@ -8,29 +9,32 @@ namespace Aplib.Core.Desire /// Represents a goal structure that will complete if any of its children complete. /// /// - /// This is the most primitive goal structure. It is used to represent a single goal that is not part of a larger structure. + /// This is the most primitive goal structure. It is used to represent a single goal that is not part of a larger + /// structure. /// This goal structure will only return the goal it was created with if the goal is not yet finished. /// /// The beliefset of the agent. public class PrimitiveGoalStructure : GoalStructure where TBeliefSet : IBeliefSet { - private readonly Goal _goal; + private readonly IGoal _goal; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The goal to fulfill. - public PrimitiveGoalStructure(Goal goal) : base(children: System.Array.Empty>()) - { - _goal = goal; - } + public PrimitiveGoalStructure(IGoal goal) : base(Array.Empty>()) => _goal = goal; /// - public override Goal? GetCurrentGoal(TBeliefSet beliefSet) => _goal!.GetState(beliefSet) switch - { - Unfinished => _goal, - _ => null - }; + public override IGoal? GetCurrentGoal(TBeliefSet beliefSet) => _goal; + + /// + public override void UpdateState(TBeliefSet beliefSet) => + State = _goal.GetState(beliefSet) switch + { + Unfinished => GoalStructureState.Unfinished, + Success => GoalStructureState.Success, + _ => GoalStructureState.Failure + }; } } diff --git a/Aplib.Core/Desire/RepeatGoalStructure.cs b/Aplib.Core/Desire/RepeatGoalStructure.cs index 4ae7762a..8bb6ad75 100644 --- a/Aplib.Core/Desire/RepeatGoalStructure.cs +++ b/Aplib.Core/Desire/RepeatGoalStructure.cs @@ -1,6 +1,5 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; -using System; using System.Collections.Generic; using static Aplib.Core.Desire.GoalStructureState; @@ -17,27 +16,38 @@ public class RepeatGoalStructure : GoalStructure where TBeliefSet : IBeliefSet { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The goalstructure to repeat - public RepeatGoalStructure(GoalStructure goalStructure) : base( - children: new List> { goalStructure }) - { + public RepeatGoalStructure(IGoalStructure goalStructure) : base( + new List> { goalStructure }) => _currentGoalStructure = goalStructure; - } /// - public override Goal? GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.State switch + public override IGoal? GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.State switch { Unfinished or Failure => _currentGoalStructure.GetCurrentGoal(beliefSet), - Success => FinishRepeat(), - _ => throw new NotImplementedException() + _ => FinishRepeat(beliefSet) }; - private Goal? FinishRepeat() + /// + public override void UpdateState(TBeliefSet beliefSet) + { + _currentGoalStructure!.UpdateState(beliefSet); + + if (_currentGoalStructure.State == Failure) _currentGoalStructure.Reinstate(beliefSet); + + State = _currentGoalStructure.State switch + { + Failure or Unfinished => Unfinished, + _ => Success + }; + } + + private IGoal? FinishRepeat(TBeliefSet beliefSet) { State = Success; - return null; + return _currentGoalStructure!.GetCurrentGoal(beliefSet); } } } diff --git a/Aplib.Core/Desire/SequentialGoalStructure.cs b/Aplib.Core/Desire/SequentialGoalStructure.cs index e00233fa..6a7da68f 100644 --- a/Aplib.Core/Desire/SequentialGoalStructure.cs +++ b/Aplib.Core/Desire/SequentialGoalStructure.cs @@ -2,7 +2,6 @@ using Aplib.Core.Desire.Goals; using System; using System.Collections.Generic; -using System.Linq; namespace Aplib.Core.Desire { @@ -17,71 +16,60 @@ namespace Aplib.Core.Desire public class SequentialGoalStructure : GoalStructure, IDisposable where TBeliefSet : IBeliefSet { - private IEnumerator> _childrenEnumerator { get; set; } + /// + /// Gets or sets the enumerator for the children of the goal structure. + /// + private IEnumerator> _childrenEnumerator { get; set; } /// - public override Goal? GetCurrentGoal(TBeliefSet beliefSet) + public SequentialGoalStructure(IList> children) : base(children) { - // Check if the current goal is completed - if (IsCurrentGoalCompleted()) return null; - - // Check if the previous goals are still completed - IEnumerator> enumerator = _children.GetEnumerator(); - while (enumerator.Current != _childrenEnumerator.Current) - { - if (enumerator.Current!.State == GoalStructureState.Success) continue; - - State = GoalStructureState.Unfinished; + if (children.Count <= 0) + throw new ArgumentException("Collection of children is empty", nameof(children)); + _childrenEnumerator = _children.GetEnumerator(); + _childrenEnumerator.MoveNext(); + _currentGoalStructure = _childrenEnumerator.Current; - // If the goal is not completed, retry the goal and reset the enumerator. - _childrenEnumerator = enumerator; - return enumerator.Current.GetCurrentGoal(beliefSet); - } + OnReinstate += CheckForGoalCompletion; + } - Goal? currentGoal = - _childrenEnumerator.Current!.GetCurrentGoal(beliefSet); // TODO what happens once Current is undefined? - if (currentGoal is not null) return currentGoal; + /// + public override IGoal? GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.GetCurrentGoal(beliefSet); - // If the goal is completed, check if all the goals are completed - if (!_childrenEnumerator.MoveNext() && _childrenEnumerator.Current.State == GoalStructureState.Success) + /// + public override void UpdateState(TBeliefSet beliefSet) + { + // Loop through all the children until one of them is unfinished or successful. + // This loop is here to prevent tail recursion. + while (true) { - State = GoalStructureState.Success; - return null!; - } + if (State == GoalStructureState.Success) return; + _currentGoalStructure!.UpdateState(beliefSet); - // We have tried all the goals, but the last one is not completed. Or something else went wrong. - State = GoalStructureState.Failure; - return null!; - } + switch (_currentGoalStructure.State) + { + case GoalStructureState.Unfinished: + return; + case GoalStructureState.Failure: + State = GoalStructureState.Failure; + return; + case GoalStructureState.Success: + default: + break; + } - private bool IsCurrentGoalCompleted() - { - switch (_currentGoalStructure!.State) - { - case GoalStructureState.Success: - _childrenEnumerator.MoveNext(); + if (_childrenEnumerator.MoveNext()) + { _currentGoalStructure = _childrenEnumerator.Current; - break; - case GoalStructureState.Failure: - State = GoalStructureState.Failure; - return true; - case GoalStructureState.Unfinished: - default: - break; - } - - return false; - } + State = GoalStructureState.Unfinished; - /// - protected override void ProcessInterrupt() => _childrenEnumerator.Reset(); + // Update the state of the new goal structure + continue; + } - /// - public SequentialGoalStructure(IList> children) : base(children) - { - if (children.Count <= 0) - throw new ArgumentException("Collection of children is empty", nameof(children)); - _childrenEnumerator = _children.GetEnumerator(); + State = GoalStructureState.Success; + return; + } } /// @@ -95,10 +83,25 @@ public void Dispose() /// Disposes the enumerator. /// /// Whether the object is being disposed. - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) => _childrenEnumerator.Dispose(); + + private void CheckForGoalCompletion(object sender, InterruptableEventArgs e) { - if (!disposing) return; - _childrenEnumerator.Dispose(); + // Check if the previous goals are still completed + IEnumerator> enumerator = _children.GetEnumerator(); + while (enumerator.Current != _childrenEnumerator.Current) + { + enumerator.MoveNext(); + enumerator.Current!.UpdateState(e.BeliefSet); + if (enumerator.Current!.State == GoalStructureState.Success) continue; + + State = GoalStructureState.Unfinished; + + // If the goal is not completed, retry the goal and reset the enumerator. + _childrenEnumerator = enumerator; + _currentGoalStructure = _childrenEnumerator.Current; + return; + } } } } From 3fe42978e5d86273b0bc8a084cfdd2de057c80ae Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sat, 30 Mar 2024 23:21:23 +0100 Subject: [PATCH 08/19] test: test the goal structures, and rework some of the tests to work with moq --- Aplib.Tests/Aplib.Tests.csproj | 59 +-- Aplib.Tests/Core/Desire/GoalStructureTests.cs | 465 ++++++++++++++++++ Aplib.Tests/{ => Core}/Desire/GoalTests.cs | 48 +- .../Core/{ => Intent/Actions}/ActionTests.cs | 92 ++-- .../Core/{ => Intent}/Tactics/TacticTests.cs | 83 ++-- Aplib.Tests/Desire/GoalStructureTests.cs | 8 - Aplib.Tests/Stubs/Desire/TacticStub.cs | 14 - Aplib.Tests/Tools/TestGoalBuilder.cs | 10 +- 8 files changed, 611 insertions(+), 168 deletions(-) create mode 100644 Aplib.Tests/Core/Desire/GoalStructureTests.cs rename Aplib.Tests/{ => Core}/Desire/GoalTests.cs (77%) rename Aplib.Tests/Core/{ => Intent/Actions}/ActionTests.cs (92%) rename Aplib.Tests/Core/{ => Intent}/Tactics/TacticTests.cs (87%) delete mode 100644 Aplib.Tests/Desire/GoalStructureTests.cs delete mode 100644 Aplib.Tests/Stubs/Desire/TacticStub.cs diff --git a/Aplib.Tests/Aplib.Tests.csproj b/Aplib.Tests/Aplib.Tests.csproj index b2470e8f..0d591c19 100644 --- a/Aplib.Tests/Aplib.Tests.csproj +++ b/Aplib.Tests/Aplib.Tests.csproj @@ -1,37 +1,38 @@ - - net8.0 - enable - enable + + net8.0 + enable + enable - false - true - + false + true + - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + - - - all - runtime; build; native; contentfiles; analyzers - - + + + all + runtime; build; native; contentfiles; analyzers + + - - - + + + diff --git a/Aplib.Tests/Core/Desire/GoalStructureTests.cs b/Aplib.Tests/Core/Desire/GoalStructureTests.cs new file mode 100644 index 00000000..f32231e8 --- /dev/null +++ b/Aplib.Tests/Core/Desire/GoalStructureTests.cs @@ -0,0 +1,465 @@ +using Aplib.Core.Belief; +using Aplib.Core.Desire; +using Aplib.Core.Desire.Goals; +using FluentAssertions; +using Moq; +using Moq.Protected; + +namespace Aplib.Tests.Core.Desire; + +public class GoalStructureTests +{ + [Theory] + [InlineData(GoalState.Success, GoalStructureState.Success)] + [InlineData(GoalState.Failure, GoalStructureState.Failure)] + [InlineData(GoalState.Unfinished, GoalStructureState.Unfinished)] + public void PrimitiveGoalStructure_WhenGoalHasState_ShouldHaveSameState(GoalState state, + GoalStructureState expected) + { + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(state); + BeliefSet beliefSet = Mock.Of(); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + + // Act + primitiveGoalStructure.UpdateState(beliefSet); + + // Assert + primitiveGoalStructure.State.Should().Be(expected); + } + + [Fact] + public void PrimitiveGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() + { + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Unfinished); + BeliefSet beliefSet = Mock.Of(); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + + // Act + primitiveGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = primitiveGoalStructure.GetCurrentGoal(beliefSet); + + // Assert + currentGoal.Should().Be(goal.Object); + } + + [Fact] + public void RepeatGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() + { + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Unfinished); + BeliefSet beliefSet = Mock.Of(); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); + + // Act + repeatGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); + + // Assert + repeatGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + currentGoal.Should().Be(goal.Object); + } + + [Fact] + public void RepeatGoalStructure_WhenGoalStructureHasFailed_ShouldReturnGoal() + { + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Failure); + BeliefSet beliefSet = Mock.Of(); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); + + // Act + repeatGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); + + // Assert + repeatGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + currentGoal.Should().Be(goal.Object); + } + + [Fact] + public void RepeatGoalStructure_WhenGoalStructureHasSucceeded_ShouldSucceed() + { + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Success); + BeliefSet beliefSet = Mock.Of(); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); + + // Act + repeatGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); + + // Assert + repeatGoalStructure.State.Should().Be(GoalStructureState.Success); + currentGoal.Should().Be(goal.Object); + } + + [Fact] + public void FirstOfGoalStructure_WhenFirstGoalIsFinished_ShouldReturnSuccess() + { + // Arrange + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + Mock> goalStructure2 = new(); + BeliefSet beliefSet = Mock.Of(); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + // Act + firstOfGoalStructure.UpdateState(beliefSet); + + // Assert + firstOfGoalStructure.State.Should().Be(GoalStructureState.Success); + } + + [Fact] + public void FirstOfGoalStructure_WhenFirstGoalIsUnfinished_ShouldReturnUnfinished() + { + // Arrange + Mock goal = new(); + Mock> goalStructure1 = new(); + goalStructure1 + .Setup(g => g.GetCurrentGoal(It.IsAny())) + .Returns(goal.Object); + + goalStructure1 + .SetupGet(g => g.State) + .Returns(GoalStructureState.Unfinished); + + Mock> goalStructure2 = new(); + BeliefSet beliefSet = Mock.Of(); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + // Act + firstOfGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); + + // Assert + firstOfGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + currentGoal.Should().Be(goal.Object); + } + + [Fact] + public void FirstOfGoalStructure_WhenFirstGoalIsFailure_ShouldReturnSecondGoal() + { + // Arrange + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Failure); + Mock> goalStructure2 = new(); + IGoal goal = Mock.Of(); + goalStructure2 + .Setup(g => g.GetCurrentGoal(It.IsAny())) + .Returns(goal); + + BeliefSet beliefSet = Mock.Of(); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + // Act + firstOfGoalStructure.UpdateState(beliefSet); + IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; + + // Assert + firstOfGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + currentGoal.Should().Be(goal); + } + + [Theory] + [InlineData(GoalStructureState.Success, GoalStructureState.Success)] + [InlineData(GoalStructureState.Failure, GoalStructureState.Unfinished)] + [InlineData(GoalStructureState.Unfinished, GoalStructureState.Unfinished)] + public void FirstOfGoalStructure_WhenInterrupted_ShouldRecalculateState(GoalStructureState input, + GoalStructureState expected) + { + // Arrange + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(input); + IGoal goal = Mock.Of(); + goalStructure1 + .Setup(g => g.GetCurrentGoal(It.IsAny())) + .Returns(goal); + + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Success); + + BeliefSet beliefSet = Mock.Of(); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + firstOfGoalStructure.UpdateState(beliefSet); + + // Act + firstOfGoalStructure.Interrupt(beliefSet); + firstOfGoalStructure.Reinstate(beliefSet); + IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; + + // Assert + firstOfGoalStructure.State.Should().Be(expected); + currentGoal.Should().Be(goal); + } + + [Fact] + public void FirstOfGoalStructure_WhenAllGoalsFail_ShouldReturnFailure() + { + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Failure); + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Failure); + + BeliefSet beliefSet = Mock.Of(); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + // Act + firstOfGoalStructure.UpdateState(beliefSet); + + // Assert + firstOfGoalStructure.State.Should().Be(GoalStructureState.Failure); + } + + [Fact] + public void FirstOfGoalStructure_WhenFinished_ShouldEarlyExit() + { + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Success); + + BeliefSet beliefSet = Mock.Of(); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + firstOfGoalStructure.UpdateState(beliefSet); + + // Act + firstOfGoalStructure.UpdateState(beliefSet); + + // Assert + firstOfGoalStructure.State.Should().Be(GoalStructureState.Success); + goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); + } + + [Fact] + public void FirstOfGoalStructure_WhenGoalIsUnfinished_ShouldReturnGoal() + { + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); + + IGoal goal = Mock.Of(); + goalStructure1.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(goal); + + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); + + BeliefSet beliefSet = Mock.Of(); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + // Act + firstOfGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); + + // Assert + firstOfGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + currentGoal.Should().Be(goal); + } + + [Fact] + public void FirstOfGoalStructure_WhenDisposing_ShouldDisposeChildren() + { + // Arrange + Mock> goalStructure1 = new(); + Mock> goalStructure2 = new(); + + Mock> firstOfGoalStructure = + new(new List> { goalStructure1.Object, goalStructure2.Object }) + { + CallBase = true + }; + + // Act + firstOfGoalStructure.Object.Dispose(); + + // Assert + firstOfGoalStructure.Protected().Verify("Dispose", Times.Once(), ItExpr.IsAny()); + } + + [Fact] + public void SequentialGoalStructure_WhenProvidingNoGoalStructure_ShouldThrowException() + { + // Arrange + List> goalStructures = new(); + + // Act + Func> act = () => new SequentialGoalStructure(goalStructures); + + // Assert + act.Should().Throw(); + } + + [Fact] + public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldReturnUnfinished() + { + // Arrange + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + Mock> goalStructure2 = new(); + BeliefSet beliefSet = Mock.Of(); + SequentialGoalStructure sequentialGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + // Act + sequentialGoalStructure.UpdateState(beliefSet); + + // Assert + sequentialGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + } + + [Fact] + public void SequentialGoalStructure_WhenGoalFails_ShouldReturnFailure() + { + // Arrange + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Failure); + Mock> goalStructure2 = new(); + BeliefSet beliefSet = Mock.Of(); + SequentialGoalStructure sequentialGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + // Act + sequentialGoalStructure.UpdateState(beliefSet); + + // Assert + sequentialGoalStructure.State.Should().Be(GoalStructureState.Failure); + } + + [Fact] + public void SequentialGoalStructure_WhenInterruptedWithNoChanges_ShouldReturnExactSameGoal() + { + // Arrange + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); + IGoal expected = Mock.Of(); + goalStructure2.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); + BeliefSet beliefSet = Mock.Of(); + SequentialGoalStructure sequentialGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + sequentialGoalStructure.UpdateState(beliefSet); + + // Act + sequentialGoalStructure.Interrupt(beliefSet); + sequentialGoalStructure.Reinstate(beliefSet); + IGoal currentGoal = sequentialGoalStructure.GetCurrentGoal(beliefSet)!; + + // Assert + sequentialGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + currentGoal.Should().Be(expected); + } + + [Fact] + public void SequentialGoalStructure_WhenInterruptedWithChanges_ShouldReturnSameGoal() + { + // Arrange + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + IGoal expected = Mock.Of(); + goalStructure1.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); + + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); + + BeliefSet beliefSet = Mock.Of(); + SequentialGoalStructure sequentialGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + sequentialGoalStructure.UpdateState(beliefSet); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); + + // Act + sequentialGoalStructure.Interrupt(beliefSet); + sequentialGoalStructure.Reinstate(beliefSet); + IGoal currentGoal = sequentialGoalStructure.GetCurrentGoal(beliefSet)!; + + // Assert + sequentialGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + currentGoal.Should().Be(expected); + } + + [Fact] + public void SequentialGoalStructure_WhenDisposing_ShouldDisposeChildren() + { + // Arrange + Mock> goalStructure1 = new(); + Mock> goalStructure2 = new(); + + Mock> sequentialGoalStructure = + new(new List> { goalStructure1.Object, goalStructure2.Object }) + { + CallBase = true + }; + + // Act + sequentialGoalStructure.Object.Dispose(); + + // Assert + sequentialGoalStructure.Protected().Verify("Dispose", Times.Once(), ItExpr.IsAny()); + } + + [Fact] + public void SequentialGoalStructure_WhenFinished_ShouldEarlyExit() + { + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Success); + + BeliefSet beliefSet = Mock.Of(); + SequentialGoalStructure sequentialGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + sequentialGoalStructure.UpdateState(beliefSet); + + // Act + sequentialGoalStructure.UpdateState(beliefSet); + + // Assert + sequentialGoalStructure.State.Should().Be(GoalStructureState.Success); + goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); + } +} diff --git a/Aplib.Tests/Desire/GoalTests.cs b/Aplib.Tests/Core/Desire/GoalTests.cs similarity index 77% rename from Aplib.Tests/Desire/GoalTests.cs rename to Aplib.Tests/Core/Desire/GoalTests.cs index 8f806bc8..30c5c568 100644 --- a/Aplib.Tests/Desire/GoalTests.cs +++ b/Aplib.Tests/Core/Desire/GoalTests.cs @@ -1,12 +1,12 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; using Aplib.Core.Intent.Tactics; -using Aplib.Tests.Stubs.Desire; using Aplib.Tests.Tools; using FluentAssertions; +using Moq; using Action = Aplib.Core.Intent.Actions.Action; -namespace Aplib.Tests.Desire; +namespace Aplib.Tests.Core.Desire; public class GoalTests { @@ -19,10 +19,11 @@ public class GoalTests public void Goal_WhenConstructed_ContainsCorrectMetaData() { // Arrange - Tactic tactic = new TacticStub(new Action(() => { })); + Tactic tactic = Mock.Of(); Goal.HeuristicFunction heuristicFunction = CommonHeuristicFunctions.Constant(0f); const string name = "Such a good goal name"; - const string description = "\"A lie is just a good story that someone ruined with the truth.\" - Barney Stinson"; + const string description = + "\"A lie is just a good story that someone ruined with the truth.\" - Barney Stinson"; // Act Goal goal = new(tactic, heuristicFunction, name, description); // Does not use helper methods on purpose @@ -43,12 +44,13 @@ public void Goal_WhenConstructed_DidNotIterateYet() { // Arrange int iterations = 0; - Tactic tactic = new TacticStub(new Action(() => iterations++)); - + Mock tactic = new(); + tactic.Setup(x => x.GetAction()).Returns(new Action(() => { iterations++; })); // Act - Goal _ = new TestGoalBuilder().UseTactic(tactic).Build(); + Goal goal = new TestGoalBuilder().UseTactic(tactic.Object).Build(); // Assert + goal.Tactic.Should().Be(tactic.Object); iterations.Should().Be(0); } @@ -66,10 +68,10 @@ public void Goal_WhenReached_ReturnsAsCompleted() // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - bool isCompleted = goal.GetState(beliefSet); + GoalState isCompleted = goal.GetState(beliefSet); // Assert - isCompleted.Should().Be(true); + isCompleted.Should().Be(GoalState.Success); } /// @@ -86,10 +88,10 @@ public void Goal_WhenNotReached_DoesNotReturnAsCompleted() // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - bool isCompleted = goal.GetState(beliefSet); + GoalState isCompleted = goal.GetState(beliefSet); // Assert - isCompleted.Should().Be(false); + isCompleted.Should().Be(GoalState.Unfinished); } /// @@ -123,9 +125,10 @@ public void Goal_WhereEvaluationIsPerformed_DoesNotInfluenceBelieveSet() public void GoalConstructor_WhereHeuristicFunctionTypeDiffers_HasEqualBehaviour(bool goalCompleted) { // Arrange - Tactic tactic = new TacticStub(new Action(() => { })); + Tactic tactic = Mock.Of(); const string name = "Such a good goal name"; - const string description = "\"A lie is just a good story that someone ruined with the truth.\" - Barney Stinson"; + const string description = + "\"A lie is just a good story that someone ruined with the truth.\" - Barney Stinson"; Func heuristicFunctionBoolean = () => goalCompleted; Goal.HeuristicFunction heuristicFunctionNonBoolean = CommonHeuristicFunctions.Boolean(() => goalCompleted); @@ -135,8 +138,8 @@ public void GoalConstructor_WhereHeuristicFunctionTypeDiffers_HasEqualBehaviour( // Act MyBeliefSet beliefSet = new(); - bool goalBooleanEvaluation = goalBoolean.GetState(beliefSet); - bool goalNonBooleanEvaluation = goalNonBoolean.GetState(beliefSet); + GoalState goalBooleanEvaluation = goalBoolean.GetState(beliefSet); + GoalState goalNonBooleanEvaluation = goalNonBoolean.GetState(beliefSet); // Assert goalBooleanEvaluation.Should().Be(goalNonBooleanEvaluation); @@ -150,25 +153,22 @@ private class MyBeliefSet : BeliefSet /// /// Belief that sets Updated to true when UpdateBelief is called. /// - public SimpleBelief MyBelief = new(); + public readonly SimpleBelief MyBelief = new(); } /// - /// A simple belief that can be used to test whether has been called. + /// A simple belief that can be used to test whether has been called. /// private class SimpleBelief : IBelief { /// - /// Stores whether has been called. + /// Stores whether has been called. /// - public bool Updated { get; private set; } = false; + public bool Updated { get; private set; } /// - /// Sets to true. + /// Sets to true. /// - public void UpdateBelief() - { - Updated = true; - } + public void UpdateBelief() => Updated = true; } } diff --git a/Aplib.Tests/Core/ActionTests.cs b/Aplib.Tests/Core/Intent/Actions/ActionTests.cs similarity index 92% rename from Aplib.Tests/Core/ActionTests.cs rename to Aplib.Tests/Core/Intent/Actions/ActionTests.cs index 26b60b45..66bd83dd 100644 --- a/Aplib.Tests/Core/ActionTests.cs +++ b/Aplib.Tests/Core/Intent/Actions/ActionTests.cs @@ -1,10 +1,10 @@ using Aplib.Core.Intent.Actions; using Action = Aplib.Core.Intent.Actions.Action; -namespace Aplib.Tests.Core; +namespace Aplib.Tests.Core.Intent.Actions; /// -/// Describes a set of tests for the class. +/// Describes a set of tests for the class. /// public class ActionTests { @@ -18,7 +18,7 @@ public void Execute_SideEffects_ReturnsCorrectEffect() { // Arrange string? result = "abc"; - Action action = new(effect: () => result = "def"); + Action action = new(() => result = "def"); // Act action.Execute(); @@ -28,33 +28,35 @@ public void Execute_SideEffects_ReturnsCorrectEffect() } /// - /// Given an action with no query, - /// When checking if the action is actionable, - /// Then the result should always be true. + /// Given a guarded action with an int guard, + /// When the action is guarded and executed, + /// Then the result should be the value of the guard. /// [Fact] - public void IsActionable_NoQuery_AlwaysTrue() + public void Execute_WithGuard_ShouldInvokeQueryAndStoreResult() { // Arrange - Action action = new(effect: () => { }); + int result = 0; + GuardedAction action = new(guard: () => 42, effect: guard => result = guard); // Act - bool actionable = action.IsActionable(); + _ = action.IsActionable(); + action.Execute(); // Assert - Assert.True(actionable); + Assert.Equal(42, result); } /// - /// Given an action with a true query, + /// Given an action with no query, /// When checking if the action is actionable, - /// Then the result should be true. + /// Then the result should always be true. /// [Fact] - public void IsActionable_QueryWithTrue_ReturnsTrue() + public void IsActionable_NoQuery_AlwaysTrue() { // Arrange - Action action = new(effect: () => { }, guard: () => true); + Action action = new(() => { }); // Act bool actionable = action.IsActionable(); @@ -64,94 +66,92 @@ public void IsActionable_QueryWithTrue_ReturnsTrue() } /// - /// Given an action with a false query, + /// Given an action with a false bool guard, /// When checking if the action is actionable, - /// Then the result should be false. + /// Then the result should be true. /// [Fact] - public void IsActionable_QueryWithFalse_ReturnsFalse() + public void IsActionable_QueryIsFalse_IsActionable() { // Arrange - Action action = new(effect: () => { }, guard: () => false); + GuardedAction action = new(guard: () => false, effect: b => { }); // Act - bool actionable = action.IsActionable(); + bool result = action.IsActionable(); // Assert - Assert.False(actionable); + Assert.True(result); } /// - /// Given a guarded action with an int guard, - /// When the action is guarded and executed, - /// Then the result should be the value of the guard. + /// Given an action with a non-null int guard, + /// When checking if the action is actionable, + /// Then the result should be true. /// [Fact] - public void Execute_WithGuard_ShouldInvokeQueryAndStoreResult() + public void IsActionable_QueryIsNotNull_IsActionable() { // Arrange - int result = 0; - GuardedAction action = new(guard: () => 42, effect: (guard) => result = guard); + GuardedAction action = new(guard: () => 10, effect: b => { }); // Act - _ = action.IsActionable(); - action.Execute(); + bool result = action.IsActionable(); // Assert - Assert.Equal(42, result); + Assert.True(result); } /// - /// Given an action with a non-null int guard, + /// Given an action with a null object guard, /// When checking if the action is actionable, - /// Then the result should be true. + /// Then the result should be false. /// [Fact] - public void IsActionable_QueryIsNotNull_IsActionable() + public void IsActionable_QueryIsNull_IsNotActionable() { // Arrange - GuardedAction action = new(guard: () => 10, effect: b => { }); + GuardedAction action = new(guard: () => null!, effect: b => { }); // Act bool result = action.IsActionable(); // Assert - Assert.True(result); + Assert.False(result); } /// - /// Given an action with a false bool guard, + /// Given an action with a false query, /// When checking if the action is actionable, - /// Then the result should be true. + /// Then the result should be false. /// [Fact] - public void IsActionable_QueryIsFalse_IsActionable() + public void IsActionable_QueryWithFalse_ReturnsFalse() { // Arrange - GuardedAction action = new(guard: () => false, effect: b => { }); + Action action = new(() => { }, () => false); // Act - bool result = action.IsActionable(); + bool actionable = action.IsActionable(); // Assert - Assert.True(result); + Assert.False(actionable); } /// - /// Given an action with a null object guard, + /// Given an action with a true query, /// When checking if the action is actionable, - /// Then the result should be false. + /// Then the result should be true. /// [Fact] - public void IsActionable_QueryIsNull_IsNotActionable() + public void IsActionable_QueryWithTrue_ReturnsTrue() { // Arrange - GuardedAction action = new(guard: () => null!, effect: b => { }); + Action action = new(() => { }, () => true); // Act - bool result = action.IsActionable(); + bool actionable = action.IsActionable(); // Assert - Assert.False(result); + Assert.True(actionable); } } diff --git a/Aplib.Tests/Core/Tactics/TacticTests.cs b/Aplib.Tests/Core/Intent/Tactics/TacticTests.cs similarity index 87% rename from Aplib.Tests/Core/Tactics/TacticTests.cs rename to Aplib.Tests/Core/Intent/Tactics/TacticTests.cs index 181a179d..e62f8e7f 100644 --- a/Aplib.Tests/Core/Tactics/TacticTests.cs +++ b/Aplib.Tests/Core/Intent/Tactics/TacticTests.cs @@ -1,29 +1,44 @@ using Aplib.Core.Intent.Tactics; using Action = Aplib.Core.Intent.Actions.Action; -namespace Aplib.Tests.Core.Tactics; +namespace Aplib.Tests.Core.Intent.Tactics; + public class TacticTests { - private readonly Action _emptyAction = new(effect: () => { }); private static string _result = "abc"; - private readonly Action _filledAction = new(effect: () => _result = "def"); + private readonly Action _emptyAction = new(() => { }); + private readonly Action _filledAction = new(() => _result = "def"); - private static bool TrueGuard() => true; + /// + /// Given a tactic with a guard that returns true and an action, + /// When calling the Execute method, + /// Then _result should be "def". + /// + [Fact] + public void Execute_WhenGuardReturnsTrue_ActionIsExecuted() + { + // Arrange + PrimitiveTactic tactic = new(_filledAction, TrueGuard); - private static bool FalseGuard() => false; + // Act + tactic.GetAction()!.Execute(); + + // Assert + Assert.Equal("def", _result); + } /// - /// Given a parent of type with two subtactics, + /// Given a parent of type with two subtactics, /// When getting the next tactic, - /// Then the result should be the first subtactic. + /// Then the result should contain all the subtactics. /// [Fact] - public void GetAction_WhenTacticTypeIsFirstOf_ReturnsEnabledPrimitiveTactics() + public void GetAction_WhenTacticTypeIsAnyOf_ReturnsEnabledPrimitiveTactics() { // Arrange PrimitiveTactic tactic1 = new(_emptyAction); - PrimitiveTactic tactic2 = new(_filledAction); - FirstOfTactic parentTactic = new([tactic1, tactic2]); + PrimitiveTactic tactic2 = new(_emptyAction); + AnyOfTactic parentTactic = new(tactic1, tactic2); // Act Action? enabledAction = parentTactic.GetAction(); @@ -34,17 +49,17 @@ public void GetAction_WhenTacticTypeIsFirstOf_ReturnsEnabledPrimitiveTactics() } /// - /// Given a parent of type with two subtactics and a guard that is true, + /// Given a parent of type with two subtactics, /// When getting the next tactic, /// Then the result should be the first subtactic. /// [Fact] - public void GetAction_WhenTacticTypeIsFirstOfAndGuardEnabled_ReturnsEnabledPrimitiveTactics() + public void GetAction_WhenTacticTypeIsFirstOf_ReturnsEnabledPrimitiveTactics() { // Arrange PrimitiveTactic tactic1 = new(_emptyAction); PrimitiveTactic tactic2 = new(_filledAction); - FirstOfTactic parentTactic = new(TrueGuard, [tactic1, tactic2]); + FirstOfTactic parentTactic = new(tactic1, tactic2); // Act Action? enabledAction = parentTactic.GetAction(); @@ -55,17 +70,17 @@ public void GetAction_WhenTacticTypeIsFirstOfAndGuardEnabled_ReturnsEnabledPrimi } /// - /// Given a parent of type with two subtactics, + /// Given a parent of type with two subtactics and a guard that is true, /// When getting the next tactic, - /// Then the result should contain all the subtactics. + /// Then the result should be the first subtactic. /// [Fact] - public void GetAction_WhenTacticTypeIsAnyOf_ReturnsEnabledPrimitiveTactics() + public void GetAction_WhenTacticTypeIsFirstOfAndGuardEnabled_ReturnsEnabledPrimitiveTactics() { // Arrange PrimitiveTactic tactic1 = new(_emptyAction); - PrimitiveTactic tactic2 = new(_emptyAction); - AnyOfTactic parentTactic = new([tactic1, tactic2]); + PrimitiveTactic tactic2 = new(_filledAction); + FirstOfTactic parentTactic = new(TrueGuard, tactic1, tactic2); // Act Action? enabledAction = parentTactic.GetAction(); @@ -113,21 +128,21 @@ public void GetAction_WhenTacticTypeIsPrimitiveAndActionIsNotActionable_ReturnsE } /// - /// Given a tactic with a guard that returns true and an action, - /// When calling the Execute method, - /// Then _result should be "def". + /// Given a tactic with a guard that returns false, + /// When checking if the tactic is actionable, + /// Then the result should be false. /// [Fact] - public void Execute_WhenGuardReturnsTrue_ActionIsExecuted() + public void IsActionable_WhenGuardReturnsFalse_ReturnsFalse() { // Arrange - PrimitiveTactic tactic = new(_filledAction, TrueGuard); + PrimitiveTactic tactic = new(_emptyAction, FalseGuard); // Act - tactic.GetAction()!.Execute(); + bool isActionable = tactic.IsActionable(); // Assert - Assert.Equal("def", _result); + Assert.False(isActionable); } /// @@ -148,21 +163,7 @@ public void IsActionable_WhenGuardReturnsTrue_ReturnsTrue() Assert.True(isActionable); } - /// - /// Given a tactic with a guard that returns false, - /// When checking if the tactic is actionable, - /// Then the result should be false. - /// - [Fact] - public void IsActionable_WhenGuardReturnsFalse_ReturnsFalse() - { - // Arrange - PrimitiveTactic tactic = new(_emptyAction, FalseGuard); - - // Act - bool isActionable = tactic.IsActionable(); + private static bool FalseGuard() => false; - // Assert - Assert.False(isActionable); - } + private static bool TrueGuard() => true; } diff --git a/Aplib.Tests/Desire/GoalStructureTests.cs b/Aplib.Tests/Desire/GoalStructureTests.cs deleted file mode 100644 index eed1a1cc..00000000 --- a/Aplib.Tests/Desire/GoalStructureTests.cs +++ /dev/null @@ -1,8 +0,0 @@ -using FluentAssertions; - -namespace Aplib.Tests.Desire; - -public class GoalStructureTests -{ - -} diff --git a/Aplib.Tests/Stubs/Desire/TacticStub.cs b/Aplib.Tests/Stubs/Desire/TacticStub.cs deleted file mode 100644 index 5843fc4e..00000000 --- a/Aplib.Tests/Stubs/Desire/TacticStub.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Aplib.Core.Intent.Tactics; -using Action = Aplib.Core.Intent.Actions.Action; - -namespace Aplib.Tests.Stubs.Desire; - -/// -/// A fake tactic, which is just a wrapper around the you define as argument. -/// -/// The method to be executed during iteration. -internal class TacticStub(Action iteration) : Tactic -{ - /// - public override Action? GetAction() => iteration; -} diff --git a/Aplib.Tests/Tools/TestGoalBuilder.cs b/Aplib.Tests/Tools/TestGoalBuilder.cs index 8d6d0574..9cf586b2 100644 --- a/Aplib.Tests/Tools/TestGoalBuilder.cs +++ b/Aplib.Tests/Tools/TestGoalBuilder.cs @@ -1,24 +1,22 @@ using Aplib.Core.Desire.Goals; using Aplib.Core.Intent.Tactics; -using Aplib.Tests.Stubs.Desire; -using Action = Aplib.Core.Intent.Actions.Action; +using Moq; namespace Aplib.Tests.Tools; internal sealed class TestGoalBuilder { - private Tactic _tactic = new TacticStub(new Action(() => { })); + private string _description = "\"A lie is just a good story that someone ruined with the truth.\" ~ Barney Stinson"; private Goal.HeuristicFunction _heuristicFunction = CommonHeuristicFunctions.Constant(0); private string _name = "Such a good goal name"; - private string _description = "\"A lie is just a good story that someone ruined with the truth.\" ~ Barney Stinson"; - - public TestGoalBuilder() { } + private Tactic _tactic = Mock.Of(); public TestGoalBuilder WithHeuristicFunction(Goal.HeuristicFunction heuristicFunction) { _heuristicFunction = heuristicFunction; return this; } + public TestGoalBuilder WithHeuristicFunction(Func heuristicFunction) => WithHeuristicFunction(CommonHeuristicFunctions.Boolean(heuristicFunction)); From 4de93236b152d4b99938c57030419aae02c26839 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sun, 31 Mar 2024 00:03:27 +0100 Subject: [PATCH 09/19] chore: conform to sonar --- Aplib.Core/Desire/FirstOfGoalStructure.cs | 4 ++++ Aplib.Core/Desire/GoalStructureState.cs | 2 +- Aplib.Core/Desire/Goals/Goal.cs | 5 +++-- Aplib.Core/Desire/Goals/IGoal.cs | 3 +++ Aplib.Core/Desire/PrimitiveGoalStructure.cs | 2 +- Aplib.Core/Desire/RepeatGoalStructure.cs | 2 +- Aplib.Core/Desire/SequentialGoalStructure.cs | 5 ++++- Aplib.Core/InterruptableEventArgs.cs | 6 +++++- 8 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Aplib.Core/Desire/FirstOfGoalStructure.cs b/Aplib.Core/Desire/FirstOfGoalStructure.cs index 4e5596a3..e9419f14 100644 --- a/Aplib.Core/Desire/FirstOfGoalStructure.cs +++ b/Aplib.Core/Desire/FirstOfGoalStructure.cs @@ -17,6 +17,10 @@ public class FirstOfGoalStructure : GoalStructure, IDisp { private IEnumerator> _childrenEnumerator { get; } + /// + /// Initializes a new instance of the class. + /// + /// The children of the goal structure. public FirstOfGoalStructure(IList> children) : base(children) { _childrenEnumerator = children.GetEnumerator(); diff --git a/Aplib.Core/Desire/GoalStructureState.cs b/Aplib.Core/Desire/GoalStructureState.cs index 22c9500e..c6dd2668 100644 --- a/Aplib.Core/Desire/GoalStructureState.cs +++ b/Aplib.Core/Desire/GoalStructureState.cs @@ -1,7 +1,7 @@ namespace Aplib.Core.Desire { /// - /// Represents the state of a . + /// Represents the state of a . /// public enum GoalStructureState { diff --git a/Aplib.Core/Desire/Goals/Goal.cs b/Aplib.Core/Desire/Goals/Goal.cs index b3c7bab8..819c36a8 100644 --- a/Aplib.Core/Desire/Goals/Goal.cs +++ b/Aplib.Core/Desire/Goals/Goal.cs @@ -6,10 +6,11 @@ namespace Aplib.Core.Desire.Goals { /// /// A goal effectively combines a heuristic function with a tactic, and aims to meet the heuristic function by - /// applying the tactic. Goals are combined in a , and are used to prepare tests or do + /// applying the tactic. Goals are combined in a , and are used to prepare tests + /// or do /// the testing. /// - /// + /// public class Goal : IGoal { /// diff --git a/Aplib.Core/Desire/Goals/IGoal.cs b/Aplib.Core/Desire/Goals/IGoal.cs index c26c4ffa..4430d05b 100644 --- a/Aplib.Core/Desire/Goals/IGoal.cs +++ b/Aplib.Core/Desire/Goals/IGoal.cs @@ -3,6 +3,9 @@ namespace Aplib.Core.Desire.Goals { + /// + /// Defines a goal that can be achieved by a . + /// public interface IGoal { /// diff --git a/Aplib.Core/Desire/PrimitiveGoalStructure.cs b/Aplib.Core/Desire/PrimitiveGoalStructure.cs index 5d2b90ad..da61c971 100644 --- a/Aplib.Core/Desire/PrimitiveGoalStructure.cs +++ b/Aplib.Core/Desire/PrimitiveGoalStructure.cs @@ -20,7 +20,7 @@ public class PrimitiveGoalStructure : GoalStructure private readonly IGoal _goal; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The goal to fulfill. public PrimitiveGoalStructure(IGoal goal) : base(Array.Empty>()) => _goal = goal; diff --git a/Aplib.Core/Desire/RepeatGoalStructure.cs b/Aplib.Core/Desire/RepeatGoalStructure.cs index 8bb6ad75..3275c33c 100644 --- a/Aplib.Core/Desire/RepeatGoalStructure.cs +++ b/Aplib.Core/Desire/RepeatGoalStructure.cs @@ -16,7 +16,7 @@ public class RepeatGoalStructure : GoalStructure where TBeliefSet : IBeliefSet { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The goalstructure to repeat public RepeatGoalStructure(IGoalStructure goalStructure) : base( diff --git a/Aplib.Core/Desire/SequentialGoalStructure.cs b/Aplib.Core/Desire/SequentialGoalStructure.cs index 6a7da68f..41c0af4a 100644 --- a/Aplib.Core/Desire/SequentialGoalStructure.cs +++ b/Aplib.Core/Desire/SequentialGoalStructure.cs @@ -21,7 +21,10 @@ public class SequentialGoalStructure : GoalStructure, ID /// private IEnumerator> _childrenEnumerator { get; set; } - /// + /// + /// Initializes a new instance of the class. + /// + /// The children of the goal structure. public SequentialGoalStructure(IList> children) : base(children) { if (children.Count <= 0) diff --git a/Aplib.Core/InterruptableEventArgs.cs b/Aplib.Core/InterruptableEventArgs.cs index 4ec69c86..bcf3caa1 100644 --- a/Aplib.Core/InterruptableEventArgs.cs +++ b/Aplib.Core/InterruptableEventArgs.cs @@ -3,11 +3,15 @@ namespace Aplib.Core.Desire { + /// + /// Describes the parameters of an interruptable event. + /// + /// The beliefset of the agent. public class InterruptableEventArgs : EventArgs where TBeliefSet : IBeliefSet { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The beliefset of the agent. public InterruptableEventArgs(TBeliefSet beliefSet) => BeliefSet = beliefSet; From c6f1dc5adee4753b31eb25e62cb09560f40daef1 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sun, 31 Mar 2024 16:42:32 +0200 Subject: [PATCH 10/19] fix: conform to Jens --- ...lStructureState.cs => CompletionStatus.cs} | 4 +- Aplib.Core/Desire/FirstOfGoalStructure.cs | 30 +- Aplib.Core/Desire/GoalStructure.cs | 14 +- Aplib.Core/Desire/Goals/Goal.cs | 13 +- Aplib.Core/Desire/Goals/GoalState.cs | 23 -- Aplib.Core/Desire/Goals/IGoal.cs | 4 +- Aplib.Core/Desire/IGoalStructure.cs | 13 +- Aplib.Core/Desire/PrimitiveGoalStructure.cs | 10 +- Aplib.Core/Desire/RepeatGoalStructure.cs | 12 +- Aplib.Core/Desire/SequentialGoalStructure.cs | 39 +- Aplib.Core/ICompletable.cs | 13 + .../{Interruptable.cs => IInterruptable.cs} | 0 Aplib.Tests/Core/Desire/GoalStructureTests.cs | 364 +++++++++--------- 13 files changed, 241 insertions(+), 298 deletions(-) rename Aplib.Core/{Desire/GoalStructureState.cs => CompletionStatus.cs} (89%) delete mode 100644 Aplib.Core/Desire/Goals/GoalState.cs create mode 100644 Aplib.Core/ICompletable.cs rename Aplib.Core/{Interruptable.cs => IInterruptable.cs} (100%) diff --git a/Aplib.Core/Desire/GoalStructureState.cs b/Aplib.Core/CompletionStatus.cs similarity index 89% rename from Aplib.Core/Desire/GoalStructureState.cs rename to Aplib.Core/CompletionStatus.cs index c6dd2668..445e80c5 100644 --- a/Aplib.Core/Desire/GoalStructureState.cs +++ b/Aplib.Core/CompletionStatus.cs @@ -1,9 +1,9 @@ -namespace Aplib.Core.Desire +namespace Aplib.Core { /// /// Represents the state of a . /// - public enum GoalStructureState + public enum CompletionStatus { /// /// Represents a goal structure that is not yet completed. diff --git a/Aplib.Core/Desire/FirstOfGoalStructure.cs b/Aplib.Core/Desire/FirstOfGoalStructure.cs index e9419f14..489c8ee1 100644 --- a/Aplib.Core/Desire/FirstOfGoalStructure.cs +++ b/Aplib.Core/Desire/FirstOfGoalStructure.cs @@ -26,22 +26,10 @@ public FirstOfGoalStructure(IList> children) : base(c _childrenEnumerator = children.GetEnumerator(); _childrenEnumerator.MoveNext(); _currentGoalStructure = _childrenEnumerator.Current; - - OnReinstate += (_, args) => - { - _childrenEnumerator.Reset(); - _childrenEnumerator.MoveNext(); - - _currentGoalStructure = _childrenEnumerator.Current!; - - State = _currentGoalStructure.State == GoalStructureState.Failure - ? GoalStructureState.Unfinished - : _currentGoalStructure.State; - }; } /// - public override IGoal? GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.GetCurrentGoal(beliefSet); + public override IGoal GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.GetCurrentGoal(beliefSet); /// public override void UpdateState(TBeliefSet beliefSet) @@ -50,28 +38,28 @@ public override void UpdateState(TBeliefSet beliefSet) // This loop is here to prevent tail recursion. while (true) { - if (State == GoalStructureState.Success) return; + if (Status == CompletionStatus.Success) return; _currentGoalStructure!.UpdateState(beliefSet); - switch (_currentGoalStructure.State) + switch (_currentGoalStructure.Status) { - case GoalStructureState.Unfinished: + case CompletionStatus.Unfinished: return; - case GoalStructureState.Success: - State = GoalStructureState.Success; + case CompletionStatus.Success: + Status = CompletionStatus.Success; return; } if (_childrenEnumerator.MoveNext()) { _currentGoalStructure = _childrenEnumerator.Current; - State = GoalStructureState.Unfinished; + Status = CompletionStatus.Unfinished; - // Update the state of the new goal structure + // Update the Status of the new goal structure continue; } - State = GoalStructureState.Failure; + Status = CompletionStatus.Failure; return; } } diff --git a/Aplib.Core/Desire/GoalStructure.cs b/Aplib.Core/Desire/GoalStructure.cs index e532980f..2822061d 100644 --- a/Aplib.Core/Desire/GoalStructure.cs +++ b/Aplib.Core/Desire/GoalStructure.cs @@ -16,14 +16,8 @@ public abstract class GoalStructure : IGoalStructure whe /// public event EventHandler>? OnReinstate; - /// - /// Gets or sets the state of the goal structure. - /// - /// - /// By default, the state is set to . - /// However, this can be changed by the goal structure. - /// - public GoalStructureState State { get; protected set; } = GoalStructureState.Unfinished; + /// + public CompletionStatus Status { get; protected set; } /// /// The children of the goal structure. @@ -31,7 +25,7 @@ public abstract class GoalStructure : IGoalStructure whe protected readonly IEnumerable> _children; /// - /// The goalstructure that is currently being fulfilled. + /// The goal structure that is currently being fulfilled. /// protected IGoalStructure? _currentGoalStructure; @@ -46,7 +40,7 @@ public abstract class GoalStructure : IGoalStructure whe /// /// The belief set of the agent. /// The current goal to be fulfilled. - public abstract IGoal? GetCurrentGoal(TBeliefSet beliefSet); + public abstract IGoal GetCurrentGoal(TBeliefSet beliefSet); /// public void Interrupt(TBeliefSet beliefSet) diff --git a/Aplib.Core/Desire/Goals/Goal.cs b/Aplib.Core/Desire/Goals/Goal.cs index 819c36a8..2ded9a07 100644 --- a/Aplib.Core/Desire/Goals/Goal.cs +++ b/Aplib.Core/Desire/Goals/Goal.cs @@ -37,6 +37,9 @@ public class Goal : IGoal /// public Tactic Tactic { get; } + /// + public CompletionStatus Status { get; protected set; } + /// /// The goal is considered to be completed, when the distance of the is below /// this value. @@ -112,8 +115,12 @@ public virtual Heuristics CurrentHeuristics(IBeliefSet beliefSet) /// /// An enum representing whether the goal is complete and if so, with what result. /// - public virtual GoalState GetState(IBeliefSet beliefSet) => CurrentHeuristics(beliefSet).Distance < _epsilon - ? GoalState.Success - : GoalState.Unfinished; + public virtual CompletionStatus GetState(IBeliefSet beliefSet) + { + Status = CurrentHeuristics(beliefSet).Distance < _epsilon + ? CompletionStatus.Success + : CompletionStatus.Unfinished; + return Status; + } } } diff --git a/Aplib.Core/Desire/Goals/GoalState.cs b/Aplib.Core/Desire/Goals/GoalState.cs deleted file mode 100644 index 7cd0746b..00000000 --- a/Aplib.Core/Desire/Goals/GoalState.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Aplib.Core.Desire.Goals -{ - /// - /// Represents the state of a goal. - /// - public enum GoalState - { - /// - /// The goal has not yet been completed. - /// - Unfinished, - - /// - /// The goal has been completed successfully. - /// - Success, - - /// - /// The goal has failed. - /// - Failure - } -} diff --git a/Aplib.Core/Desire/Goals/IGoal.cs b/Aplib.Core/Desire/Goals/IGoal.cs index 4430d05b..66a733d4 100644 --- a/Aplib.Core/Desire/Goals/IGoal.cs +++ b/Aplib.Core/Desire/Goals/IGoal.cs @@ -6,7 +6,7 @@ namespace Aplib.Core.Desire.Goals /// /// Defines a goal that can be achieved by a . /// - public interface IGoal + public interface IGoal : ICompletable { /// /// The used to achieve this , which is executed during every @@ -29,6 +29,6 @@ public interface IGoal /// /// An enum representing whether the goal is complete and if so, with what result. /// - GoalState GetState(IBeliefSet beliefSet); + CompletionStatus GetState(IBeliefSet beliefSet); } } diff --git a/Aplib.Core/Desire/IGoalStructure.cs b/Aplib.Core/Desire/IGoalStructure.cs index c29a7ce2..95020730 100644 --- a/Aplib.Core/Desire/IGoalStructure.cs +++ b/Aplib.Core/Desire/IGoalStructure.cs @@ -10,24 +10,15 @@ namespace Aplib.Core.Desire /// A goal structure is structure of predicates that must be fulfilled in order to complete a test. /// /// - public interface IGoalStructure : IInterruptable + public interface IGoalStructure : IInterruptable, ICompletable where TBeliefSet : IBeliefSet { - /// - /// Gets or sets the state of the goal structure. - /// - /// - /// By default, the state is set to . - /// However, this can be changed by the goal structure itself. - /// - GoalStructureState State { get; } - /// /// Gets the current goal using the given . /// /// The belief set of the agent. /// The current goal to be fulfilled. - IGoal? GetCurrentGoal(TBeliefSet beliefSet); + IGoal GetCurrentGoal(TBeliefSet beliefSet); /// /// Updates the state of the goal structure. diff --git a/Aplib.Core/Desire/PrimitiveGoalStructure.cs b/Aplib.Core/Desire/PrimitiveGoalStructure.cs index da61c971..05cecd52 100644 --- a/Aplib.Core/Desire/PrimitiveGoalStructure.cs +++ b/Aplib.Core/Desire/PrimitiveGoalStructure.cs @@ -1,7 +1,6 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; using System; -using static Aplib.Core.Desire.Goals.GoalState; namespace Aplib.Core.Desire { @@ -26,15 +25,10 @@ public class PrimitiveGoalStructure : GoalStructure public PrimitiveGoalStructure(IGoal goal) : base(Array.Empty>()) => _goal = goal; /// - public override IGoal? GetCurrentGoal(TBeliefSet beliefSet) => _goal; + public override IGoal GetCurrentGoal(TBeliefSet beliefSet) => _goal; /// public override void UpdateState(TBeliefSet beliefSet) => - State = _goal.GetState(beliefSet) switch - { - Unfinished => GoalStructureState.Unfinished, - Success => GoalStructureState.Success, - _ => GoalStructureState.Failure - }; + Status = _goal.GetState(beliefSet); } } diff --git a/Aplib.Core/Desire/RepeatGoalStructure.cs b/Aplib.Core/Desire/RepeatGoalStructure.cs index 3275c33c..02663969 100644 --- a/Aplib.Core/Desire/RepeatGoalStructure.cs +++ b/Aplib.Core/Desire/RepeatGoalStructure.cs @@ -1,7 +1,7 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; using System.Collections.Generic; -using static Aplib.Core.Desire.GoalStructureState; +using static Aplib.Core.CompletionStatus; namespace Aplib.Core.Desire { @@ -24,7 +24,7 @@ public RepeatGoalStructure(IGoalStructure goalStructure) : base( _currentGoalStructure = goalStructure; /// - public override IGoal? GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.State switch + public override IGoal GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.Status switch { Unfinished or Failure => _currentGoalStructure.GetCurrentGoal(beliefSet), _ => FinishRepeat(beliefSet) @@ -35,18 +35,18 @@ public override void UpdateState(TBeliefSet beliefSet) { _currentGoalStructure!.UpdateState(beliefSet); - if (_currentGoalStructure.State == Failure) _currentGoalStructure.Reinstate(beliefSet); + if (_currentGoalStructure.Status == Failure) _currentGoalStructure.Reinstate(beliefSet); - State = _currentGoalStructure.State switch + Status = _currentGoalStructure.Status switch { Failure or Unfinished => Unfinished, _ => Success }; } - private IGoal? FinishRepeat(TBeliefSet beliefSet) + private IGoal FinishRepeat(TBeliefSet beliefSet) { - State = Success; + Status = Success; return _currentGoalStructure!.GetCurrentGoal(beliefSet); } } diff --git a/Aplib.Core/Desire/SequentialGoalStructure.cs b/Aplib.Core/Desire/SequentialGoalStructure.cs index 41c0af4a..b9e829ae 100644 --- a/Aplib.Core/Desire/SequentialGoalStructure.cs +++ b/Aplib.Core/Desire/SequentialGoalStructure.cs @@ -19,7 +19,7 @@ public class SequentialGoalStructure : GoalStructure, ID /// /// Gets or sets the enumerator for the children of the goal structure. /// - private IEnumerator> _childrenEnumerator { get; set; } + private IEnumerator> _childrenEnumerator { get; } /// /// Initializes a new instance of the class. @@ -32,8 +32,6 @@ public SequentialGoalStructure(IList> children) : bas _childrenEnumerator = _children.GetEnumerator(); _childrenEnumerator.MoveNext(); _currentGoalStructure = _childrenEnumerator.Current; - - OnReinstate += CheckForGoalCompletion; } /// @@ -46,17 +44,17 @@ public override void UpdateState(TBeliefSet beliefSet) // This loop is here to prevent tail recursion. while (true) { - if (State == GoalStructureState.Success) return; + if (Status == CompletionStatus.Success) return; _currentGoalStructure!.UpdateState(beliefSet); - switch (_currentGoalStructure.State) + switch (_currentGoalStructure.Status) { - case GoalStructureState.Unfinished: + case CompletionStatus.Unfinished: return; - case GoalStructureState.Failure: - State = GoalStructureState.Failure; + case CompletionStatus.Failure: + Status = CompletionStatus.Failure; return; - case GoalStructureState.Success: + case CompletionStatus.Success: default: break; } @@ -64,13 +62,13 @@ public override void UpdateState(TBeliefSet beliefSet) if (_childrenEnumerator.MoveNext()) { _currentGoalStructure = _childrenEnumerator.Current; - State = GoalStructureState.Unfinished; + Status = CompletionStatus.Unfinished; // Update the state of the new goal structure continue; } - State = GoalStructureState.Success; + Status = CompletionStatus.Success; return; } } @@ -87,24 +85,5 @@ public void Dispose() /// /// Whether the object is being disposed. protected virtual void Dispose(bool disposing) => _childrenEnumerator.Dispose(); - - private void CheckForGoalCompletion(object sender, InterruptableEventArgs e) - { - // Check if the previous goals are still completed - IEnumerator> enumerator = _children.GetEnumerator(); - while (enumerator.Current != _childrenEnumerator.Current) - { - enumerator.MoveNext(); - enumerator.Current!.UpdateState(e.BeliefSet); - if (enumerator.Current!.State == GoalStructureState.Success) continue; - - State = GoalStructureState.Unfinished; - - // If the goal is not completed, retry the goal and reset the enumerator. - _childrenEnumerator = enumerator; - _currentGoalStructure = _childrenEnumerator.Current; - return; - } - } } } diff --git a/Aplib.Core/ICompletable.cs b/Aplib.Core/ICompletable.cs new file mode 100644 index 00000000..7843d330 --- /dev/null +++ b/Aplib.Core/ICompletable.cs @@ -0,0 +1,13 @@ +namespace Aplib.Core +{ + /// + /// Defines an object that can be completed. + /// + public interface ICompletable + { + /// + /// Gets the completion status of the object. + /// + public CompletionStatus Status { get; } + } +} diff --git a/Aplib.Core/Interruptable.cs b/Aplib.Core/IInterruptable.cs similarity index 100% rename from Aplib.Core/Interruptable.cs rename to Aplib.Core/IInterruptable.cs diff --git a/Aplib.Tests/Core/Desire/GoalStructureTests.cs b/Aplib.Tests/Core/Desire/GoalStructureTests.cs index f32231e8..483e280c 100644 --- a/Aplib.Tests/Core/Desire/GoalStructureTests.cs +++ b/Aplib.Tests/Core/Desire/GoalStructureTests.cs @@ -9,98 +9,97 @@ namespace Aplib.Tests.Core.Desire; public class GoalStructureTests { - [Theory] - [InlineData(GoalState.Success, GoalStructureState.Success)] - [InlineData(GoalState.Failure, GoalStructureState.Failure)] - [InlineData(GoalState.Unfinished, GoalStructureState.Unfinished)] - public void PrimitiveGoalStructure_WhenGoalHasState_ShouldHaveSameState(GoalState state, - GoalStructureState expected) + [Fact] + public void FirstOfGoalStructure_WhenAllGoalsFail_ShouldReturnFailure() { - // Arrange - Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(state); + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Failure); + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Failure); + BeliefSet beliefSet = Mock.Of(); - PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); // Act - primitiveGoalStructure.UpdateState(beliefSet); + firstOfGoalStructure.UpdateState(beliefSet); // Assert - primitiveGoalStructure.State.Should().Be(expected); + firstOfGoalStructure.State.Should().Be(CompletionStatus.Failure); } [Fact] - public void PrimitiveGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() + public void FirstOfGoalStructure_WhenDisposing_ShouldDisposeChildren() { // Arrange - Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Unfinished); - BeliefSet beliefSet = Mock.Of(); - PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + Mock> goalStructure1 = new(); + Mock> goalStructure2 = new(); + + Mock> firstOfGoalStructure = + new(new List> { goalStructure1.Object, goalStructure2.Object }) + { + CallBase = true + }; // Act - primitiveGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = primitiveGoalStructure.GetCurrentGoal(beliefSet); + firstOfGoalStructure.Object.Dispose(); // Assert - currentGoal.Should().Be(goal.Object); + firstOfGoalStructure.Protected().Verify("Dispose", Times.Once(), ItExpr.IsAny()); } [Fact] - public void RepeatGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() + public void FirstOfGoalStructure_WhenFinished_ShouldEarlyExit() { - // Arrange - Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Unfinished); - BeliefSet beliefSet = Mock.Of(); - PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); - RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); - - // Act - repeatGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); - // Assert - repeatGoalStructure.State.Should().Be(GoalStructureState.Unfinished); - currentGoal.Should().Be(goal.Object); - } + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Success); - [Fact] - public void RepeatGoalStructure_WhenGoalStructureHasFailed_ShouldReturnGoal() - { - // Arrange - Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Failure); BeliefSet beliefSet = Mock.Of(); - PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); - RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + + firstOfGoalStructure.UpdateState(beliefSet); // Act - repeatGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); + firstOfGoalStructure.UpdateState(beliefSet); // Assert - repeatGoalStructure.State.Should().Be(GoalStructureState.Unfinished); - currentGoal.Should().Be(goal.Object); + firstOfGoalStructure.State.Should().Be(CompletionStatus.Success); + goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); } [Fact] - public void RepeatGoalStructure_WhenGoalStructureHasSucceeded_ShouldSucceed() + public void FirstOfGoalStructure_WhenFirstGoalIsFailure_ShouldReturnSecondGoal() { // Arrange - Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Success); + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Failure); + Mock> goalStructure2 = new(); + IGoal goal = Mock.Of(); + goalStructure2 + .Setup(g => g.GetCurrentGoal(It.IsAny())) + .Returns(goal); + BeliefSet beliefSet = Mock.Of(); - PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); - RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); // Act - repeatGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); + firstOfGoalStructure.UpdateState(beliefSet); + IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; // Assert - repeatGoalStructure.State.Should().Be(GoalStructureState.Success); - currentGoal.Should().Be(goal.Object); + firstOfGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + currentGoal.Should().Be(goal); } [Fact] @@ -108,7 +107,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFinished_ShouldReturnSuccess() { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); Mock> goalStructure2 = new(); BeliefSet beliefSet = Mock.Of(); FirstOfGoalStructure firstOfGoalStructure = new(new List> @@ -120,7 +119,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFinished_ShouldReturnSuccess() firstOfGoalStructure.UpdateState(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(GoalStructureState.Success); + firstOfGoalStructure.State.Should().Be(CompletionStatus.Success); } [Fact] @@ -135,7 +134,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsUnfinished_ShouldReturnUnfinishe goalStructure1 .SetupGet(g => g.State) - .Returns(GoalStructureState.Unfinished); + .Returns(CompletionStatus.Unfinished); Mock> goalStructure2 = new(); BeliefSet beliefSet = Mock.Of(); @@ -149,21 +148,21 @@ public void FirstOfGoalStructure_WhenFirstGoalIsUnfinished_ShouldReturnUnfinishe IGoal? currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + firstOfGoalStructure.State.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(goal.Object); } [Fact] - public void FirstOfGoalStructure_WhenFirstGoalIsFailure_ShouldReturnSecondGoal() + public void FirstOfGoalStructure_WhenGoalIsUnfinished_ShouldReturnGoal() { - // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Failure); - Mock> goalStructure2 = new(); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); + IGoal goal = Mock.Of(); - goalStructure2 - .Setup(g => g.GetCurrentGoal(It.IsAny())) - .Returns(goal); + goalStructure1.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(goal); + + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); BeliefSet beliefSet = Mock.Of(); FirstOfGoalStructure firstOfGoalStructure = new(new List> @@ -173,19 +172,19 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFailure_ShouldReturnSecondGoal() // Act firstOfGoalStructure.UpdateState(beliefSet); - IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; + IGoal? currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + firstOfGoalStructure.State.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(goal); } [Theory] - [InlineData(GoalStructureState.Success, GoalStructureState.Success)] - [InlineData(GoalStructureState.Failure, GoalStructureState.Unfinished)] - [InlineData(GoalStructureState.Unfinished, GoalStructureState.Unfinished)] - public void FirstOfGoalStructure_WhenInterrupted_ShouldRecalculateState(GoalStructureState input, - GoalStructureState expected) + [InlineData(CompletionStatus.Success, CompletionStatus.Success)] + [InlineData(CompletionStatus.Failure, CompletionStatus.Unfinished)] + [InlineData(CompletionStatus.Unfinished, CompletionStatus.Unfinished)] + public void FirstOfGoalStructure_WhenInterrupted_ShouldRecalculateState(CompletionStatus input, + CompletionStatus expected) { // Arrange Mock> goalStructure1 = new(); @@ -196,7 +195,7 @@ public void FirstOfGoalStructure_WhenInterrupted_ShouldRecalculateState(GoalStru .Returns(goal); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Success); + goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Success); BeliefSet beliefSet = Mock.Of(); FirstOfGoalStructure firstOfGoalStructure = new(new List> @@ -215,138 +214,151 @@ public void FirstOfGoalStructure_WhenInterrupted_ShouldRecalculateState(GoalStru currentGoal.Should().Be(goal); } - [Fact] - public void FirstOfGoalStructure_WhenAllGoalsFail_ShouldReturnFailure() + [Theory] + [InlineData(GoalState.Success, CompletionStatus.Success)] + [InlineData(GoalState.Failure, CompletionStatus.Failure)] + [InlineData(GoalState.Unfinished, CompletionStatus.Unfinished)] + public void PrimitiveGoalStructure_WhenGoalHasState_ShouldHaveSameState(GoalState state, + CompletionStatus expected) { - Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Failure); - Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Failure); - + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(state); BeliefSet beliefSet = Mock.Of(); - FirstOfGoalStructure firstOfGoalStructure = new(new List> - { - goalStructure1.Object, goalStructure2.Object - }); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); // Act - firstOfGoalStructure.UpdateState(beliefSet); + primitiveGoalStructure.UpdateState(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(GoalStructureState.Failure); + primitiveGoalStructure.State.Should().Be(expected); } [Fact] - public void FirstOfGoalStructure_WhenFinished_ShouldEarlyExit() + public void PrimitiveGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() { - Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Unfinished); + BeliefSet beliefSet = Mock.Of(); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); - Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Success); + // Act + primitiveGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = primitiveGoalStructure.GetCurrentGoal(beliefSet); - BeliefSet beliefSet = Mock.Of(); - FirstOfGoalStructure firstOfGoalStructure = new(new List> - { - goalStructure1.Object, goalStructure2.Object - }); + // Assert + currentGoal.Should().Be(goal.Object); + } - firstOfGoalStructure.UpdateState(beliefSet); + [Fact] + public void RepeatGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() + { + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Unfinished); + BeliefSet beliefSet = Mock.Of(); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); // Act - firstOfGoalStructure.UpdateState(beliefSet); + repeatGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(GoalStructureState.Success); - goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); + repeatGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + currentGoal.Should().Be(goal.Object); } [Fact] - public void FirstOfGoalStructure_WhenGoalIsUnfinished_ShouldReturnGoal() + public void RepeatGoalStructure_WhenGoalStructureHasFailed_ShouldReturnGoal() { - Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Failure); + BeliefSet beliefSet = Mock.Of(); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); - IGoal goal = Mock.Of(); - goalStructure1.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(goal); + // Act + repeatGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); - Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); + // Assert + repeatGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + currentGoal.Should().Be(goal.Object); + } + [Fact] + public void RepeatGoalStructure_WhenGoalStructureHasSucceeded_ShouldSucceed() + { + // Arrange + Mock goal = new(); + goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Success); BeliefSet beliefSet = Mock.Of(); - FirstOfGoalStructure firstOfGoalStructure = new(new List> - { - goalStructure1.Object, goalStructure2.Object - }); + PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); + RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); // Act - firstOfGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); + repeatGoalStructure.UpdateState(beliefSet); + IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(GoalStructureState.Unfinished); - currentGoal.Should().Be(goal); + repeatGoalStructure.State.Should().Be(CompletionStatus.Success); + currentGoal.Should().Be(goal.Object); } [Fact] - public void FirstOfGoalStructure_WhenDisposing_ShouldDisposeChildren() + public void SequentialGoalStructure_WhenDisposing_ShouldDisposeChildren() { // Arrange Mock> goalStructure1 = new(); Mock> goalStructure2 = new(); - Mock> firstOfGoalStructure = + Mock> sequentialGoalStructure = new(new List> { goalStructure1.Object, goalStructure2.Object }) { CallBase = true }; // Act - firstOfGoalStructure.Object.Dispose(); - - // Assert - firstOfGoalStructure.Protected().Verify("Dispose", Times.Once(), ItExpr.IsAny()); - } - - [Fact] - public void SequentialGoalStructure_WhenProvidingNoGoalStructure_ShouldThrowException() - { - // Arrange - List> goalStructures = new(); - - // Act - Func> act = () => new SequentialGoalStructure(goalStructures); + sequentialGoalStructure.Object.Dispose(); // Assert - act.Should().Throw(); + sequentialGoalStructure.Protected().Verify("Dispose", Times.Once(), ItExpr.IsAny()); } [Fact] - public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldReturnUnfinished() + public void SequentialGoalStructure_WhenFinished_ShouldEarlyExit() { - // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Success); + BeliefSet beliefSet = Mock.Of(); SequentialGoalStructure sequentialGoalStructure = new(new List> { goalStructure1.Object, goalStructure2.Object }); + sequentialGoalStructure.UpdateState(beliefSet); + // Act sequentialGoalStructure.UpdateState(beliefSet); // Assert - sequentialGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + sequentialGoalStructure.State.Should().Be(CompletionStatus.Success); + goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); } [Fact] - public void SequentialGoalStructure_WhenGoalFails_ShouldReturnFailure() + public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldReturnUnfinished() { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Failure); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); Mock> goalStructure2 = new(); BeliefSet beliefSet = Mock.Of(); SequentialGoalStructure sequentialGoalStructure = new(new List> @@ -358,34 +370,27 @@ public void SequentialGoalStructure_WhenGoalFails_ShouldReturnFailure() sequentialGoalStructure.UpdateState(beliefSet); // Assert - sequentialGoalStructure.State.Should().Be(GoalStructureState.Failure); + sequentialGoalStructure.State.Should().Be(CompletionStatus.Unfinished); } [Fact] - public void SequentialGoalStructure_WhenInterruptedWithNoChanges_ShouldReturnExactSameGoal() + public void SequentialGoalStructure_WhenGoalFails_ShouldReturnFailure() { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Failure); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); - IGoal expected = Mock.Of(); - goalStructure2.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); BeliefSet beliefSet = Mock.Of(); SequentialGoalStructure sequentialGoalStructure = new(new List> { goalStructure1.Object, goalStructure2.Object }); - sequentialGoalStructure.UpdateState(beliefSet); // Act - sequentialGoalStructure.Interrupt(beliefSet); - sequentialGoalStructure.Reinstate(beliefSet); - IGoal currentGoal = sequentialGoalStructure.GetCurrentGoal(beliefSet)!; + sequentialGoalStructure.UpdateState(beliefSet); // Assert - sequentialGoalStructure.State.Should().Be(GoalStructureState.Unfinished); - currentGoal.Should().Be(expected); + sequentialGoalStructure.State.Should().Be(CompletionStatus.Failure); } [Fact] @@ -393,12 +398,12 @@ public void SequentialGoalStructure_WhenInterruptedWithChanges_ShouldReturnSameG { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); IGoal expected = Mock.Of(); goalStructure1.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); + goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); BeliefSet beliefSet = Mock.Of(); SequentialGoalStructure sequentialGoalStructure = new(new List> @@ -406,7 +411,7 @@ public void SequentialGoalStructure_WhenInterruptedWithChanges_ShouldReturnSameG goalStructure1.Object, goalStructure2.Object }); sequentialGoalStructure.UpdateState(beliefSet); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Unfinished); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); // Act sequentialGoalStructure.Interrupt(beliefSet); @@ -414,52 +419,47 @@ public void SequentialGoalStructure_WhenInterruptedWithChanges_ShouldReturnSameG IGoal currentGoal = sequentialGoalStructure.GetCurrentGoal(beliefSet)!; // Assert - sequentialGoalStructure.State.Should().Be(GoalStructureState.Unfinished); + sequentialGoalStructure.State.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(expected); } [Fact] - public void SequentialGoalStructure_WhenDisposing_ShouldDisposeChildren() + public void SequentialGoalStructure_WhenInterruptedWithNoChanges_ShouldReturnExactSameGoal() { // Arrange Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); Mock> goalStructure2 = new(); - - Mock> sequentialGoalStructure = - new(new List> { goalStructure1.Object, goalStructure2.Object }) - { - CallBase = true - }; + goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); + IGoal expected = Mock.Of(); + goalStructure2.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); + BeliefSet beliefSet = Mock.Of(); + SequentialGoalStructure sequentialGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + sequentialGoalStructure.UpdateState(beliefSet); // Act - sequentialGoalStructure.Object.Dispose(); + sequentialGoalStructure.Interrupt(beliefSet); + sequentialGoalStructure.Reinstate(beliefSet); + IGoal currentGoal = sequentialGoalStructure.GetCurrentGoal(beliefSet)!; // Assert - sequentialGoalStructure.Protected().Verify("Dispose", Times.Once(), ItExpr.IsAny()); + sequentialGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + currentGoal.Should().Be(expected); } [Fact] - public void SequentialGoalStructure_WhenFinished_ShouldEarlyExit() + public void SequentialGoalStructure_WhenProvidingNoGoalStructure_ShouldThrowException() { - Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(GoalStructureState.Success); - - Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(GoalStructureState.Success); - - BeliefSet beliefSet = Mock.Of(); - SequentialGoalStructure sequentialGoalStructure = new(new List> - { - goalStructure1.Object, goalStructure2.Object - }); - - sequentialGoalStructure.UpdateState(beliefSet); + // Arrange + List> goalStructures = new(); // Act - sequentialGoalStructure.UpdateState(beliefSet); + Func> act = () => new SequentialGoalStructure(goalStructures); // Assert - sequentialGoalStructure.State.Should().Be(GoalStructureState.Success); - goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); + act.Should().Throw(); } } From b8a393b1da9395cfb18733bc9c6d2fdb033de5ac Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sun, 31 Mar 2024 18:21:56 +0200 Subject: [PATCH 11/19] test: fix tests --- Aplib.Tests/Core/Desire/GoalStructureTests.cs | 127 +++++++++++------- Aplib.Tests/Core/Desire/GoalTests.cs | 83 ++++++------ 2 files changed, 121 insertions(+), 89 deletions(-) diff --git a/Aplib.Tests/Core/Desire/GoalStructureTests.cs b/Aplib.Tests/Core/Desire/GoalStructureTests.cs index 483e280c..8f40bbc8 100644 --- a/Aplib.Tests/Core/Desire/GoalStructureTests.cs +++ b/Aplib.Tests/Core/Desire/GoalStructureTests.cs @@ -1,3 +1,4 @@ +using Aplib.Core; using Aplib.Core.Belief; using Aplib.Core.Desire; using Aplib.Core.Desire.Goals; @@ -13,9 +14,9 @@ public class GoalStructureTests public void FirstOfGoalStructure_WhenAllGoalsFail_ShouldReturnFailure() { Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Failure); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Failure); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Failure); + goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Failure); BeliefSet beliefSet = Mock.Of(); FirstOfGoalStructure firstOfGoalStructure = new(new List> @@ -27,7 +28,7 @@ public void FirstOfGoalStructure_WhenAllGoalsFail_ShouldReturnFailure() firstOfGoalStructure.UpdateState(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(CompletionStatus.Failure); + firstOfGoalStructure.Status.Should().Be(CompletionStatus.Failure); } [Fact] @@ -54,10 +55,10 @@ public void FirstOfGoalStructure_WhenDisposing_ShouldDisposeChildren() public void FirstOfGoalStructure_WhenFinished_ShouldEarlyExit() { Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Success); + goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Success); BeliefSet beliefSet = Mock.Of(); FirstOfGoalStructure firstOfGoalStructure = new(new List> @@ -71,7 +72,7 @@ public void FirstOfGoalStructure_WhenFinished_ShouldEarlyExit() firstOfGoalStructure.UpdateState(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(CompletionStatus.Success); + firstOfGoalStructure.Status.Should().Be(CompletionStatus.Success); goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); } @@ -80,7 +81,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFailure_ShouldReturnSecondGoal() { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Failure); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Failure); Mock> goalStructure2 = new(); IGoal goal = Mock.Of(); goalStructure2 @@ -98,7 +99,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFailure_ShouldReturnSecondGoal() IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; // Assert - firstOfGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + firstOfGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(goal); } @@ -107,7 +108,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFinished_ShouldReturnSuccess() { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); Mock> goalStructure2 = new(); BeliefSet beliefSet = Mock.Of(); FirstOfGoalStructure firstOfGoalStructure = new(new List> @@ -119,7 +120,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFinished_ShouldReturnSuccess() firstOfGoalStructure.UpdateState(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(CompletionStatus.Success); + firstOfGoalStructure.Status.Should().Be(CompletionStatus.Success); } [Fact] @@ -133,7 +134,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsUnfinished_ShouldReturnUnfinishe .Returns(goal.Object); goalStructure1 - .SetupGet(g => g.State) + .SetupGet(g => g.Status) .Returns(CompletionStatus.Unfinished); Mock> goalStructure2 = new(); @@ -148,7 +149,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsUnfinished_ShouldReturnUnfinishe IGoal? currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + firstOfGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(goal.Object); } @@ -156,13 +157,13 @@ public void FirstOfGoalStructure_WhenFirstGoalIsUnfinished_ShouldReturnUnfinishe public void FirstOfGoalStructure_WhenGoalIsUnfinished_ShouldReturnGoal() { Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Unfinished); IGoal goal = Mock.Of(); goalStructure1.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(goal); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); + goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Unfinished); BeliefSet beliefSet = Mock.Of(); FirstOfGoalStructure firstOfGoalStructure = new(new List> @@ -175,27 +176,26 @@ public void FirstOfGoalStructure_WhenGoalIsUnfinished_ShouldReturnGoal() IGoal? currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert - firstOfGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + firstOfGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(goal); } [Theory] [InlineData(CompletionStatus.Success, CompletionStatus.Success)] - [InlineData(CompletionStatus.Failure, CompletionStatus.Unfinished)] [InlineData(CompletionStatus.Unfinished, CompletionStatus.Unfinished)] public void FirstOfGoalStructure_WhenInterrupted_ShouldRecalculateState(CompletionStatus input, CompletionStatus expected) { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(input); + goalStructure1.SetupGet(g => g.Status).Returns(input); IGoal goal = Mock.Of(); goalStructure1 .Setup(g => g.GetCurrentGoal(It.IsAny())) .Returns(goal); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Success); + goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Success); BeliefSet beliefSet = Mock.Of(); FirstOfGoalStructure firstOfGoalStructure = new(new List> @@ -210,20 +210,51 @@ public void FirstOfGoalStructure_WhenInterrupted_ShouldRecalculateState(Completi IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; // Assert - firstOfGoalStructure.State.Should().Be(expected); + firstOfGoalStructure.Status.Should().Be(expected); + currentGoal.Should().Be(goal); + } + + [Fact] + public void FirstOfGoalStructure_WhenInterruptedAndFailure_ShouldNotFail() + { + // Arrange + Mock> goalStructure1 = new(); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Failure); + + Mock> goalStructure2 = new(); + goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Success); + IGoal goal = Mock.Of(); + goalStructure2 + .Setup(g => g.GetCurrentGoal(It.IsAny())) + .Returns(goal); + + BeliefSet beliefSet = Mock.Of(); + FirstOfGoalStructure firstOfGoalStructure = new(new List> + { + goalStructure1.Object, goalStructure2.Object + }); + firstOfGoalStructure.UpdateState(beliefSet); + + // Act + firstOfGoalStructure.Interrupt(beliefSet); + firstOfGoalStructure.Reinstate(beliefSet); + IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; + + // Assert + firstOfGoalStructure.Status.Should().Be(CompletionStatus.Success); currentGoal.Should().Be(goal); } [Theory] - [InlineData(GoalState.Success, CompletionStatus.Success)] - [InlineData(GoalState.Failure, CompletionStatus.Failure)] - [InlineData(GoalState.Unfinished, CompletionStatus.Unfinished)] - public void PrimitiveGoalStructure_WhenGoalHasState_ShouldHaveSameState(GoalState state, + [InlineData(CompletionStatus.Success, CompletionStatus.Success)] + [InlineData(CompletionStatus.Failure, CompletionStatus.Failure)] + [InlineData(CompletionStatus.Unfinished, CompletionStatus.Unfinished)] + public void PrimitiveGoalStructure_WhenGoalHasState_ShouldHaveSameState(CompletionStatus Status, CompletionStatus expected) { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(state); + goal.Setup(g => g.GetState(It.IsAny())).Returns(Status); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); @@ -231,7 +262,7 @@ public void PrimitiveGoalStructure_WhenGoalHasState_ShouldHaveSameState(GoalStat primitiveGoalStructure.UpdateState(beliefSet); // Assert - primitiveGoalStructure.State.Should().Be(expected); + primitiveGoalStructure.Status.Should().Be(expected); } [Fact] @@ -239,7 +270,7 @@ public void PrimitiveGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Unfinished); + goal.Setup(g => g.GetState(It.IsAny())).Returns(CompletionStatus.Unfinished); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); @@ -256,7 +287,7 @@ public void RepeatGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Unfinished); + goal.Setup(g => g.GetState(It.IsAny())).Returns(CompletionStatus.Unfinished); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); @@ -266,7 +297,7 @@ public void RepeatGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert - repeatGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + repeatGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(goal.Object); } @@ -275,7 +306,7 @@ public void RepeatGoalStructure_WhenGoalStructureHasFailed_ShouldReturnGoal() { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Failure); + goal.Setup(g => g.GetState(It.IsAny())).Returns(CompletionStatus.Failure); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); @@ -285,7 +316,7 @@ public void RepeatGoalStructure_WhenGoalStructureHasFailed_ShouldReturnGoal() IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert - repeatGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + repeatGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(goal.Object); } @@ -294,7 +325,7 @@ public void RepeatGoalStructure_WhenGoalStructureHasSucceeded_ShouldSucceed() { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(GoalState.Success); + goal.Setup(g => g.GetState(It.IsAny())).Returns(CompletionStatus.Success); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); @@ -304,7 +335,7 @@ public void RepeatGoalStructure_WhenGoalStructureHasSucceeded_ShouldSucceed() IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert - repeatGoalStructure.State.Should().Be(CompletionStatus.Success); + repeatGoalStructure.Status.Should().Be(CompletionStatus.Success); currentGoal.Should().Be(goal.Object); } @@ -332,10 +363,10 @@ public void SequentialGoalStructure_WhenDisposing_ShouldDisposeChildren() public void SequentialGoalStructure_WhenFinished_ShouldEarlyExit() { Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Success); + goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Success); BeliefSet beliefSet = Mock.Of(); SequentialGoalStructure sequentialGoalStructure = new(new List> @@ -349,7 +380,7 @@ public void SequentialGoalStructure_WhenFinished_ShouldEarlyExit() sequentialGoalStructure.UpdateState(beliefSet); // Assert - sequentialGoalStructure.State.Should().Be(CompletionStatus.Success); + sequentialGoalStructure.Status.Should().Be(CompletionStatus.Success); goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); } @@ -358,7 +389,7 @@ public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldReturnUnfinish { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); Mock> goalStructure2 = new(); BeliefSet beliefSet = Mock.Of(); SequentialGoalStructure sequentialGoalStructure = new(new List> @@ -370,7 +401,7 @@ public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldReturnUnfinish sequentialGoalStructure.UpdateState(beliefSet); // Assert - sequentialGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + sequentialGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); } [Fact] @@ -378,7 +409,7 @@ public void SequentialGoalStructure_WhenGoalFails_ShouldReturnFailure() { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Failure); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Failure); Mock> goalStructure2 = new(); BeliefSet beliefSet = Mock.Of(); SequentialGoalStructure sequentialGoalStructure = new(new List> @@ -390,7 +421,7 @@ public void SequentialGoalStructure_WhenGoalFails_ShouldReturnFailure() sequentialGoalStructure.UpdateState(beliefSet); // Assert - sequentialGoalStructure.State.Should().Be(CompletionStatus.Failure); + sequentialGoalStructure.Status.Should().Be(CompletionStatus.Failure); } [Fact] @@ -398,12 +429,12 @@ public void SequentialGoalStructure_WhenInterruptedWithChanges_ShouldReturnSameG { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); - IGoal expected = Mock.Of(); - goalStructure1.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); + goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Unfinished); + IGoal expected = Mock.Of(); + goalStructure2.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); BeliefSet beliefSet = Mock.Of(); SequentialGoalStructure sequentialGoalStructure = new(new List> @@ -411,7 +442,7 @@ public void SequentialGoalStructure_WhenInterruptedWithChanges_ShouldReturnSameG goalStructure1.Object, goalStructure2.Object }); sequentialGoalStructure.UpdateState(beliefSet); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Unfinished); // Act sequentialGoalStructure.Interrupt(beliefSet); @@ -419,7 +450,7 @@ public void SequentialGoalStructure_WhenInterruptedWithChanges_ShouldReturnSameG IGoal currentGoal = sequentialGoalStructure.GetCurrentGoal(beliefSet)!; // Assert - sequentialGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + sequentialGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(expected); } @@ -428,9 +459,9 @@ public void SequentialGoalStructure_WhenInterruptedWithNoChanges_ShouldReturnExa { // Arrange Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.State).Returns(CompletionStatus.Success); + goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.State).Returns(CompletionStatus.Unfinished); + goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Unfinished); IGoal expected = Mock.Of(); goalStructure2.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); BeliefSet beliefSet = Mock.Of(); @@ -446,7 +477,7 @@ public void SequentialGoalStructure_WhenInterruptedWithNoChanges_ShouldReturnExa IGoal currentGoal = sequentialGoalStructure.GetCurrentGoal(beliefSet)!; // Assert - sequentialGoalStructure.State.Should().Be(CompletionStatus.Unfinished); + sequentialGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(expected); } diff --git a/Aplib.Tests/Core/Desire/GoalTests.cs b/Aplib.Tests/Core/Desire/GoalTests.cs index 30c5c568..43737439 100644 --- a/Aplib.Tests/Core/Desire/GoalTests.cs +++ b/Aplib.Tests/Core/Desire/GoalTests.cs @@ -1,3 +1,4 @@ +using Aplib.Core; using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; using Aplib.Core.Intent.Tactics; @@ -10,6 +11,33 @@ namespace Aplib.Tests.Core.Desire; public class GoalTests { + /// + /// A test belief set that contains two public simple beliefs. + /// + private class MyBeliefSet : BeliefSet + { + /// + /// Belief that sets Updated to true when UpdateBelief is called. + /// + public readonly SimpleBelief MyBelief = new(); + } + + /// + /// A simple belief that can be used to test whether has been called. + /// + private class SimpleBelief : IBelief + { + /// + /// Stores whether has been called. + /// + public bool Updated { get; private set; } + + /// + /// Sets to true. + /// + public void UpdateBelief() => Updated = true; + } + /// /// Given valid parameters and metadata, /// When the goal is constructed, @@ -55,43 +83,43 @@ public void Goal_WhenConstructed_DidNotIterateYet() } /// - /// Given the Goal's heuristic function is configured to have reached its goal + /// Given the Goal's heuristic function is configured to *not* have reached its goal, /// when the Evaluate() method of a goal is used, - /// then the method should return true. + /// then the method should return false. /// [Fact] - public void Goal_WhenReached_ReturnsAsCompleted() + public void Goal_WhenNotReached_DoesNotReturnAsCompleted() { // Arrange MyBeliefSet beliefSet = new(); - Goal.HeuristicFunction heuristicFunction = CommonHeuristicFunctions.Completed(); + Goal.HeuristicFunction heuristicFunction = CommonHeuristicFunctions.Uncompleted(); // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - GoalState isCompleted = goal.GetState(beliefSet); + CompletionStatus isCompleted = goal.GetState(beliefSet); // Assert - isCompleted.Should().Be(GoalState.Success); + isCompleted.Should().Be(CompletionStatus.Unfinished); } /// - /// Given the Goal's heuristic function is configured to *not* have reached its goal, + /// Given the Goal's heuristic function is configured to have reached its goal /// when the Evaluate() method of a goal is used, - /// then the method should return false. + /// then the method should return true. /// [Fact] - public void Goal_WhenNotReached_DoesNotReturnAsCompleted() + public void Goal_WhenReached_ReturnsAsCompleted() { // Arrange MyBeliefSet beliefSet = new(); - Goal.HeuristicFunction heuristicFunction = CommonHeuristicFunctions.Uncompleted(); + Goal.HeuristicFunction heuristicFunction = CommonHeuristicFunctions.Completed(); // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - GoalState isCompleted = goal.GetState(beliefSet); + CompletionStatus isCompleted = goal.GetState(beliefSet); // Assert - isCompleted.Should().Be(GoalState.Unfinished); + isCompleted.Should().Be(CompletionStatus.Success); } /// @@ -138,37 +166,10 @@ public void GoalConstructor_WhereHeuristicFunctionTypeDiffers_HasEqualBehaviour( // Act MyBeliefSet beliefSet = new(); - GoalState goalBooleanEvaluation = goalBoolean.GetState(beliefSet); - GoalState goalNonBooleanEvaluation = goalNonBoolean.GetState(beliefSet); + CompletionStatus goalBooleanEvaluation = goalBoolean.GetState(beliefSet); + CompletionStatus goalNonBooleanEvaluation = goalNonBoolean.GetState(beliefSet); // Assert goalBooleanEvaluation.Should().Be(goalNonBooleanEvaluation); } - - /// - /// A test belief set that contains two public simple beliefs. - /// - private class MyBeliefSet : BeliefSet - { - /// - /// Belief that sets Updated to true when UpdateBelief is called. - /// - public readonly SimpleBelief MyBelief = new(); - } - - /// - /// A simple belief that can be used to test whether has been called. - /// - private class SimpleBelief : IBelief - { - /// - /// Stores whether has been called. - /// - public bool Updated { get; private set; } - - /// - /// Sets to true. - /// - public void UpdateBelief() => Updated = true; - } } From 85f486eb123bff5e5c7f7e1eee1fb5029a142a79 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sun, 31 Mar 2024 18:37:09 +0200 Subject: [PATCH 12/19] feat: make goal structure type contravariant --- Aplib.Core/Desire/IGoalStructure.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Aplib.Core/Desire/IGoalStructure.cs b/Aplib.Core/Desire/IGoalStructure.cs index 95020730..ea362eb0 100644 --- a/Aplib.Core/Desire/IGoalStructure.cs +++ b/Aplib.Core/Desire/IGoalStructure.cs @@ -9,8 +9,8 @@ namespace Aplib.Core.Desire /// /// A goal structure is structure of predicates that must be fulfilled in order to complete a test. /// - /// - public interface IGoalStructure : IInterruptable, ICompletable + /// The belief set of the agent. + public interface IGoalStructure : ICompletable where TBeliefSet : IBeliefSet { /// From 2de1956ec948b6d115890754c2f9d0392cbc38ff Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sun, 31 Mar 2024 18:37:30 +0200 Subject: [PATCH 13/19] feat: remove interruptable --- Aplib.Core/Desire/GoalStructure.cs | 24 --- Aplib.Core/Desire/RepeatGoalStructure.cs | 2 - Aplib.Core/IInterruptable.cs | 36 ----- Aplib.Core/InterruptableEventArgs.cs | 24 --- Aplib.Tests/Core/Desire/GoalStructureTests.cs | 140 ++---------------- Aplib.Tests/Core/Desire/GoalTests.cs | 2 +- 6 files changed, 10 insertions(+), 218 deletions(-) delete mode 100644 Aplib.Core/IInterruptable.cs delete mode 100644 Aplib.Core/InterruptableEventArgs.cs diff --git a/Aplib.Core/Desire/GoalStructure.cs b/Aplib.Core/Desire/GoalStructure.cs index 2822061d..d374ac67 100644 --- a/Aplib.Core/Desire/GoalStructure.cs +++ b/Aplib.Core/Desire/GoalStructure.cs @@ -1,6 +1,5 @@ using Aplib.Core.Belief; using Aplib.Core.Desire.Goals; -using System; using System.Collections.Generic; namespace Aplib.Core.Desire @@ -10,12 +9,6 @@ namespace Aplib.Core.Desire /// public abstract class GoalStructure : IGoalStructure where TBeliefSet : IBeliefSet { - /// - public event EventHandler>? OnInterrupt; - - /// - public event EventHandler>? OnReinstate; - /// public CompletionStatus Status { get; protected set; } @@ -42,23 +35,6 @@ public abstract class GoalStructure : IGoalStructure whe /// The current goal to be fulfilled. public abstract IGoal GetCurrentGoal(TBeliefSet beliefSet); - /// - public void Interrupt(TBeliefSet beliefSet) - { - OnInterrupt?.Invoke(this, new InterruptableEventArgs(beliefSet)); - - foreach (IGoalStructure child in _children) - child.Interrupt(beliefSet); - } - - /// - public void Reinstate(TBeliefSet beliefSet) - { - OnReinstate?.Invoke(this, new InterruptableEventArgs(beliefSet)); - foreach (IGoalStructure child in _children) - child.Reinstate(beliefSet); - } - /// /// Updates the state of the goal structure. /// diff --git a/Aplib.Core/Desire/RepeatGoalStructure.cs b/Aplib.Core/Desire/RepeatGoalStructure.cs index 02663969..fdf05e30 100644 --- a/Aplib.Core/Desire/RepeatGoalStructure.cs +++ b/Aplib.Core/Desire/RepeatGoalStructure.cs @@ -35,8 +35,6 @@ public override void UpdateState(TBeliefSet beliefSet) { _currentGoalStructure!.UpdateState(beliefSet); - if (_currentGoalStructure.Status == Failure) _currentGoalStructure.Reinstate(beliefSet); - Status = _currentGoalStructure.Status switch { Failure or Unfinished => Unfinished, diff --git a/Aplib.Core/IInterruptable.cs b/Aplib.Core/IInterruptable.cs deleted file mode 100644 index d13426a5..00000000 --- a/Aplib.Core/IInterruptable.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Aplib.Core.Belief; -using Aplib.Core.Desire; -using System; - -namespace Aplib.Core -{ - /// - /// Defines a class that can be interrupted and reinstated. - /// - public interface IInterruptable - where TBeliefSet : IBeliefSet - { - /// - /// Interrupts the current goal structure. - /// - /// The beliefset at the time of interrupting. - public void Interrupt(TBeliefSet beliefSet); - - /// - /// Reinstates the goal structure after an interrupt. - /// - /// The beliefset at the time of reinstating. - public void Reinstate(TBeliefSet beliefSet); - - - /// - /// Handles the interrupt event. - /// - public event EventHandler> OnInterrupt; - - /// - /// Handles the reinstate event. - /// - public event EventHandler> OnReinstate; - } -} diff --git a/Aplib.Core/InterruptableEventArgs.cs b/Aplib.Core/InterruptableEventArgs.cs deleted file mode 100644 index bcf3caa1..00000000 --- a/Aplib.Core/InterruptableEventArgs.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Aplib.Core.Belief; -using System; - -namespace Aplib.Core.Desire -{ - /// - /// Describes the parameters of an interruptable event. - /// - /// The beliefset of the agent. - public class InterruptableEventArgs : EventArgs - where TBeliefSet : IBeliefSet - { - /// - /// Initializes a new instance of the class. - /// - /// The beliefset of the agent. - public InterruptableEventArgs(TBeliefSet beliefSet) => BeliefSet = beliefSet; - - /// - /// Gets the beliefset of the agent. - /// - public TBeliefSet BeliefSet { get; } - } -} diff --git a/Aplib.Tests/Core/Desire/GoalStructureTests.cs b/Aplib.Tests/Core/Desire/GoalStructureTests.cs index 8f40bbc8..a57937c1 100644 --- a/Aplib.Tests/Core/Desire/GoalStructureTests.cs +++ b/Aplib.Tests/Core/Desire/GoalStructureTests.cs @@ -96,7 +96,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFailure_ShouldReturnSecondGoal() // Act firstOfGoalStructure.UpdateState(beliefSet); - IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; + IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert firstOfGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); @@ -146,7 +146,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsUnfinished_ShouldReturnUnfinishe // Act firstOfGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); + IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert firstOfGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); @@ -173,88 +173,23 @@ public void FirstOfGoalStructure_WhenGoalIsUnfinished_ShouldReturnGoal() // Act firstOfGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); + IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert firstOfGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); currentGoal.Should().Be(goal); } - [Theory] - [InlineData(CompletionStatus.Success, CompletionStatus.Success)] - [InlineData(CompletionStatus.Unfinished, CompletionStatus.Unfinished)] - public void FirstOfGoalStructure_WhenInterrupted_ShouldRecalculateState(CompletionStatus input, - CompletionStatus expected) - { - // Arrange - Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.Status).Returns(input); - IGoal goal = Mock.Of(); - goalStructure1 - .Setup(g => g.GetCurrentGoal(It.IsAny())) - .Returns(goal); - - Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Success); - - BeliefSet beliefSet = Mock.Of(); - FirstOfGoalStructure firstOfGoalStructure = new(new List> - { - goalStructure1.Object, goalStructure2.Object - }); - firstOfGoalStructure.UpdateState(beliefSet); - - // Act - firstOfGoalStructure.Interrupt(beliefSet); - firstOfGoalStructure.Reinstate(beliefSet); - IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; - - // Assert - firstOfGoalStructure.Status.Should().Be(expected); - currentGoal.Should().Be(goal); - } - - [Fact] - public void FirstOfGoalStructure_WhenInterruptedAndFailure_ShouldNotFail() - { - // Arrange - Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Failure); - - Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Success); - IGoal goal = Mock.Of(); - goalStructure2 - .Setup(g => g.GetCurrentGoal(It.IsAny())) - .Returns(goal); - - BeliefSet beliefSet = Mock.Of(); - FirstOfGoalStructure firstOfGoalStructure = new(new List> - { - goalStructure1.Object, goalStructure2.Object - }); - firstOfGoalStructure.UpdateState(beliefSet); - - // Act - firstOfGoalStructure.Interrupt(beliefSet); - firstOfGoalStructure.Reinstate(beliefSet); - IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet)!; - - // Assert - firstOfGoalStructure.Status.Should().Be(CompletionStatus.Success); - currentGoal.Should().Be(goal); - } - [Theory] [InlineData(CompletionStatus.Success, CompletionStatus.Success)] [InlineData(CompletionStatus.Failure, CompletionStatus.Failure)] [InlineData(CompletionStatus.Unfinished, CompletionStatus.Unfinished)] - public void PrimitiveGoalStructure_WhenGoalHasState_ShouldHaveSameState(CompletionStatus Status, + public void PrimitiveGoalStructure_WhenGoalHasState_ShouldHaveSameState(CompletionStatus status, CompletionStatus expected) { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(Status); + goal.Setup(g => g.GetState(It.IsAny())).Returns(status); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); @@ -276,7 +211,7 @@ public void PrimitiveGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() // Act primitiveGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = primitiveGoalStructure.GetCurrentGoal(beliefSet); + IGoal currentGoal = primitiveGoalStructure.GetCurrentGoal(beliefSet); // Assert currentGoal.Should().Be(goal.Object); @@ -294,7 +229,7 @@ public void RepeatGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() // Act repeatGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); + IGoal currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert repeatGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); @@ -313,7 +248,7 @@ public void RepeatGoalStructure_WhenGoalStructureHasFailed_ShouldReturnGoal() // Act repeatGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); + IGoal currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert repeatGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); @@ -332,7 +267,7 @@ public void RepeatGoalStructure_WhenGoalStructureHasSucceeded_ShouldSucceed() // Act repeatGoalStructure.UpdateState(beliefSet); - IGoal? currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); + IGoal currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert repeatGoalStructure.Status.Should().Be(CompletionStatus.Success); @@ -424,63 +359,6 @@ public void SequentialGoalStructure_WhenGoalFails_ShouldReturnFailure() sequentialGoalStructure.Status.Should().Be(CompletionStatus.Failure); } - [Fact] - public void SequentialGoalStructure_WhenInterruptedWithChanges_ShouldReturnSameGoal() - { - // Arrange - Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); - - Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Unfinished); - IGoal expected = Mock.Of(); - goalStructure2.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); - - BeliefSet beliefSet = Mock.Of(); - SequentialGoalStructure sequentialGoalStructure = new(new List> - { - goalStructure1.Object, goalStructure2.Object - }); - sequentialGoalStructure.UpdateState(beliefSet); - goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Unfinished); - - // Act - sequentialGoalStructure.Interrupt(beliefSet); - sequentialGoalStructure.Reinstate(beliefSet); - IGoal currentGoal = sequentialGoalStructure.GetCurrentGoal(beliefSet)!; - - // Assert - sequentialGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); - currentGoal.Should().Be(expected); - } - - [Fact] - public void SequentialGoalStructure_WhenInterruptedWithNoChanges_ShouldReturnExactSameGoal() - { - // Arrange - Mock> goalStructure1 = new(); - goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); - Mock> goalStructure2 = new(); - goalStructure2.SetupGet(g => g.Status).Returns(CompletionStatus.Unfinished); - IGoal expected = Mock.Of(); - goalStructure2.Setup(g => g.GetCurrentGoal(It.IsAny())).Returns(expected); - BeliefSet beliefSet = Mock.Of(); - SequentialGoalStructure sequentialGoalStructure = new(new List> - { - goalStructure1.Object, goalStructure2.Object - }); - sequentialGoalStructure.UpdateState(beliefSet); - - // Act - sequentialGoalStructure.Interrupt(beliefSet); - sequentialGoalStructure.Reinstate(beliefSet); - IGoal currentGoal = sequentialGoalStructure.GetCurrentGoal(beliefSet)!; - - // Assert - sequentialGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); - currentGoal.Should().Be(expected); - } - [Fact] public void SequentialGoalStructure_WhenProvidingNoGoalStructure_ShouldThrowException() { diff --git a/Aplib.Tests/Core/Desire/GoalTests.cs b/Aplib.Tests/Core/Desire/GoalTests.cs index 43737439..e64e3358 100644 --- a/Aplib.Tests/Core/Desire/GoalTests.cs +++ b/Aplib.Tests/Core/Desire/GoalTests.cs @@ -125,7 +125,7 @@ public void Goal_WhenReached_ReturnsAsCompleted() /// /// Given a valid goal and belief, /// when the goal's heuristic function is evaluated, - /// the beliefset is not altered + /// the belief set is not altered /// [Fact] public void Goal_WhereEvaluationIsPerformed_DoesNotInfluenceBelieveSet() From 8dee103b5dbea43451c2943a4101707deaa3c116 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sun, 31 Mar 2024 18:46:06 +0200 Subject: [PATCH 14/19] docs: fix the wording of CompletionStatus.cs --- Aplib.Core/CompletionStatus.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Aplib.Core/CompletionStatus.cs b/Aplib.Core/CompletionStatus.cs index 445e80c5..265f953f 100644 --- a/Aplib.Core/CompletionStatus.cs +++ b/Aplib.Core/CompletionStatus.cs @@ -1,22 +1,22 @@ namespace Aplib.Core { /// - /// Represents the state of a . + /// Represents the state of a completable object. /// public enum CompletionStatus { /// - /// Represents a goal structure that is not yet completed. + /// Represents the status of a completable object that is not yet completed. /// Unfinished, /// - /// Represents a goal structure that has been successfully completed. + /// Represents the status of a completable object that has been successfully completed. /// Success, /// - /// Represents a goal structure that has failed to complete. + /// Represents the status of a completable object that has failed to complete. /// Failure } From 9a1723d8fa6b44ea4c57c748e7f23b1f44068ff0 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Sun, 31 Mar 2024 18:48:13 +0200 Subject: [PATCH 15/19] chore: conform to sonar --- Aplib.Core/Desire/SequentialGoalStructure.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Aplib.Core/Desire/SequentialGoalStructure.cs b/Aplib.Core/Desire/SequentialGoalStructure.cs index b9e829ae..4395f4cf 100644 --- a/Aplib.Core/Desire/SequentialGoalStructure.cs +++ b/Aplib.Core/Desire/SequentialGoalStructure.cs @@ -35,7 +35,7 @@ public SequentialGoalStructure(IList> children) : bas } /// - public override IGoal? GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.GetCurrentGoal(beliefSet); + public override IGoal GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.GetCurrentGoal(beliefSet); /// public override void UpdateState(TBeliefSet beliefSet) From fc85f1cf873d0cef3cd98b41cfe879540a815cb0 Mon Sep 17 00:00:00 2001 From: Jens Steenmetz Date: Mon, 1 Apr 2024 13:16:44 +0200 Subject: [PATCH 16/19] fix: improve GoalStructure tests --- Aplib.Tests/Core/Desire/GoalStructureTests.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Aplib.Tests/Core/Desire/GoalStructureTests.cs b/Aplib.Tests/Core/Desire/GoalStructureTests.cs index a57937c1..162da8d8 100644 --- a/Aplib.Tests/Core/Desire/GoalStructureTests.cs +++ b/Aplib.Tests/Core/Desire/GoalStructureTests.cs @@ -65,15 +65,15 @@ public void FirstOfGoalStructure_WhenFinished_ShouldEarlyExit() { goalStructure1.Object, goalStructure2.Object }); - - firstOfGoalStructure.UpdateState(beliefSet); - + // Act firstOfGoalStructure.UpdateState(beliefSet); + firstOfGoalStructure.UpdateState(beliefSet); // Assert firstOfGoalStructure.Status.Should().Be(CompletionStatus.Success); goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); + goalStructure2.Verify(x => x.UpdateState(It.IsAny()), Times.Never); } [Fact] @@ -295,7 +295,7 @@ public void SequentialGoalStructure_WhenDisposing_ShouldDisposeChildren() } [Fact] - public void SequentialGoalStructure_WhenFinished_ShouldEarlyExit() + public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldNotReturnFirstGoalAgain() { Mock> goalStructure1 = new(); goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); @@ -309,10 +309,9 @@ public void SequentialGoalStructure_WhenFinished_ShouldEarlyExit() goalStructure1.Object, goalStructure2.Object }); - sequentialGoalStructure.UpdateState(beliefSet); - // Act sequentialGoalStructure.UpdateState(beliefSet); + sequentialGoalStructure.UpdateState(beliefSet); // Assert sequentialGoalStructure.Status.Should().Be(CompletionStatus.Success); From ba768b737cf984b86f61386a20356f9d913c8bce Mon Sep 17 00:00:00 2001 From: Jens Steenmetz Date: Mon, 1 Apr 2024 13:28:29 +0200 Subject: [PATCH 17/19] refactor: rename State to Status --- Aplib.Core/Desire/FirstOfGoalStructure.cs | 4 +- Aplib.Core/Desire/GoalStructure.cs | 2 +- Aplib.Core/Desire/Goals/Goal.cs | 6 +-- Aplib.Core/Desire/Goals/IGoal.cs | 2 +- Aplib.Core/Desire/IGoalStructure.cs | 2 +- Aplib.Core/Desire/PrimitiveGoalStructure.cs | 4 +- Aplib.Core/Desire/RepeatGoalStructure.cs | 4 +- Aplib.Core/Desire/SequentialGoalStructure.cs | 4 +- Aplib.Tests/Core/Desire/GoalStructureTests.cs | 50 +++++++++---------- Aplib.Tests/Core/Desire/GoalTests.cs | 10 ++-- 10 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Aplib.Core/Desire/FirstOfGoalStructure.cs b/Aplib.Core/Desire/FirstOfGoalStructure.cs index 489c8ee1..335c0034 100644 --- a/Aplib.Core/Desire/FirstOfGoalStructure.cs +++ b/Aplib.Core/Desire/FirstOfGoalStructure.cs @@ -32,14 +32,14 @@ public FirstOfGoalStructure(IList> children) : base(c public override IGoal GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.GetCurrentGoal(beliefSet); /// - public override void UpdateState(TBeliefSet beliefSet) + public override void UpdateStatus(TBeliefSet beliefSet) { // Loop through all the children until one of them is unfinished or successful. // This loop is here to prevent tail recursion. while (true) { if (Status == CompletionStatus.Success) return; - _currentGoalStructure!.UpdateState(beliefSet); + _currentGoalStructure!.UpdateStatus(beliefSet); switch (_currentGoalStructure.Status) { diff --git a/Aplib.Core/Desire/GoalStructure.cs b/Aplib.Core/Desire/GoalStructure.cs index d374ac67..f9e0f54d 100644 --- a/Aplib.Core/Desire/GoalStructure.cs +++ b/Aplib.Core/Desire/GoalStructure.cs @@ -39,6 +39,6 @@ public abstract class GoalStructure : IGoalStructure whe /// Updates the state of the goal structure. /// /// The belief set of the agent. - public abstract void UpdateState(TBeliefSet beliefSet); + public abstract void UpdateStatus(TBeliefSet beliefSet); } } diff --git a/Aplib.Core/Desire/Goals/Goal.cs b/Aplib.Core/Desire/Goals/Goal.cs index 2ded9a07..630386e8 100644 --- a/Aplib.Core/Desire/Goals/Goal.cs +++ b/Aplib.Core/Desire/Goals/Goal.cs @@ -17,7 +17,7 @@ public class Goal : IGoal /// The abstract definition of what is means to test the Goal's heuristic function. Returns , as /// they represent how close we are to matching the heuristic function, and if the goal is completed. /// - /// + /// public delegate Heuristics HeuristicFunction(IBeliefSet beliefSet); /// @@ -51,7 +51,7 @@ public class Goal : IGoal /// The concrete implementation of this Goal's . Used to test whether this goal is /// completed. /// - /// + /// protected HeuristicFunction _heuristicFunction; /// @@ -115,7 +115,7 @@ public virtual Heuristics CurrentHeuristics(IBeliefSet beliefSet) /// /// An enum representing whether the goal is complete and if so, with what result. /// - public virtual CompletionStatus GetState(IBeliefSet beliefSet) + public virtual CompletionStatus GetStatus(IBeliefSet beliefSet) { Status = CurrentHeuristics(beliefSet).Distance < _epsilon ? CompletionStatus.Success diff --git a/Aplib.Core/Desire/Goals/IGoal.cs b/Aplib.Core/Desire/Goals/IGoal.cs index 66a733d4..38975379 100644 --- a/Aplib.Core/Desire/Goals/IGoal.cs +++ b/Aplib.Core/Desire/Goals/IGoal.cs @@ -29,6 +29,6 @@ public interface IGoal : ICompletable /// /// An enum representing whether the goal is complete and if so, with what result. /// - CompletionStatus GetState(IBeliefSet beliefSet); + CompletionStatus GetStatus(IBeliefSet beliefSet); } } diff --git a/Aplib.Core/Desire/IGoalStructure.cs b/Aplib.Core/Desire/IGoalStructure.cs index ea362eb0..ef3ea2ee 100644 --- a/Aplib.Core/Desire/IGoalStructure.cs +++ b/Aplib.Core/Desire/IGoalStructure.cs @@ -24,6 +24,6 @@ public interface IGoalStructure : ICompletable /// Updates the state of the goal structure. /// /// The belief set of the agent. - void UpdateState(TBeliefSet beliefSet); + void UpdateStatus(TBeliefSet beliefSet); } } diff --git a/Aplib.Core/Desire/PrimitiveGoalStructure.cs b/Aplib.Core/Desire/PrimitiveGoalStructure.cs index 05cecd52..530cbae2 100644 --- a/Aplib.Core/Desire/PrimitiveGoalStructure.cs +++ b/Aplib.Core/Desire/PrimitiveGoalStructure.cs @@ -28,7 +28,7 @@ public class PrimitiveGoalStructure : GoalStructure public override IGoal GetCurrentGoal(TBeliefSet beliefSet) => _goal; /// - public override void UpdateState(TBeliefSet beliefSet) => - Status = _goal.GetState(beliefSet); + public override void UpdateStatus(TBeliefSet beliefSet) => + Status = _goal.GetStatus(beliefSet); } } diff --git a/Aplib.Core/Desire/RepeatGoalStructure.cs b/Aplib.Core/Desire/RepeatGoalStructure.cs index fdf05e30..6d041e0f 100644 --- a/Aplib.Core/Desire/RepeatGoalStructure.cs +++ b/Aplib.Core/Desire/RepeatGoalStructure.cs @@ -31,9 +31,9 @@ public RepeatGoalStructure(IGoalStructure goalStructure) : base( }; /// - public override void UpdateState(TBeliefSet beliefSet) + public override void UpdateStatus(TBeliefSet beliefSet) { - _currentGoalStructure!.UpdateState(beliefSet); + _currentGoalStructure!.UpdateStatus(beliefSet); Status = _currentGoalStructure.Status switch { diff --git a/Aplib.Core/Desire/SequentialGoalStructure.cs b/Aplib.Core/Desire/SequentialGoalStructure.cs index 4395f4cf..57d660a4 100644 --- a/Aplib.Core/Desire/SequentialGoalStructure.cs +++ b/Aplib.Core/Desire/SequentialGoalStructure.cs @@ -38,14 +38,14 @@ public SequentialGoalStructure(IList> children) : bas public override IGoal GetCurrentGoal(TBeliefSet beliefSet) => _currentGoalStructure!.GetCurrentGoal(beliefSet); /// - public override void UpdateState(TBeliefSet beliefSet) + public override void UpdateStatus(TBeliefSet beliefSet) { // Loop through all the children until one of them is unfinished or successful. // This loop is here to prevent tail recursion. while (true) { if (Status == CompletionStatus.Success) return; - _currentGoalStructure!.UpdateState(beliefSet); + _currentGoalStructure!.UpdateStatus(beliefSet); switch (_currentGoalStructure.Status) { diff --git a/Aplib.Tests/Core/Desire/GoalStructureTests.cs b/Aplib.Tests/Core/Desire/GoalStructureTests.cs index 162da8d8..0dafbe2a 100644 --- a/Aplib.Tests/Core/Desire/GoalStructureTests.cs +++ b/Aplib.Tests/Core/Desire/GoalStructureTests.cs @@ -25,7 +25,7 @@ public void FirstOfGoalStructure_WhenAllGoalsFail_ShouldReturnFailure() }); // Act - firstOfGoalStructure.UpdateState(beliefSet); + firstOfGoalStructure.UpdateStatus(beliefSet); // Assert firstOfGoalStructure.Status.Should().Be(CompletionStatus.Failure); @@ -67,13 +67,13 @@ public void FirstOfGoalStructure_WhenFinished_ShouldEarlyExit() }); // Act - firstOfGoalStructure.UpdateState(beliefSet); - firstOfGoalStructure.UpdateState(beliefSet); + firstOfGoalStructure.UpdateStatus(beliefSet); + firstOfGoalStructure.UpdateStatus(beliefSet); // Assert firstOfGoalStructure.Status.Should().Be(CompletionStatus.Success); - goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); - goalStructure2.Verify(x => x.UpdateState(It.IsAny()), Times.Never); + goalStructure1.Verify(x => x.UpdateStatus(It.IsAny()), Times.Once); + goalStructure2.Verify(x => x.UpdateStatus(It.IsAny()), Times.Never); } [Fact] @@ -95,7 +95,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFailure_ShouldReturnSecondGoal() }); // Act - firstOfGoalStructure.UpdateState(beliefSet); + firstOfGoalStructure.UpdateStatus(beliefSet); IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert @@ -117,7 +117,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsFinished_ShouldReturnSuccess() }); // Act - firstOfGoalStructure.UpdateState(beliefSet); + firstOfGoalStructure.UpdateStatus(beliefSet); // Assert firstOfGoalStructure.Status.Should().Be(CompletionStatus.Success); @@ -145,7 +145,7 @@ public void FirstOfGoalStructure_WhenFirstGoalIsUnfinished_ShouldReturnUnfinishe }); // Act - firstOfGoalStructure.UpdateState(beliefSet); + firstOfGoalStructure.UpdateStatus(beliefSet); IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert @@ -172,7 +172,7 @@ public void FirstOfGoalStructure_WhenGoalIsUnfinished_ShouldReturnGoal() }); // Act - firstOfGoalStructure.UpdateState(beliefSet); + firstOfGoalStructure.UpdateStatus(beliefSet); IGoal currentGoal = firstOfGoalStructure.GetCurrentGoal(beliefSet); // Assert @@ -189,12 +189,12 @@ public void PrimitiveGoalStructure_WhenGoalHasState_ShouldHaveSameState(Completi { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(status); + goal.Setup(g => g.GetStatus(It.IsAny())).Returns(status); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); // Act - primitiveGoalStructure.UpdateState(beliefSet); + primitiveGoalStructure.UpdateStatus(beliefSet); // Assert primitiveGoalStructure.Status.Should().Be(expected); @@ -205,12 +205,12 @@ public void PrimitiveGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(CompletionStatus.Unfinished); + goal.Setup(g => g.GetStatus(It.IsAny())).Returns(CompletionStatus.Unfinished); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); // Act - primitiveGoalStructure.UpdateState(beliefSet); + primitiveGoalStructure.UpdateStatus(beliefSet); IGoal currentGoal = primitiveGoalStructure.GetCurrentGoal(beliefSet); // Assert @@ -222,13 +222,13 @@ public void RepeatGoalStructure_WhenGoalIsNotFinished_ShouldReturnGoal() { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(CompletionStatus.Unfinished); + goal.Setup(g => g.GetStatus(It.IsAny())).Returns(CompletionStatus.Unfinished); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); // Act - repeatGoalStructure.UpdateState(beliefSet); + repeatGoalStructure.UpdateStatus(beliefSet); IGoal currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert @@ -241,13 +241,13 @@ public void RepeatGoalStructure_WhenGoalStructureHasFailed_ShouldReturnGoal() { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(CompletionStatus.Failure); + goal.Setup(g => g.GetStatus(It.IsAny())).Returns(CompletionStatus.Failure); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); // Act - repeatGoalStructure.UpdateState(beliefSet); + repeatGoalStructure.UpdateStatus(beliefSet); IGoal currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert @@ -260,13 +260,13 @@ public void RepeatGoalStructure_WhenGoalStructureHasSucceeded_ShouldSucceed() { // Arrange Mock goal = new(); - goal.Setup(g => g.GetState(It.IsAny())).Returns(CompletionStatus.Success); + goal.Setup(g => g.GetStatus(It.IsAny())).Returns(CompletionStatus.Success); BeliefSet beliefSet = Mock.Of(); PrimitiveGoalStructure primitiveGoalStructure = new(goal.Object); RepeatGoalStructure repeatGoalStructure = new(primitiveGoalStructure); // Act - repeatGoalStructure.UpdateState(beliefSet); + repeatGoalStructure.UpdateStatus(beliefSet); IGoal currentGoal = repeatGoalStructure.GetCurrentGoal(beliefSet); // Assert @@ -295,7 +295,7 @@ public void SequentialGoalStructure_WhenDisposing_ShouldDisposeChildren() } [Fact] - public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldNotReturnFirstGoalAgain() + public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldNotUseFirstGoalAgain() { Mock> goalStructure1 = new(); goalStructure1.SetupGet(g => g.Status).Returns(CompletionStatus.Success); @@ -310,12 +310,12 @@ public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldNotReturnFirst }); // Act - sequentialGoalStructure.UpdateState(beliefSet); - sequentialGoalStructure.UpdateState(beliefSet); + sequentialGoalStructure.UpdateStatus(beliefSet); + sequentialGoalStructure.UpdateStatus(beliefSet); // Assert sequentialGoalStructure.Status.Should().Be(CompletionStatus.Success); - goalStructure1.Verify(x => x.UpdateState(It.IsAny()), Times.Once); + goalStructure1.Verify(x => x.UpdateStatus(It.IsAny()), Times.Once); } [Fact] @@ -332,7 +332,7 @@ public void SequentialGoalStructure_WhenFirstGoalIsFinished_ShouldReturnUnfinish }); // Act - sequentialGoalStructure.UpdateState(beliefSet); + sequentialGoalStructure.UpdateStatus(beliefSet); // Assert sequentialGoalStructure.Status.Should().Be(CompletionStatus.Unfinished); @@ -352,7 +352,7 @@ public void SequentialGoalStructure_WhenGoalFails_ShouldReturnFailure() }); // Act - sequentialGoalStructure.UpdateState(beliefSet); + sequentialGoalStructure.UpdateStatus(beliefSet); // Assert sequentialGoalStructure.Status.Should().Be(CompletionStatus.Failure); diff --git a/Aplib.Tests/Core/Desire/GoalTests.cs b/Aplib.Tests/Core/Desire/GoalTests.cs index e64e3358..13503cc8 100644 --- a/Aplib.Tests/Core/Desire/GoalTests.cs +++ b/Aplib.Tests/Core/Desire/GoalTests.cs @@ -96,7 +96,7 @@ public void Goal_WhenNotReached_DoesNotReturnAsCompleted() // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - CompletionStatus isCompleted = goal.GetState(beliefSet); + CompletionStatus isCompleted = goal.GetStatus(beliefSet); // Assert isCompleted.Should().Be(CompletionStatus.Unfinished); @@ -116,7 +116,7 @@ public void Goal_WhenReached_ReturnsAsCompleted() // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - CompletionStatus isCompleted = goal.GetState(beliefSet); + CompletionStatus isCompleted = goal.GetStatus(beliefSet); // Assert isCompleted.Should().Be(CompletionStatus.Success); @@ -135,7 +135,7 @@ public void Goal_WhereEvaluationIsPerformed_DoesNotInfluenceBelieveSet() // Act Goal goal = new TestGoalBuilder().Build(); - _ = goal.GetState(beliefSet); + _ = goal.GetStatus(beliefSet); // Assert beliefSet.MyBelief.Updated.Should().Be(false); @@ -166,8 +166,8 @@ public void GoalConstructor_WhereHeuristicFunctionTypeDiffers_HasEqualBehaviour( // Act MyBeliefSet beliefSet = new(); - CompletionStatus goalBooleanEvaluation = goalBoolean.GetState(beliefSet); - CompletionStatus goalNonBooleanEvaluation = goalNonBoolean.GetState(beliefSet); + CompletionStatus goalBooleanEvaluation = goalBoolean.GetStatus(beliefSet); + CompletionStatus goalNonBooleanEvaluation = goalNonBoolean.GetStatus(beliefSet); // Assert goalBooleanEvaluation.Should().Be(goalNonBooleanEvaluation); From 1fa457ed28aadd7942140d601ddff19e171cc243 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Mon, 1 Apr 2024 15:25:21 +0200 Subject: [PATCH 18/19] fix: conform to tboefijn Co-authored-by: Thijs Boerefijn <51719415+Tboefijn@users.noreply.github.com> --- Aplib.Core/Desire/Goals/Goal.cs | 4 +--- Aplib.Core/Desire/Goals/IGoal.cs | 3 +-- Aplib.Core/Desire/IGoalStructure.cs | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Aplib.Core/Desire/Goals/Goal.cs b/Aplib.Core/Desire/Goals/Goal.cs index 630386e8..fca77102 100644 --- a/Aplib.Core/Desire/Goals/Goal.cs +++ b/Aplib.Core/Desire/Goals/Goal.cs @@ -32,8 +32,7 @@ public class Goal : IGoal /// /// The used to achieve this , which is executed during every - /// iteration - /// of the BDI cycle. + /// iteration of the BDI cycle. /// public Tactic Tactic { get; } @@ -100,7 +99,6 @@ public Goal(Tactic tactic, Func predicate, string name, string description _epsilon = epsilon; } - /// /// Gets the of the current state of the game. /// diff --git a/Aplib.Core/Desire/Goals/IGoal.cs b/Aplib.Core/Desire/Goals/IGoal.cs index 38975379..bfd0a5d5 100644 --- a/Aplib.Core/Desire/Goals/IGoal.cs +++ b/Aplib.Core/Desire/Goals/IGoal.cs @@ -24,8 +24,7 @@ public interface IGoal : ICompletable /// /// Tests whether the goal has been achieved, bases on the and the /// . When the distance of the heuristics is smaller than - /// , - /// the goal is considered to be completed. + /// , the goal is considered to be completed. /// /// An enum representing whether the goal is complete and if so, with what result. /// diff --git a/Aplib.Core/Desire/IGoalStructure.cs b/Aplib.Core/Desire/IGoalStructure.cs index ef3ea2ee..66be3923 100644 --- a/Aplib.Core/Desire/IGoalStructure.cs +++ b/Aplib.Core/Desire/IGoalStructure.cs @@ -7,7 +7,7 @@ namespace Aplib.Core.Desire /// Represents a goal structure. /// /// - /// A goal structure is structure of predicates that must be fulfilled in order to complete a test. + /// A goal structure is a structure of predicates that must be fulfilled in order to complete a test. /// /// The belief set of the agent. public interface IGoalStructure : ICompletable From 366cfdf9f66c0ec174ba35f74dc159e18cfab963 Mon Sep 17 00:00:00 2001 From: Joachim Dekker Date: Mon, 1 Apr 2024 15:37:24 +0200 Subject: [PATCH 19/19] fix: conform to tboefijn, again Co-authored-by: Thijs Boerefijn <51719415+Tboefijn@users.noreply.github.com> --- Aplib.Core/Desire/Goals/Goal.cs | 2 -- Aplib.Core/Desire/Goals/IGoal.cs | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Aplib.Core/Desire/Goals/Goal.cs b/Aplib.Core/Desire/Goals/Goal.cs index fca77102..5c9d8130 100644 --- a/Aplib.Core/Desire/Goals/Goal.cs +++ b/Aplib.Core/Desire/Goals/Goal.cs @@ -44,8 +44,6 @@ public class Goal : IGoal /// this value. /// protected double _epsilon { get; } - - /// /// The concrete implementation of this Goal's . Used to test whether this goal is /// completed. diff --git a/Aplib.Core/Desire/Goals/IGoal.cs b/Aplib.Core/Desire/Goals/IGoal.cs index bfd0a5d5..9fb23eaa 100644 --- a/Aplib.Core/Desire/Goals/IGoal.cs +++ b/Aplib.Core/Desire/Goals/IGoal.cs @@ -10,8 +10,7 @@ public interface IGoal : ICompletable { /// /// The used to achieve this , which is executed during every - /// iteration - /// of the BDI cycle. + /// iteration of the BDI cycle. /// Tactic Tactic { get; }