From c4f5145320f96127e7d1c00c24f1595affc65cdb Mon Sep 17 00:00:00 2001 From: Jaculabilis Date: Fri, 18 Feb 2022 14:52:15 -0800 Subject: [PATCH] Add order model and adjudicator framework --- .../Adjudicate/IPhaseAdjudicator.cs | 22 +++++++++ .../Adjudicate/OrderValidation.cs | 46 +++++++++++++++++++ .../Adjudicate/ValidationReason.cs | 24 ++++++++++ MultiversalDiplomacy/Orders/Order.cs | 19 ++++++++ MultiversalDiplomacyTests/AdjudicatorTests.cs | 28 +++++++++++ MultiversalDiplomacyTests/NullOrder.cs | 9 ++++ MultiversalDiplomacyTests/TestAdjudicator.cs | 18 ++++++++ 7 files changed, 166 insertions(+) create mode 100644 MultiversalDiplomacy/Adjudicate/IPhaseAdjudicator.cs create mode 100644 MultiversalDiplomacy/Adjudicate/OrderValidation.cs create mode 100644 MultiversalDiplomacy/Adjudicate/ValidationReason.cs create mode 100644 MultiversalDiplomacy/Orders/Order.cs create mode 100644 MultiversalDiplomacyTests/AdjudicatorTests.cs create mode 100644 MultiversalDiplomacyTests/NullOrder.cs create mode 100644 MultiversalDiplomacyTests/TestAdjudicator.cs diff --git a/MultiversalDiplomacy/Adjudicate/IPhaseAdjudicator.cs b/MultiversalDiplomacy/Adjudicate/IPhaseAdjudicator.cs new file mode 100644 index 0000000..3bdfbf9 --- /dev/null +++ b/MultiversalDiplomacy/Adjudicate/IPhaseAdjudicator.cs @@ -0,0 +1,22 @@ +using MultiversalDiplomacy.Orders; + +namespace MultiversalDiplomacy.Adjudicate; + +/// +/// An input handler for game phases. +/// +public interface IPhaseAdjudicator +{ + /// + /// Given a list of orders, determine which orders are valid for this adjudicator and + /// which should be rejected before adjudication. Adjudication should be performed on + /// all orders in the output for which is true. + /// + /// Orders to validate for adjudication. + /// + /// A list of order validation results. Note that this list may be longer than the input + /// list if illegal orders were replaced with hold orders, as there will be an invalid + /// result for the illegal order and a valid result for the replacement order. + /// + public IEnumerable ValidateOrders(IEnumerable orders); +} diff --git a/MultiversalDiplomacy/Adjudicate/OrderValidation.cs b/MultiversalDiplomacy/Adjudicate/OrderValidation.cs new file mode 100644 index 0000000..553e47c --- /dev/null +++ b/MultiversalDiplomacy/Adjudicate/OrderValidation.cs @@ -0,0 +1,46 @@ +using MultiversalDiplomacy.Orders; + +namespace MultiversalDiplomacy.Adjudicate; + +/// +/// Represents the result of validating an order. +/// +public class OrderValidation +{ + /// + /// The order that was validated. + /// + public Order Order { get; } + + /// + /// Whether the order is valid. + /// + public bool Valid { get; } + + /// + /// The reason for the order validation result. + /// + public ValidationReason Reason { get; } + + internal OrderValidation(Order order, bool valid, ValidationReason reason) + { + this.Order = order; + this.Valid = valid; + this.Reason = reason; + } +} + +public static class OrderValidationExtensions +{ + /// + /// Create an accepting this order. + /// + public static OrderValidation Validate(this Order order, ValidationReason reason) + => new OrderValidation(order, true, reason); + + /// + /// Create an rejecting this order. + /// + public static OrderValidation Invalidate(this Order order, ValidationReason reason) + => new OrderValidation(order, false, reason); +} diff --git a/MultiversalDiplomacy/Adjudicate/ValidationReason.cs b/MultiversalDiplomacy/Adjudicate/ValidationReason.cs new file mode 100644 index 0000000..50b65d3 --- /dev/null +++ b/MultiversalDiplomacy/Adjudicate/ValidationReason.cs @@ -0,0 +1,24 @@ +namespace MultiversalDiplomacy.Adjudicate; + +public enum ValidationReason +{ + /// + /// The order is valid. + /// + Valid = 0, + + /// + /// The order type is not valid for the current phase of the game. + /// + InvalidOrderTypeForPhase = 1, + + /// + /// A hold order was created to replace an illegal order. + /// + IllegalOrderReplacedWithHold = 2, + + /// + /// Another order was submitted that replaced this order. + /// + SupersededByLaterOrder = 3, +} \ No newline at end of file diff --git a/MultiversalDiplomacy/Orders/Order.cs b/MultiversalDiplomacy/Orders/Order.cs new file mode 100644 index 0000000..a8f1900 --- /dev/null +++ b/MultiversalDiplomacy/Orders/Order.cs @@ -0,0 +1,19 @@ +using MultiversalDiplomacy.Model; + +namespace MultiversalDiplomacy.Orders; + +/// +/// A submitted action by a power. +/// +public abstract class Order +{ + /// + /// The power that submitted this order. + /// + public Power Power { get; } + + public Order(Power power) + { + this.Power = power; + } +} diff --git a/MultiversalDiplomacyTests/AdjudicatorTests.cs b/MultiversalDiplomacyTests/AdjudicatorTests.cs new file mode 100644 index 0000000..576c2d8 --- /dev/null +++ b/MultiversalDiplomacyTests/AdjudicatorTests.cs @@ -0,0 +1,28 @@ +using MultiversalDiplomacy.Adjudicate; +using MultiversalDiplomacy.Model; +using MultiversalDiplomacy.Orders; + +using NUnit.Framework; + +namespace MultiversalDiplomacyTests; + +public class AdjudicatorTests +{ + [Test] + public void OrderValidationTest() + { + IPhaseAdjudicator rubberStamp = new TestAdjudicator(orders => { + return orders.Select(o => o.Validate(ValidationReason.Valid)); + }); + Power power = new Power(nameof(Power)); + + Order order = new NullOrder(power); + List orders = new List { order }; + IEnumerable results = rubberStamp.ValidateOrders(orders); + + Assert.That(results.Count(), Is.EqualTo(1)); + Assert.That(results.First().Order, Is.EqualTo(order)); + Assert.That(results.First().Reason, Is.EqualTo(ValidationReason.Valid)); + Assert.That(results.First().Valid, Is.True); + } +} \ No newline at end of file diff --git a/MultiversalDiplomacyTests/NullOrder.cs b/MultiversalDiplomacyTests/NullOrder.cs new file mode 100644 index 0000000..cab6c7a --- /dev/null +++ b/MultiversalDiplomacyTests/NullOrder.cs @@ -0,0 +1,9 @@ +using MultiversalDiplomacy.Model; +using MultiversalDiplomacy.Orders; + +namespace MultiversalDiplomacyTests; + +public class NullOrder : Order +{ + public NullOrder(Power power) : base(power) {} +} \ No newline at end of file diff --git a/MultiversalDiplomacyTests/TestAdjudicator.cs b/MultiversalDiplomacyTests/TestAdjudicator.cs new file mode 100644 index 0000000..0404b94 --- /dev/null +++ b/MultiversalDiplomacyTests/TestAdjudicator.cs @@ -0,0 +1,18 @@ +using MultiversalDiplomacy.Adjudicate; +using MultiversalDiplomacy.Orders; + +namespace MultiversalDiplomacyTests; + +public class TestAdjudicator : IPhaseAdjudicator +{ + private Func, IEnumerable> ValidateOrdersCallback; + + public TestAdjudicator( + Func, IEnumerable> validateOrdersCallback) + { + this.ValidateOrdersCallback = validateOrdersCallback; + } + + public IEnumerable ValidateOrders(IEnumerable orders) + => this.ValidateOrdersCallback.Invoke(orders); +} \ No newline at end of file