From 56f23aeefb1b6c935d109cd24974745b46edbcd9 Mon Sep 17 00:00:00 2001 From: Silas Peters <69711739+SilasPeters@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:30:42 +0100 Subject: [PATCH] fix: apply Tactic fix (#19) * fix: apply tactic fix * fix: processed feedback * fix: typo * fix: removed redundant qualifier Co-authored-by: Quake <74355598+QuakeEye@users.noreply.github.com> --------- Co-authored-by: Quake <74355598+QuakeEye@users.noreply.github.com> --- Aplib.Core/Believe/BeliefSet.cs | 13 ++++ .../{ => Goals}/CommonHeuristicFunctions.cs | 7 +- Aplib.Core/Desire/{ => Goals}/Goal.cs | 42 +++++------- Aplib.Core/Desire/{ => Goals}/Heuristics.cs | 4 +- Aplib.Core/Desire/Tactic.cs | 17 ----- Aplib.Core/{ => Intent/Actions}/Action.cs | 2 +- .../{ => Intent/Actions}/GuardedAction.cs | 2 +- .../{ => Intent}/Tactics/AnyOfTactic.cs | 3 +- .../{ => Intent}/Tactics/FirstOfTactic.cs | 6 +- .../{ => Intent}/Tactics/PrimitiveTactic.cs | 8 +-- Aplib.Core/{ => Intent}/Tactics/Tactic.cs | 9 ++- Aplib.Tests/Core/ActionTests.cs | 4 +- Aplib.Tests/Core/Tactics/TacticTests.cs | 15 +++-- Aplib.Tests/Desire/GoalTests.cs | 64 ++++++++++--------- Aplib.Tests/Stubs/Desire/TacticStub.cs | 8 +-- Aplib.Tests/Tools/TestGoalBuilder.cs | 6 +- 16 files changed, 106 insertions(+), 104 deletions(-) create mode 100644 Aplib.Core/Believe/BeliefSet.cs rename Aplib.Core/Desire/{ => Goals}/CommonHeuristicFunctions.cs (88%) rename Aplib.Core/Desire/{ => Goals}/Goal.cs (78%) rename Aplib.Core/Desire/{ => Goals}/Heuristics.cs (84%) delete mode 100644 Aplib.Core/Desire/Tactic.cs rename Aplib.Core/{ => Intent/Actions}/Action.cs (97%) rename Aplib.Core/{ => Intent/Actions}/GuardedAction.cs (97%) rename Aplib.Core/{ => Intent}/Tactics/AnyOfTactic.cs (95%) rename Aplib.Core/{ => Intent}/Tactics/FirstOfTactic.cs (91%) rename Aplib.Core/{ => Intent}/Tactics/PrimitiveTactic.cs (84%) rename Aplib.Core/{ => Intent}/Tactics/Tactic.cs (67%) diff --git a/Aplib.Core/Believe/BeliefSet.cs b/Aplib.Core/Believe/BeliefSet.cs new file mode 100644 index 00000000..2fc254f5 --- /dev/null +++ b/Aplib.Core/Believe/BeliefSet.cs @@ -0,0 +1,13 @@ +namespace Aplib.Core.Believe +{ + /// + /// This is a summary + /// + public class BeliefSet + { + /// + /// This is merely here to allow some unit test + /// + public string State = "kaas"; + } +} diff --git a/Aplib.Core/Desire/CommonHeuristicFunctions.cs b/Aplib.Core/Desire/Goals/CommonHeuristicFunctions.cs similarity index 88% rename from Aplib.Core/Desire/CommonHeuristicFunctions.cs rename to Aplib.Core/Desire/Goals/CommonHeuristicFunctions.cs index 0471c545..f203a3cb 100644 --- a/Aplib.Core/Desire/CommonHeuristicFunctions.cs +++ b/Aplib.Core/Desire/Goals/CommonHeuristicFunctions.cs @@ -1,6 +1,7 @@ +using Aplib.Core.Believe; using System; -namespace Aplib.Core.Desire +namespace Aplib.Core.Desire.Goals { /// /// Contains helper methods to generate commonly used heuristic functions. @@ -15,13 +16,13 @@ public static class CommonHeuristicFunctions /// /// A heuristic function which wraps around the boolean-based heuristic function. public static Goal.HeuristicFunction Boolean(Func heuristicFunction) - => () => Heuristics.BooleanHeuristic(heuristicFunction.Invoke()); + => _ => Heuristics.Boolean(heuristicFunction.Invoke()); /// /// A which always returns with the same distance. /// /// The distance which the heuristic function must always return. - public static Goal.HeuristicFunction Constant(float distance) => () => new Heuristics { Distance = distance }; + public static Goal.HeuristicFunction Constant(float distance) => _ => new Heuristics { Distance = distance }; /// /// Returns a heuristic function which always, at all times, and forever, returns a value indicating the state diff --git a/Aplib.Core/Desire/Goal.cs b/Aplib.Core/Desire/Goals/Goal.cs similarity index 78% rename from Aplib.Core/Desire/Goal.cs rename to Aplib.Core/Desire/Goals/Goal.cs index 4a6ab580..75510b59 100644 --- a/Aplib.Core/Desire/Goal.cs +++ b/Aplib.Core/Desire/Goals/Goal.cs @@ -1,6 +1,8 @@ +using Aplib.Core.Believe; +using Aplib.Core.Intent.Tactics; using System; -namespace Aplib.Core.Desire +namespace Aplib.Core.Desire.Goals { /// /// A goal effectively combines a heuristic function with a tactic, and aims to meet the heuristic function by @@ -14,15 +16,16 @@ 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(); + /// + public delegate Heuristics HeuristicFunction(BeliefSet 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 => _currentHeuristics ??= _heuristicFunction.Invoke(); + public virtual Heuristics CurrentHeuristics(BeliefSet beliefSet) + => _currentHeuristics ??= _heuristicFunction.Invoke(beliefSet); /// /// The name used to display the current goal during debugging, logging, or general overviews. @@ -45,15 +48,14 @@ public class Goal /// 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 + /// The used to achieve this , which is executed during every iteration /// of the BDI cycle. /// - /// - private readonly Tactic _tactic; + public Tactic Tactic { get; private set; } /// /// The backing field of . @@ -73,7 +75,7 @@ public class Goal /// public Goal(Tactic tactic, HeuristicFunction heuristicFunction, string name, string description, double epsilon = 0.005d) { - _tactic = tactic; + Tactic = tactic; _heuristicFunction = heuristicFunction; Name = name; Description = description; @@ -84,31 +86,22 @@ 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 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. /// /// The goal is considered to be completed, when the distance of the is below /// this value. /// - public Goal(Tactic tactic, Func heuristicFunction, string name, string description, double epsilon = 0.005d) + public Goal(Tactic tactic, Func predicate, string name, string description, double epsilon = 0.005d) { - _tactic = tactic; - _heuristicFunction = CommonHeuristicFunctions.Boolean(heuristicFunction); + Tactic = tactic; + _heuristicFunction = CommonHeuristicFunctions.Boolean(predicate); Name = name; Description = description; _epsilon = epsilon; } - /// - /// Performs the next steps needed to be taken to approach this goal. Effectively this means that one BDI - /// cycle will be executed. - /// - public void Iterate() - { - _tactic.IterateBdiCycle(); - } - /// /// Tests whether the goal has been achieved, bases on the and the /// . When the distance of the heuristics is smaller than , @@ -116,9 +109,6 @@ public void Iterate() /// /// A boolean representing whether the goal is considered to be completed. /// - public bool Evaluate() - { - return CurrentHeuristics.Distance < _epsilon; - } + public bool IsCompleted(BeliefSet beliefSet) => CurrentHeuristics(beliefSet).Distance < _epsilon; } } diff --git a/Aplib.Core/Desire/Heuristics.cs b/Aplib.Core/Desire/Goals/Heuristics.cs similarity index 84% rename from Aplib.Core/Desire/Heuristics.cs rename to Aplib.Core/Desire/Goals/Heuristics.cs index d80d4302..aa2a3fb1 100644 --- a/Aplib.Core/Desire/Heuristics.cs +++ b/Aplib.Core/Desire/Goals/Heuristics.cs @@ -1,4 +1,4 @@ -namespace Aplib.Core.Desire +namespace Aplib.Core.Desire.Goals { /// /// Contains all information on how close the associated state is to its goal. @@ -17,6 +17,6 @@ public class Heuristics /// /// True if completed, False if not completed. /// - public static Heuristics BooleanHeuristic(bool value) => new() { Distance = value ? 0f : 1f }; + public static Heuristics Boolean(bool value) => new() { Distance = value ? 0f : 1f }; } } diff --git a/Aplib.Core/Desire/Tactic.cs b/Aplib.Core/Desire/Tactic.cs deleted file mode 100644 index e6c89010..00000000 --- a/Aplib.Core/Desire/Tactic.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Aplib.Core.Desire -{ - /// - /// Tactics are the real meat of s, as they define how the agent can approach the goal in hopes - /// of finding a solution which makes the Goal's heuristic function evaluate to being completed. A tactic represents - /// a smart combination of s, which are executed in a Believe Desire Intent Cycle. - /// - /// - /// - public abstract class Tactic - { - /// - /// Execute the next cycle in the Believe Desire Intent Cycle. - /// - public abstract void IterateBdiCycle(); - } -} diff --git a/Aplib.Core/Action.cs b/Aplib.Core/Intent/Actions/Action.cs similarity index 97% rename from Aplib.Core/Action.cs rename to Aplib.Core/Intent/Actions/Action.cs index ff790496..f4d03d34 100644 --- a/Aplib.Core/Action.cs +++ b/Aplib.Core/Intent/Actions/Action.cs @@ -1,6 +1,6 @@ using System; -namespace Aplib.Core +namespace Aplib.Core.Intent.Actions { /// /// Describes an action that can be executed and guarded. diff --git a/Aplib.Core/GuardedAction.cs b/Aplib.Core/Intent/Actions/GuardedAction.cs similarity index 97% rename from Aplib.Core/GuardedAction.cs rename to Aplib.Core/Intent/Actions/GuardedAction.cs index 15423309..67a43dee 100644 --- a/Aplib.Core/GuardedAction.cs +++ b/Aplib.Core/Intent/Actions/GuardedAction.cs @@ -1,6 +1,6 @@ using System; -namespace Aplib.Core +namespace Aplib.Core.Intent.Actions { /// /// Describes an action that can be executed and guarded with a query that stores the result of the guard. diff --git a/Aplib.Core/Tactics/AnyOfTactic.cs b/Aplib.Core/Intent/Tactics/AnyOfTactic.cs similarity index 95% rename from Aplib.Core/Tactics/AnyOfTactic.cs rename to Aplib.Core/Intent/Tactics/AnyOfTactic.cs index 7b32795f..7bd34635 100644 --- a/Aplib.Core/Tactics/AnyOfTactic.cs +++ b/Aplib.Core/Intent/Tactics/AnyOfTactic.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; +using Action = Aplib.Core.Intent.Actions.Action; -namespace Aplib.Core.Tactics +namespace Aplib.Core.Intent.Tactics { /// /// Represents a tactic that executes any of the provided sub-tactics. diff --git a/Aplib.Core/Tactics/FirstOfTactic.cs b/Aplib.Core/Intent/Tactics/FirstOfTactic.cs similarity index 91% rename from Aplib.Core/Tactics/FirstOfTactic.cs rename to Aplib.Core/Intent/Tactics/FirstOfTactic.cs index 35eed950..433248bf 100644 --- a/Aplib.Core/Tactics/FirstOfTactic.cs +++ b/Aplib.Core/Intent/Tactics/FirstOfTactic.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; +using Action = Aplib.Core.Intent.Actions.Action; -namespace Aplib.Core.Tactics +namespace Aplib.Core.Intent.Tactics { /// /// Represents a tactic that executes the first enabled action from a list of sub-tactics. @@ -25,7 +25,7 @@ public FirstOfTactic(Func guard, params Tactic[] subTactics) : base(guard, { } - /// + /// public override Action? GetAction() { foreach (Tactic subTactic in SubTactics) diff --git a/Aplib.Core/Tactics/PrimitiveTactic.cs b/Aplib.Core/Intent/Tactics/PrimitiveTactic.cs similarity index 84% rename from Aplib.Core/Tactics/PrimitiveTactic.cs rename to Aplib.Core/Intent/Tactics/PrimitiveTactic.cs index da4dc805..b2f825c9 100644 --- a/Aplib.Core/Tactics/PrimitiveTactic.cs +++ b/Aplib.Core/Intent/Tactics/PrimitiveTactic.cs @@ -1,10 +1,10 @@ using System; -using System.Collections.Generic; +using Action = Aplib.Core.Intent.Actions.Action; -namespace Aplib.Core.Tactics +namespace Aplib.Core.Intent.Tactics { /// - /// Represents a primitive tactic in the Aplib.Core namespace. + /// Represents a primitive tactic /// public class PrimitiveTactic : Tactic { @@ -23,7 +23,7 @@ public class PrimitiveTactic : Tactic /// Initializes a new instance of the class with the specified action and guard. /// /// The action of the primitive tactic. - /// The guard of the tactic. + /// The guard of the primitive tactic. public PrimitiveTactic(Action action, Func guard) : base(guard) => Action = action; /// diff --git a/Aplib.Core/Tactics/Tactic.cs b/Aplib.Core/Intent/Tactics/Tactic.cs similarity index 67% rename from Aplib.Core/Tactics/Tactic.cs rename to Aplib.Core/Intent/Tactics/Tactic.cs index cf4054da..0cc9b13b 100644 --- a/Aplib.Core/Tactics/Tactic.cs +++ b/Aplib.Core/Intent/Tactics/Tactic.cs @@ -1,10 +1,15 @@ using System; +using Action = Aplib.Core.Intent.Actions.Action; -namespace Aplib.Core.Tactics +namespace Aplib.Core.Intent.Tactics { /// - /// Represents a tactic in the Aplib.Core namespace. + /// Tactics are the real meat of s, as they define how the agent can approach the goal in hopes + /// of finding a solution which makes the Goal's heuristic function evaluate to being completed. A tactic represents + /// a smart combination of s, which are executed in a Believe Desire Intent Cycle. /// + /// + /// public abstract class Tactic { /// diff --git a/Aplib.Tests/Core/ActionTests.cs b/Aplib.Tests/Core/ActionTests.cs index 16ad68d8..26b60b45 100644 --- a/Aplib.Tests/Core/ActionTests.cs +++ b/Aplib.Tests/Core/ActionTests.cs @@ -1,5 +1,5 @@ -using Aplib.Core; -using Action = Aplib.Core.Action; +using Aplib.Core.Intent.Actions; +using Action = Aplib.Core.Intent.Actions.Action; namespace Aplib.Tests.Core; diff --git a/Aplib.Tests/Core/Tactics/TacticTests.cs b/Aplib.Tests/Core/Tactics/TacticTests.cs index d009c806..181a179d 100644 --- a/Aplib.Tests/Core/Tactics/TacticTests.cs +++ b/Aplib.Tests/Core/Tactics/TacticTests.cs @@ -1,6 +1,5 @@ -using Aplib.Core.Tactics; -using FluentAssertions; -using Action = Aplib.Core.Action; +using Aplib.Core.Intent.Tactics; +using Action = Aplib.Core.Intent.Actions.Action; namespace Aplib.Tests.Core.Tactics; public class TacticTests @@ -14,7 +13,7 @@ public class TacticTests private static bool FalseGuard() => false; /// - /// 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. /// @@ -30,11 +29,12 @@ public void GetAction_WhenTacticTypeIsFirstOf_ReturnsEnabledPrimitiveTactics() Action? enabledAction = parentTactic.GetAction(); // Assert + Assert.NotNull(enabledAction); Assert.Equal(_emptyAction, enabledAction); } /// - /// Given a parent of type with two subtactics and a guard that is true, + /// Given a parent of type with two subtactics and a guard that is true, /// When getting the next tactic, /// Then the result should be the first subtactic. /// @@ -50,11 +50,12 @@ public void GetAction_WhenTacticTypeIsFirstOfAndGuardEnabled_ReturnsEnabledPrimi Action? enabledAction = parentTactic.GetAction(); // Assert + Assert.NotNull(enabledAction); Assert.Equal(_emptyAction, enabledAction); } /// - /// 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 contain all the subtactics. /// @@ -70,6 +71,7 @@ public void GetAction_WhenTacticTypeIsAnyOf_ReturnsEnabledPrimitiveTactics() Action? enabledAction = parentTactic.GetAction(); // Assert + Assert.NotNull(enabledAction); Assert.Equal(_emptyAction, enabledAction); } @@ -88,6 +90,7 @@ public void GetAction_WhenTacticTypeIsPrimitiveAndActionIsActionable_ReturnsEnab Action? enabledAction = tactic.GetAction(); // Assert + Assert.NotNull(enabledAction); Assert.Equal(_emptyAction, enabledAction); } diff --git a/Aplib.Tests/Desire/GoalTests.cs b/Aplib.Tests/Desire/GoalTests.cs index fb865050..24b87d6f 100644 --- a/Aplib.Tests/Desire/GoalTests.cs +++ b/Aplib.Tests/Desire/GoalTests.cs @@ -1,7 +1,10 @@ -using Aplib.Core.Desire; +using Aplib.Core.Believe; +using Aplib.Core.Desire.Goals; +using Aplib.Core.Intent.Tactics; using Aplib.Tests.Stubs.Desire; using Aplib.Tests.Tools; using FluentAssertions; +using Action = Aplib.Core.Intent.Actions.Action; namespace Aplib.Tests.Desire; @@ -16,7 +19,7 @@ public class GoalTests public void Goal_WhenConstructed_ContainsCorrectMetaData() { // Arrange - Tactic tactic = new TacticStub(() => { }); + Tactic tactic = new TacticStub(new Action(() => { })); 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"; @@ -40,7 +43,7 @@ public void Goal_WhenConstructed_DidNotIterateYet() { // Arrange int iterations = 0; - Tactic tactic = new TacticStub(() => iterations++); + Tactic tactic = new TacticStub(new Action(() => iterations++)); // Act Goal _ = new TestGoalBuilder().UseTactic(tactic).Build(); @@ -49,26 +52,6 @@ public void Goal_WhenConstructed_DidNotIterateYet() iterations.Should().Be(0); } - /// - /// Given the Goal is created properly using its constructor, - /// When the goal is being iterated over, - /// Then the given tactic has has been applied at least once - /// - [Fact] - public void Goal_WhenIterating_DoesIterate() - { - // Arrange - int iterations = 0; - Tactic tactic = new TacticStub(() => iterations++); - - // Act - Goal goal = new TestGoalBuilder().UseTactic(tactic).Build(); - goal.Iterate(); - - // Assert - iterations.Should().BeGreaterThan(0); - } - /// /// Given the Goal's heuristic function is configured to have reached its goal /// when the Evaluate() method of a goal is used, @@ -78,11 +61,12 @@ public void Goal_WhenIterating_DoesIterate() public void Goal_WhenReached_ReturnsAsCompleted() { // Arrange + BeliefSet beliefSet = new(); Goal.HeuristicFunction heuristicFunction = CommonHeuristicFunctions.Completed(); // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - bool isCompleted = goal.Evaluate(); + bool isCompleted = goal.IsCompleted(beliefSet); // Assert isCompleted.Should().Be(true); @@ -97,18 +81,39 @@ public void Goal_WhenReached_ReturnsAsCompleted() public void Goal_WhenNotReached_DoesNotReturnAsCompleted() { // Arrange + BeliefSet beliefSet = new(); Goal.HeuristicFunction heuristicFunction = CommonHeuristicFunctions.Uncompleted(); // Act Goal goal = new TestGoalBuilder().WithHeuristicFunction(heuristicFunction).Build(); - bool isCompleted = goal.Evaluate(); + bool isCompleted = goal.IsCompleted(beliefSet); // Assert isCompleted.Should().Be(false); } /// - /// Given the Goal's different constructors have been called with semantically equal argumetns + /// Given a valid goal and believe, + /// when the goal's heuristic function is evaluated, + /// the believeset is not altered + /// + [Fact] + public void Goal_WhereEvaluationIsPerformed_DoesNotInfluenceBelieveSet() + { + // Arrange + BeliefSet beliefSet = new(); + + // Act + string currentBelieveSetState = beliefSet.State; + Goal goal = new TestGoalBuilder().Build(); + _ = goal.IsCompleted(beliefSet); + + // Assert + beliefSet.State.Should().Be(currentBelieveSetState); + } + + /// + /// Given the Goal's different constructors have been called with semantically equal arguments /// when the Evaluate() method of all goals are used, /// then all returned values should equal. /// @@ -119,7 +124,7 @@ public void Goal_WhenNotReached_DoesNotReturnAsCompleted() public void GoalConstructor_WhereHeuristicFunctionTypeDiffers_HasEqualBehaviour(bool goalCompleted) { // Arrange - Tactic tactic = new TacticStub(() => { }); + Tactic tactic = new TacticStub(new Action(() => { })); 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"; @@ -130,8 +135,9 @@ public void GoalConstructor_WhereHeuristicFunctionTypeDiffers_HasEqualBehaviour( Goal goalNonBoolean = new(tactic, heuristicFunctionNonBoolean, name, description); // Act - bool goalBooleanEvaluation = goalBoolean.Evaluate(); - bool goalNonBooleanEvaluation = goalNonBoolean.Evaluate(); + BeliefSet beliefSet = new(); + bool goalBooleanEvaluation = goalBoolean.IsCompleted(beliefSet); + bool goalNonBooleanEvaluation = goalNonBoolean.IsCompleted(beliefSet); // Assert goalBooleanEvaluation.Should().Be(goalNonBooleanEvaluation); diff --git a/Aplib.Tests/Stubs/Desire/TacticStub.cs b/Aplib.Tests/Stubs/Desire/TacticStub.cs index 0e1845d2..5843fc4e 100644 --- a/Aplib.Tests/Stubs/Desire/TacticStub.cs +++ b/Aplib.Tests/Stubs/Desire/TacticStub.cs @@ -1,4 +1,5 @@ -using Aplib.Core.Desire; +using Aplib.Core.Intent.Tactics; +using Action = Aplib.Core.Intent.Actions.Action; namespace Aplib.Tests.Stubs.Desire; @@ -9,8 +10,5 @@ namespace Aplib.Tests.Stubs.Desire; internal class TacticStub(Action iteration) : Tactic { /// - public override void IterateBdiCycle() - { - iteration.Invoke(); - } + public override Action? GetAction() => iteration; } diff --git a/Aplib.Tests/Tools/TestGoalBuilder.cs b/Aplib.Tests/Tools/TestGoalBuilder.cs index 68e32ea7..8d6d0574 100644 --- a/Aplib.Tests/Tools/TestGoalBuilder.cs +++ b/Aplib.Tests/Tools/TestGoalBuilder.cs @@ -1,11 +1,13 @@ -using Aplib.Core.Desire; +using Aplib.Core.Desire.Goals; +using Aplib.Core.Intent.Tactics; using Aplib.Tests.Stubs.Desire; +using Action = Aplib.Core.Intent.Actions.Action; namespace Aplib.Tests.Tools; internal sealed class TestGoalBuilder { - private Tactic _tactic = new TacticStub(() => { }); + private Tactic _tactic = new TacticStub(new Action(() => { })); 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";