-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
328 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Aplib.Core.Tactics | ||
{ | ||
/// <summary> | ||
/// Represents a tactic that executes any of the provided sub-tactics. | ||
/// </summary> | ||
public class AnyOfTactic : Tactic | ||
{ | ||
/// <summary> | ||
/// Gets or sets the sub-tactics of the tactic. | ||
/// </summary> | ||
protected LinkedList<Tactic> SubTactics { get; set; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="AnyOfTactic"/> class with the specified sub-tactics. | ||
/// </summary> | ||
/// <param name="subTactics">The list of sub-tactics.</param> | ||
public AnyOfTactic(List<Tactic> subTactics) | ||
{ | ||
SubTactics = new(); | ||
|
||
foreach (Tactic tactic in subTactics) | ||
{ | ||
_ = SubTactics.AddLast(tactic); | ||
} | ||
} | ||
|
||
/// <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(List<Tactic> subTactics, Func<bool> guard) : this(subTactics) => Guard = guard; | ||
|
||
/// <inheritdoc/> | ||
public override List<PrimitiveTactic> GetFirstEnabledActions() | ||
{ | ||
List<PrimitiveTactic> primitiveTactics = new(); | ||
|
||
foreach (Tactic subTactic in SubTactics) | ||
{ | ||
primitiveTactics.AddRange(subTactic.GetFirstEnabledActions()); | ||
} | ||
|
||
return primitiveTactics; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Aplib.Core.Tactics | ||
{ | ||
/// <summary> | ||
/// Represents a tactic that executes the first enabled action from a list of sub-tactics. | ||
/// </summary> | ||
public class FirstOfTactic : AnyOfTactic | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="FirstOfTactic"/> class with the specified sub-tactics. | ||
/// </summary> | ||
/// <param name="subTactics">The list of sub-tactics.</param> | ||
public FirstOfTactic(List<Tactic> subTactics) : base(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(List<Tactic> subTactics, Func<bool> guard) : base(subTactics, guard) | ||
{ | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override List<PrimitiveTactic> GetFirstEnabledActions() | ||
{ | ||
foreach (Tactic subTactic in SubTactics) | ||
{ | ||
List<PrimitiveTactic> firstOfTactics = subTactic.GetFirstEnabledActions(); | ||
|
||
if (firstOfTactics.Count > 0) | ||
return firstOfTactics; | ||
} | ||
|
||
return new(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Aplib.Core.Tactics | ||
{ | ||
/// <summary> | ||
/// Represents a primitive tactic in the Aplib.Core namespace. | ||
/// </summary> | ||
public class PrimitiveTactic : Tactic | ||
{ | ||
/// <summary> | ||
/// Gets or sets the action of the primitive tactic. | ||
/// </summary> | ||
public readonly Action Action; | ||
|
||
/// <summary> | ||
/// 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; | ||
|
||
/// <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 tactic.</param> | ||
public PrimitiveTactic(Action action, Func<bool> guard) : base(guard) => Action = action; | ||
|
||
/// <inheritdoc/> | ||
public override List<PrimitiveTactic> GetFirstEnabledActions() => IsActionable() ? new() { this } : new(); | ||
|
||
/// <inheritdoc/> | ||
public override bool IsActionable() => base.IsActionable() && Action.IsActionable(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Aplib.Core.Tactics | ||
{ | ||
/// <summary> | ||
/// Represents a tactic in the Aplib.Core namespace. | ||
/// </summary> | ||
public abstract class Tactic | ||
{ | ||
/// <summary> | ||
/// Gets or sets the guard of the tactic. | ||
/// </summary> | ||
protected Func<bool> Guard { get; set; } = () => true; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="Tactic"/>. | ||
/// </summary> | ||
protected Tactic() | ||
{ | ||
} | ||
|
||
/// <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; | ||
|
||
/// <summary> | ||
/// Gets the first enabled primitive actions. | ||
/// </summary> | ||
/// <returns>A list of primitive tactics that are enabled.</returns> | ||
public abstract List<PrimitiveTactic> GetFirstEnabledActions(); | ||
|
||
/// <summary> | ||
/// Determines whether the tactic is actionable. | ||
/// </summary> | ||
/// <returns>True if the tactic is actionable, false otherwise.</returns> | ||
public virtual bool IsActionable() => Guard(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
using Aplib.Core.Tactics; | ||
using Action = Aplib.Core.Action; | ||
|
||
namespace Aplib.Tests.Core.Tactics; | ||
public class TacticTests | ||
{ | ||
private readonly Action _emptyAction = new(() => { }); | ||
|
||
private static bool TrueGuard() => true; | ||
|
||
private static bool FalseGuard() => false; | ||
|
||
/// <summary> | ||
/// Given a parent of type <see cref="TacticType.FirstOf"/> with two subtactics, | ||
/// When getting the next tactic, | ||
/// Then the result should be the first subtactic. | ||
/// </summary> | ||
[Fact] | ||
public void GetFirstEnabledActions_WhenTacticTypeIsFirstOf_ReturnsEnabledPrimitiveTactics() | ||
{ | ||
// Arrange | ||
PrimitiveTactic tactic1 = new(_emptyAction); | ||
PrimitiveTactic tactic2 = new(_emptyAction); | ||
FirstOfTactic parentTactic = new([tactic1, tactic2]); | ||
|
||
// Act | ||
List<PrimitiveTactic> enabledActions = parentTactic.GetFirstEnabledActions(); | ||
|
||
// Assert | ||
Assert.Contains(tactic1, enabledActions); | ||
} | ||
|
||
/// <summary> | ||
/// Given a parent of type <see cref="TacticType.FirstOf"/> with two subtactics and a guard that is true, | ||
/// When getting the next tactic, | ||
/// Then the result should be the first subtactic. | ||
/// </summary> | ||
[Fact] | ||
public void GetFirstEnabledActions_WhenTacticTypeIsFirstOfAndGuardEnabled_ReturnsEnabledPrimitiveTactics() | ||
{ | ||
// Arrange | ||
PrimitiveTactic tactic1 = new(_emptyAction); | ||
PrimitiveTactic tactic2 = new(_emptyAction); | ||
FirstOfTactic parentTactic = new([tactic1, tactic2], TrueGuard); | ||
|
||
// Act | ||
List<PrimitiveTactic> enabledActions = parentTactic.GetFirstEnabledActions(); | ||
|
||
// Assert | ||
Assert.Contains(tactic1, enabledActions); | ||
} | ||
|
||
/// <summary> | ||
/// Given a parent of type <see cref="TacticType.AnyOf"/> with two subtactics, | ||
/// When getting the next tactic, | ||
/// Then the result should contain all the subtactics. | ||
/// </summary> | ||
[Fact] | ||
public void GetFirstEnabledActions_WhenTacticTypeIsAnyOf_ReturnsEnabledPrimitiveTactics() | ||
{ | ||
// Arrange | ||
PrimitiveTactic tactic1 = new(_emptyAction); | ||
PrimitiveTactic tactic2 = new(_emptyAction); | ||
AnyOfTactic parentTactic = new([tactic1, tactic2]); | ||
|
||
// Act | ||
List<PrimitiveTactic> enabledActions = parentTactic.GetFirstEnabledActions(); | ||
|
||
// Assert | ||
Assert.Contains(tactic1, enabledActions); | ||
Assert.Contains(tactic2, enabledActions); | ||
} | ||
|
||
/// <summary> | ||
/// Given a primitive tactic with an actionable action, | ||
/// When getting the first enabled actions, | ||
/// Then the result should contain the primitive tactic. | ||
/// </summary> | ||
[Fact] | ||
public void GetFirstEnabledActions_WhenTacticTypeIsPrimitiveAndActionIsActionable_ReturnsEnabledPrimitiveTactic() | ||
{ | ||
// Arrange | ||
PrimitiveTactic tactic = new(_emptyAction, TrueGuard); | ||
|
||
// Act | ||
List<PrimitiveTactic> enabledActions = tactic.GetFirstEnabledActions(); | ||
|
||
// Assert | ||
Assert.Contains(tactic, enabledActions); | ||
} | ||
|
||
/// <summary> | ||
/// Given a primitive tactic with a non-actionable action, | ||
/// When getting the first enabled actions, | ||
/// Then the result should be an empty list. | ||
/// </summary> | ||
[Fact] | ||
public void GetFirstEnabledActions_WhenTacticTypeIsPrimitiveAndActionIsNotActionable_ReturnsEmptyList() | ||
{ | ||
// Arrange | ||
PrimitiveTactic tactic = new(_emptyAction, FalseGuard); | ||
|
||
// Act | ||
List<PrimitiveTactic> enabledActions = tactic.GetFirstEnabledActions(); | ||
|
||
// Assert | ||
Assert.Empty(enabledActions); | ||
} | ||
|
||
/// <summary> | ||
/// Given a tactic with a guard that returns true, | ||
/// When checking if the tactic is actionable, | ||
/// Then the result should be true. | ||
/// </summary> | ||
[Fact] | ||
public void IsActionable_WhenGuardReturnsTrue_ReturnsTrue() | ||
{ | ||
// Arrange | ||
PrimitiveTactic tactic = new(_emptyAction, TrueGuard); | ||
|
||
// Act | ||
bool isActionable = tactic.IsActionable(); | ||
|
||
// Assert | ||
Assert.True(isActionable); | ||
} | ||
|
||
/// <summary> | ||
/// Given a tactic with a guard that returns false, | ||
/// When checking if the tactic is actionable, | ||
/// Then the result should be false. | ||
/// </summary> | ||
[Fact] | ||
public void IsActionable_WhenGuardReturnsFalse_ReturnsFalse() | ||
{ | ||
// Arrange | ||
PrimitiveTactic tactic = new(_emptyAction, FalseGuard); | ||
|
||
// Act | ||
bool isActionable = tactic.IsActionable(); | ||
|
||
// Assert | ||
Assert.False(isActionable); | ||
} | ||
} |