Add movement order validation tests

This commit is contained in:
Jaculabilis 2022-03-22 21:27:06 -07:00
parent 00cac2cb89
commit c50dbf6b46
4 changed files with 295 additions and 19 deletions

View File

@ -51,11 +51,12 @@ internal static class AdjudicatorHelpers
.ToList();
if (nonOrderTypes.Any())
{
throw new ArgumentException($"Unknown order type: {nonOrderTypes.Select(t => t.FullName).First()}");
throw new ArgumentException(
$"Unknown order type: {nonOrderTypes.Select(t => t.FullName).First()}");
}
InvalidateIfNotMatching(
order => !validOrderTypes.Contains(order.GetType()),
order => validOrderTypes.Contains(order.GetType()),
ValidationReason.InvalidOrderTypeForPhase,
ref orders,
ref invalidOrders);
@ -83,7 +84,8 @@ internal static class AdjudicatorHelpers
RetreatOrder retreat => retreat.Power == retreat.Unit.Power,
SupportHoldOrder support => support.Power == support.Unit.Power,
SupportMoveOrder support => support.Power == support.Unit.Power,
// Any order not given to a unit by definition cannot be given to a unit of the wrong power
// Any order not given to a unit, by definition, cannot be given to a unit of the
// wrong power
_ => true,
},
ValidationReason.InvalidUnitForPower,

View File

