Add movement order validation tests
This commit is contained in:
parent
00cac2cb89
commit
c50dbf6b46
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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,15 +433,13 @@ 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))
|
||||
.Locations.Single(l => l.Name == coastName || l.Abbreviation == coastName);
|
||||
Location Coast(string provinceName, string coastName)
|
||||
=> GetProvince(provinceName, standardProvinces)
|
||||
.Locations.Single(l => l.Name == coastName || l.Abbreviation == coastName);
|
||||
|
||||
Land("NAF").AddBorder(Land("TUN"));
|
||||
Water("NAF").AddBorder(Water("MAO"));
|
||||
@ -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);
|
||||
}
|
||||
|
110
MultiversalDiplomacyTests/MovementAdjudicatorTest.cs
Normal file
110
MultiversalDiplomacyTests/MovementAdjudicatorTest.cs
Normal 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");
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user