Skip to content

Commit

Permalink
feat: add Metadata (#22)
Browse files Browse the repository at this point in the history
* feat: add metadata to Goals and Tactics

* feat: add metadata to Actions

* test: Tactic metadata

* test: Action metadata

* fix: metadata property doc comments

* feat: let the constructors of goals and actions take a metadata object, instead of a raw name and description

* feat: let the constructors of tactics take a metadata object, instead of a raw name and description

* feat: add a unique identifier to component metadata

* feat: make metadata optional for goals

* feat: make metadata optional for actions

* feat: make metadata optional for tactics

* test: metadata identifiers

* feat: give `Metadata` a ctor that takes an id, for testing

* test: remove unnecessary tests

* fix: fix overlapping signature

* chore: change visibility of parameterless constructor

---------

Co-authored-by: Jens Steenmetz <[email protected]>
  • Loading branch information
LukaBerkers and JensSteenmetz authored Apr 2, 2024
1 parent 29c0a94 commit 553533f
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 49 deletions.
41 changes: 22 additions & 19 deletions Aplib.Core/Desire/Goals/Goal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,12 @@ public class Goal : IGoal
public delegate Heuristics HeuristicFunction(IBeliefSet beliefSet);

/// <summary>
/// The description used to describe the current goal during debugging, logging, or general overviews.
/// Gets the metadata of the goal.
/// </summary>
public string Description { get; }

/// <summary>
/// The name used to display the current goal during debugging, logging, or general overviews.
/// </summary>
public string Name { get; }
/// <remark>
/// This metadata may be useful for debugging or logging.
/// </remark>
public Metadata Metadata { get; }

/// <summary>
/// The <see cref="Intent.Tactics.Tactic" /> used to achieve this <see cref="Goal" />, which is executed during every
Expand Down Expand Up @@ -61,40 +59,45 @@ public class Goal : IGoal
/// </summary>
/// <param name="tactic">The tactic used to approach this goal.</param>
/// <param name="heuristicFunction">The heuristic function which defines whether a goal is reached</param>
/// <param name="name">The name of this goal, used to quickly display this goal in several contexts.</param>
/// <param name="description">The description of this goal, used to explain this goal in several contexts.</param>
/// <param name="epsilon">
/// The goal is considered to be completed, when the distance of the <see cref="CurrentHeuristics" /> is below
/// this value.
/// </param>
public Goal(Tactic tactic, HeuristicFunction heuristicFunction, string name, string description,
double epsilon = 0.005d)
/// <param name="metadata">
/// Metadata about this goal, used to quickly display the goal in several contexts.
/// </param>
public Goal
(
Tactic tactic,
HeuristicFunction heuristicFunction,
double epsilon = 0.005d,
Metadata? metadata = null
)
{
Tactic = tactic;
_heuristicFunction = heuristicFunction;
Name = name;
Description = description;
_epsilon = epsilon;
Metadata = metadata ?? new Metadata();
}

/// <summary>
/// Creates a new goal which works with boolean-based <see cref="Heuristics" />.
/// </summary>
/// <param name="tactic">The tactic used to approach this goal.</param>
/// <param name="predicate">The heuristic function (or specifically predicate) which defines whether a goal is reached.</param>
/// <param name="name">The name of this goal, used to quickly display this goal in several contexts.</param>
/// <param name="description">The description of this goal, used to explain this goal in several contexts.</param>
/// <param name="predicate">The heuristic function (or specifically predicate) which defines whether a goal is reached</param>
/// <param name="epsilon">
/// The goal is considered to be completed, when the distance of the <see cref="CurrentHeuristics" /> is below
/// this value.
/// </param>
public Goal(Tactic tactic, Func<bool> predicate, string name, string description, double epsilon = 0.005d)
/// <param name="metadata">
/// Metadata about this goal, used to quickly display the goal in several contexts.
/// </param>
public Goal(Tactic tactic, Func<bool> predicate, double epsilon = 0.005d, Metadata? metadata = null)
{
Tactic = tactic;
_heuristicFunction = CommonHeuristicFunctions.Boolean(predicate);
Name = name;
Description = description;
_epsilon = epsilon;
Metadata = metadata ?? new Metadata();
}

/// <summary>
Expand Down
35 changes: 31 additions & 4 deletions Aplib.Core/Intent/Actions/Action.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ namespace Aplib.Core.Intent.Actions
/// </summary>
public class Action
{
/// <summary>
/// Gets the metadata of the action.
/// </summary>
/// <remark>
/// This metadata may be useful for debugging or logging.
/// </remark>
public Metadata Metadata { get; }

/// <summary>
/// Gets or sets the effect of the action.
/// </summary>
Expand All @@ -18,26 +26,45 @@ public class Action
protected Func<bool> _guard { get; set; } = () => true;

/// <summary>
/// Initializes a new instance of the <see cref="Action{TQuery}" /> class.
/// Parameterless constructor for internal use.
/// </summary>
internal Action() : this(null) { }

/// <summary>
/// Initializes a new instance of the <see cref="Action{TQuery}" /> class.
/// </summary>=
/// <param name="effect">The effect of the action.</param>
public Action(System.Action effect) => _effect = effect;
/// <param name="metadata">
/// Metadata about this action, used to quickly display the action in several contexts.
/// </param>
public Action(System.Action effect, Metadata? metadata = null)
{
_effect = effect;
Metadata = metadata ?? new Metadata();
}

/// <summary>
/// Initializes a new instance of the <see cref="Action{TQuery}" /> class.
/// </summary>
/// <param name="effect">The effect of the action.</param>
/// <param name="guard">The guard of the action.</param>
public Action(System.Action effect, Func<bool> guard) : this(effect) => _guard = guard;
/// <param name="metadata">
/// Metadata about this action, used to quickly display the action in several contexts.
/// </param>
public Action(System.Action effect, Func<bool> guard, Metadata? metadata = null) : this(effect, metadata) => _guard = guard;

/// <summary>
/// Initializes a new empty instance of the <see cref="Action{TQuery}" /> class.
/// </summary>
/// <remarks>Only meant for internal use</remarks>
protected internal Action()
/// <param name="metadata">
/// Metadata about this action, used to quickly display the action in several contexts.
/// </param>
protected internal Action(Metadata? metadata)
{
_effect = () => { };
_guard = () => false;
Metadata = metadata ?? new Metadata();
}

/// <summary>
Expand Down
6 changes: 5 additions & 1 deletion Aplib.Core/Intent/Actions/GuardedAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public class GuardedAction<TQuery> : Action
/// </summary>
/// <param name="effect">The effect of the action.</param>
/// <param name="guard">The guard of the action.</param>
public GuardedAction(System.Action<TQuery> effect, Func<TQuery?> guard) : base()
/// <param name="metadata">
/// Metadata about this action, used to quickly display the action in several contexts.
/// </param>
public GuardedAction(Action<TQuery> effect, Func<TQuery?> guard, Metadata? metadata = null)
: base(metadata)
{
Effect = effect;
Guard = guard;
Expand Down
14 changes: 11 additions & 3 deletions Aplib.Core/Intent/Tactics/AnyOfTactic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ public class AnyOfTactic : Tactic
/// <summary>
/// Initializes a new instance of the <see cref="AnyOfTactic"/> class with the specified sub-tactics.
/// </summary>
/// <param name="metadata">
/// Metadata about this tactic, used to quickly display the tactic in several contexts.
/// </param>
/// <param name="subTactics">The list of sub-tactics.</param>
public AnyOfTactic(params Tactic[] subTactics)
public AnyOfTactic(Metadata? metadata = null, params Tactic[] subTactics)
: base(metadata)
{
SubTactics = new();

Expand All @@ -31,9 +35,13 @@ public AnyOfTactic(params Tactic[] subTactics)
/// <summary>
/// Initializes a new instance of the <see cref="AnyOfTactic"/> class with the specified sub-tactics and guard condition.
/// </summary>
/// <param name="subTactics">The list of sub-tactics.</param>
/// <param name="guard">The guard condition.</param>
public AnyOfTactic(Func<bool> guard, params Tactic[] subTactics) : this(subTactics) => Guard = guard;
/// <param name="metadata">
/// Metadata about this tactic, used to quickly display the tactic in several contexts.
/// </param>
/// <param name="subTactics">The list of sub-tactics.</param>
public AnyOfTactic(Func<bool> guard, Metadata? metadata = null, params Tactic[] subTactics)
: this(metadata, subTactics) => Guard = guard;

/// <inheritdoc/>
public override Action? GetAction()
Expand Down
14 changes: 11 additions & 3 deletions Aplib.Core/Intent/Tactics/FirstOfTactic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,25 @@ public class FirstOfTactic : AnyOfTactic
/// <summary>
/// Initializes a new instance of the <see cref="FirstOfTactic"/> class with the specified sub-tactics.
/// </summary>
/// <param name="metadata">
/// Metadata about this tactic, used to quickly display the tactic in several contexts.
/// </param>
/// <param name="subTactics">The list of sub-tactics.</param>
public FirstOfTactic(params Tactic[] subTactics) : base(subTactics)
public FirstOfTactic(Metadata? metadata = null, params Tactic[] subTactics)
: base(metadata, subTactics)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="FirstOfTactic"/> class with the specified sub-tactics and guard condition.
/// </summary>
/// <param name="subTactics">The list of sub-tactics.</param>
/// <param name="guard">The guard condition.</param>
public FirstOfTactic(Func<bool> guard, params Tactic[] subTactics) : base(guard, subTactics)
/// <param name="metadata">
/// Metadata about this tactic, used to quickly display the tactic in several contexts.
/// </param>
/// <param name="subTactics">The list of sub-tactics.</param>
public FirstOfTactic(Func<bool> guard, Metadata? metadata = null, params Tactic[] subTactics)
: base(guard, metadata, subTactics)
{
}

Expand Down
12 changes: 10 additions & 2 deletions Aplib.Core/Intent/Tactics/PrimitiveTactic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,22 @@ public class PrimitiveTactic : Tactic
/// Initializes a new instance of the <see cref="PrimitiveTactic"/> class with the specified action.
/// </summary>
/// <param name="action">The action of the primitive tactic.</param>
public PrimitiveTactic(Action action) => Action = action;
/// <param name="metadata">
/// Metadata about this tactic, used to quickly display the tactic in several contexts.
/// </param>
public PrimitiveTactic(Action action, Metadata? metadata = null)
: base(metadata) => Action = action;

/// <summary>
/// Initializes a new instance of the <see cref="PrimitiveTactic"/> class with the specified action and guard.
/// </summary>
/// <param name="action">The action of the primitive tactic.</param>
/// <param name="guard">The guard of the primitive tactic.</param>
public PrimitiveTactic(Action action, Func<bool> guard) : base(guard) => Action = action;
/// <param name="metadata">
/// Metadata about this tactic, used to quickly display the tactic in several contexts.
/// </param>
public PrimitiveTactic(Action action, Func<bool> guard, Metadata? metadata = null)
: base(guard, metadata) => Action = action;

/// <inheritdoc/>
public override Action? GetAction() => IsActionable() ? Action : null;
Expand Down
29 changes: 25 additions & 4 deletions Aplib.Core/Intent/Tactics/Tactic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,44 @@ namespace Aplib.Core.Intent.Tactics
/// <seealso cref="Intent.Actions.Action"/>
public abstract class Tactic
{
/// <summary>
/// Gets the metadata of the tactic.
/// </summary>
/// <remark>
/// This metadata may be useful for debugging or logging.
/// </remark>
public Metadata Metadata { get; }

/// <summary>
/// Gets or sets the guard of the tactic.
/// </summary>
protected Func<bool> Guard { get; set; } = () => true;

/// <summary>
/// Parameterless constructor for internal use.
/// </summary>
private protected Tactic() : this(null) { }

/// <summary>
/// Initializes a new instance of the <see cref="Tactic"/>.
/// </summary>
protected Tactic()
{
}
/// <param name="metadata">
/// Metadata about this tactic, used to quickly display the tactic in several contexts.
/// </param>
protected Tactic(Metadata? metadata) => Metadata = metadata ?? new Metadata();

/// <summary>
/// Initializes a new instance of the <see cref="Tactic"/> class with a specified guard.
/// </summary>
/// <param name="guard">The guard of the tactic.</param>
protected Tactic(Func<bool> guard) => Guard = guard;
/// <param name="metadata">
/// Metadata about this tactic, used to quickly display the tactic in several contexts.
/// </param>
protected Tactic(Func<bool> guard, Metadata? metadata = null)
{
Guard = guard;
Metadata = metadata ?? new Metadata();
}

/// <summary>
/// Gets the enabled action.
Expand Down
47 changes: 47 additions & 0 deletions Aplib.Core/Metadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;

namespace Aplib.Core
{
/// <summary>
/// Data structure to store information about a component which may be useful for debugging or logging.
/// </summary>
public class Metadata
{
/// <summary>
/// Gets the unique identifier of the component.
/// </summary>
public Guid Id { get; }

/// <summary>
/// Gets the name used to display the component during debugging, logging, or general overviews.
/// </summary>
public string? Name { get; }

/// <summary>
/// Gets the description used to describe the component during debugging, logging, or general overviews.
/// </summary>
public string? Description { get; }

/// <summary>
/// Store information about a component which may be useful for debugging or logging or general overviews.
/// </summary>
/// <param name="name">The name used to display the component.</param>
/// <param name="description">The description used to describe the component.</param>
public Metadata(string? name = null, string? description = null)
{
Id = Guid.NewGuid();
Name = name;
Description = description;
}

/// <summary>
/// Store information about a component which may be useful for debugging or logging or general overviews.
/// </summary>
/// <remarks>This constructor is mainly for testing.</remarks>
/// <param name="id">A unique identifier for the component.</param>
/// <param name="name">The name used to display the component.</param>
/// <param name="description">The description used to describe the component.</param>
internal Metadata(Guid id, string? name = null, string? description = null)
: this(name, description) => Id = id;
}
}
4 changes: 4 additions & 0 deletions Aplib.Tests/Aplib.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@
<ProjectReference Include="..\Aplib.Core\Aplib.Core.csproj"/>
</ItemGroup>

<ItemGroup>
<Folder Include="Stubs\" />
</ItemGroup>

</Project>
14 changes: 6 additions & 8 deletions Aplib.Tests/Core/Desire/GoalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,15 @@ public void Goal_WhenConstructed_ContainsCorrectMetaData()
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";
Metadata metadata = new(name, description);

// Act
Goal goal = new(tactic, heuristicFunction, name, description); // Does not use helper methods on purpose
// Does not use helper methods on purpose
Goal goal = new(tactic, heuristicFunction: heuristicFunction, metadata: metadata);

// Assert
goal.Should().NotBeNull();
goal.Name.Should().Be(name);
goal.Description.Should().Be(description);
goal.Metadata.Should().Be(metadata);
}

/// <summary>
Expand Down Expand Up @@ -154,15 +155,12 @@ public void GoalConstructor_WhereHeuristicFunctionTypeDiffers_HasEqualBehaviour(
{
// Arrange
Tactic tactic = Mock.Of<Tactic>();
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";

Func<bool> heuristicFunctionBoolean = () => goalCompleted;
Goal.HeuristicFunction heuristicFunctionNonBoolean = CommonHeuristicFunctions.Boolean(() => goalCompleted);

Goal goalBoolean = new(tactic, heuristicFunctionBoolean, name, description);
Goal goalNonBoolean = new(tactic, heuristicFunctionNonBoolean, name, description);
Goal goalBoolean = new(tactic, heuristicFunctionBoolean);
Goal goalNonBoolean = new(tactic, heuristicFunctionNonBoolean);

// Act
MyBeliefSet beliefSet = new();
Expand Down
Loading

0 comments on commit 553533f

Please sign in to comment.