@ -6,7 +6,7 @@ namespace MultiversalDiplomacy.Adjudicate;
/// <summary>
/// Adjudicator for the movement phase.
/// </summary>
internal class MovementPhaseAdjudicator : IPhaseAdjudicator
public class MovementPhaseAdjudicator : IPhaseAdjudicator
{
public List<OrderValidation> ValidateOrders(World world, List<Order> orders)
{
@ -35,7 +35,7 @@ internal class MovementPhaseAdjudicator : IPhaseAdjudicator
AdjudicatorHelpers.InvalidateWrongPower(orders, ref orders, ref validationResults);
// Since all the order types in this phase are UnitOrders, downcast to get the Unit.
List<UnitOrder> unitOrders = orders.OfType<UnitOrder>().ToList();
List<UnitOrder> unitOrders = orders.Cast<UnitOrder>().ToList();
// Invalidate any order given to a unit in the past.
AdjudicatorHelpers.InvalidateIfNotMatching(

View File

@ -96,7 +96,7 @@ public class World
{
"A" => UnitType.Army,
"F" => UnitType.Fleet,
_ => throw new ArgumentOutOfRangeException($"Unknown unit type {splits[1]}")
_ => throw new ApplicationException($"Unknown unit type {splits[1]}")
};
Location location = type == UnitType.Army
? this.GetLand(splits[2])
@ -152,14 +152,20 @@ public class World
/// Get a province by name. Throws if the province is not found.
/// </summary>
private Province GetProvince(string provinceName)
=> GetProvince(provinceName, this.Provinces);
/// <summary>
/// Get a province by name. Throws if the province is not found.
/// </summary>
private static Province GetProvince(string provinceName, IEnumerable<Province> provinces)
{
string provinceNameUpper = provinceName.ToUpperInvariant();
Province? foundProvince = this.Provinces.SingleOrDefault(
Province? foundProvince = provinces.SingleOrDefault(
p => p != null &&
(p.Name.ToUpperInvariant() == provinceNameUpper
|| p.Abbreviations.Any(a => a.ToUpperInvariant() == provinceNameUpper)),
null);
if (foundProvince == null) throw new ArgumentOutOfRangeException(
if (foundProvince == null) throw new KeyNotFoundException(
$"Province {provinceName} not found");
return foundProvince;
}
@ -172,7 +178,7 @@ public class World
{
Location? foundLocation = GetProvince(provinceName).Locations.SingleOrDefault(
l => l != null && predicate(l), null);
if (foundLocation == null) throw new ArgumentException(
if (foundLocation == null) throw new KeyNotFoundException(
$"No such location in {provinceName}");
return foundLocation;
}
@ -201,7 +207,7 @@ public class World
p != null
&& (p.Name == powerName || p.Name.StartsWith(powerName)),
null);
if (foundPower == null) throw new ArgumentOutOfRangeException(
if (foundPower == null) throw new KeyNotFoundException(
$"Power {powerName} not found");
return foundPower;
}
@ -427,14 +433,12 @@ public class World
};
// Declare some helpers for border definitions
Location Land(string provinceName) => standardProvinces
.Single(p => p.Name == provinceName || p.Abbreviations.Contains(provinceName))
Location Land(string provinceName) => GetProvince(provinceName, standardProvinces)
.Locations.Single(l => l.Type == LocationType.Land);
Location Water(string provinceName) => standardProvinces
.Single(p => p.Name == provinceName || p.Abbreviations.Contains(provinceName))
Location Water(string provinceName) => GetProvince(provinceName, standardProvinces)
.Locations.Single(l => l.Type == LocationType.Water);
Location Coast(string provinceName, string coastName) => standardProvinces
.Single(p => p.Name == provinceName || p.Abbreviations.Contains(provinceName))
Location Coast(string provinceName, string coastName)
=> GetProvince(provinceName, standardProvinces)
.Locations.Single(l => l.Name == coastName || l.Abbreviation == coastName);
Land("NAF").AddBorder(Land("TUN"));
@ -517,7 +521,165 @@ public class World
Water("GRE").AddBorder(Water("AEG"));
Water("GRE").AddBorder(Coast("BUL", "sc"));
// TODO
// RUM
// SER
// CLY
// EDI
// LVP
Land("LON").AddBorder(Land("WAL"));
Land("LON").AddBorder(Land("YOR"));
Water("LON").AddBorder(Water("ENC"));
Water("LON").AddBorder(Water("NTH"));
// WAL
// YOR
// BRE
// BUR
// GAS
// MAR
// PAR
// PIC
Land("BER").AddBorder(Land("PRU"));
Land("BER").AddBorder(Land("SIL"));
Land("BER").AddBorder(Land("MUN"));
Land("BER").AddBorder(Land("KIE"));
Water("BER").AddBorder(Water("KIE"));
Water("BER").AddBorder(Water("BAL"));
Water("BER").AddBorder(Water("PRU"));
Land("KIE").AddBorder(Land("BER"));
Land("KIE").AddBorder(Land("MUN"));
Land("KIE").AddBorder(Land("RUH"));
Land("KIE").AddBorder(Land("HOL"));
Land("KIE").AddBorder(Land("DEN"));
Water("KIE").AddBorder(Water("HOL"));
Water("KIE").AddBorder(Water("HEL"));
Water("KIE").AddBorder(Water("DEN"));
Water("KIE").AddBorder(Water("BAL"));
Water("KIE").AddBorder(Water("BER"));
Land("MUN").AddBorder(Land("BUR"));
Land("MUN").AddBorder(Land("RUH"));
Land("MUN").AddBorder(Land("KIE"));
Land("MUN").AddBorder(Land("BER"));
Land("MUN").AddBorder(Land("SIL"));
Land("MUN").AddBorder(Land("BOH"));
Land("MUN").AddBorder(Land("TYR"));
// PRU
// RUH
// SIL
// SPA
// POR
// APU
// NAP
// PIE
// ROM
// TUS
// VEN
// BEL
Land("HOL").AddBorder(Land("BEL"));
Land("HOL").AddBorder(Land("RUH"));
Land("HOL").AddBorder(Land("KIE"));
Water("HOL").AddBorder(Water("NTH"));
Water("HOL").AddBorder(Water("HEL"));
// FIN
// LVN
// MOS
// SEV
// STP
// UKR
// WAR
// DEN
// NWY
// SWE
// ANK
// ARM
// CON
// SMY
// SYR
// BAR
// ENC
// HEL
// IRS
// MAO
// NAO
Water("NTH").AddBorder(Water("NWG"));
Water("NTH").AddBorder(Water("NWY"));
Water("NTH").AddBorder(Water("SKA"));
Water("NTH").AddBorder(Water("DEN"));
Water("NTH").AddBorder(Water("HEL"));
Water("NTH").AddBorder(Water("HOL"));
Water("NTH").AddBorder(Water("BEL"));
Water("NTH").AddBorder(Water("ENC"));
Water("NTH").AddBorder(Water("LON"));
Water("NTH").AddBorder(Water("YOR"));
Water("NTH").AddBorder(Water("EDI"));
// NWS
// SKA
// BAL
// GOB
// ADS
// AEG
// BLA
// EMS
// GOL
Water("IOS").AddBorder(Water("TUN"));
Water("IOS").AddBorder(Water("TYS"));
@ -528,7 +690,9 @@ public class World
Water("IOS").AddBorder(Water("GRE"));
Water("IOS").AddBorder(Water("AEG"));
// TODO
// TYS
// WMS
return new(standardProvinces);
}

View File

@ -0,0 +1,110 @@
using MultiversalDiplomacy.Adjudicate;
using MultiversalDiplomacy.Model;
using MultiversalDiplomacy.Orders;
using NUnit.Framework;
namespace MultiversalDiplomacyTests;
public class MovementAdjudicatorTest
{
[Test]
public void Validation_ValidHold()
{
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
setup["Germany"]
.Army("Mun").Holds().GetReference(out var order);
setup.ValidateOrders(new MovementPhaseAdjudicator());
Assert.Multiple(() =>
{
Assert.That(order.Validation.Valid, Is.True, "Unexpected validation result");
Assert.That(
order.Validation.Reason,
Is.EqualTo(ValidationReason.Valid),
"Unexpected validation reason");
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
});
}
[Test]
public void Validation_ValidMove()
{
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
setup["Germany"]
.Army("Mun").MovesTo("Tyr").GetReference(out var order);
setup.ValidateOrders(new MovementPhaseAdjudicator());
Assert.Multiple(() =>
{
Assert.That(order.Validation.Valid, Is.True, "Unexpected validation result");
Assert.That(
order.Validation.Reason,
Is.EqualTo(ValidationReason.Valid),
"Unexpected validation reason");
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
});
}
[Test]
public void Validation_ValidConvoy()
{
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
setup["Germany"]
.Fleet("Nth").Convoys.Army("Hol").To("Lon").GetReference(out var order);
setup.ValidateOrders(new MovementPhaseAdjudicator());
Assert.Multiple(() =>
{
Assert.That(order.Validation.Valid, Is.True, "Unexpected validation result");
Assert.That(
order.Validation.Reason,
Is.EqualTo(ValidationReason.Valid),
"Unexpected validation reason");
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
});
}
[Test]
public void Validation_ValidSupportHold()
{
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
setup["Germany"]
.Army("Mun").Supports.Army("Kie").Hold().GetReference(out var order);
setup.ValidateOrders(new MovementPhaseAdjudicator());
Assert.Multiple(() =>
{
Assert.That(order.Validation.Valid, Is.True, "Unexpected validation result");
Assert.That(
order.Validation.Reason,
Is.EqualTo(ValidationReason.Valid),
"Unexpected validation reason");
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
});
}
[Test]
public void Validation_ValidSupportMove()
{
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
setup["Germany"]
.Army("Mun").Supports.Army("Kie").MoveTo("Ber").GetReference(out var order);
setup.ValidateOrders(new MovementPhaseAdjudicator());
Assert.Multiple(() =>
{
Assert.That(order.Validation.Valid, Is.True, "Unexpected validation result");
Assert.That(
order.Validation.Reason,
Is.EqualTo(ValidationReason.Valid),
"Unexpected validation reason");
Assert.That(order.Replacement, Is.Null, "Unexpected order replacement");
});
}
}