Shift usage of Unit.Location to Unit.LocationId

This is in preparation for removing province and location references from Unit
This commit is contained in:
Tim Van Baak 2024-08-14 07:39:49 -07:00
parent 7400334a3d
commit 4df5ef84dc
9 changed files with 52 additions and 41 deletions

View File

@ -77,7 +77,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
// Trivial check: a unit cannot move to where it already is.
AdjudicatorHelpers.InvalidateIfNotMatching(
order => !(order.Location == order.Unit.Location && order.Season == order.Unit.Season),
order => !(order.Location.Designation == order.Unit.LocationId && order.Season == order.Unit.Season),
ValidationReason.DestinationMatchesOrigin,
ref moveOrders,
ref validationResults);
@ -90,7 +90,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
ILookup<bool, MoveOrder> moveOrdersByAdjacency = moveOrders
.ToLookup(order =>
// Map adjacency
order.Unit.Location.Adjacents.Contains(order.Location)
world.Map.GetLocation(order.Unit).Adjacents.Contains(order.Location)
// Turn adjacency
&& Math.Abs(order.Unit.Season.Turn - order.Season.Turn) <= 1
// Timeline adjacency
@ -138,7 +138,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
// Trivial check: cannot convoy a unit to its own location
AdjudicatorHelpers.InvalidateIfNotMatching(
order => !(
order.Location == order.Target.Location
order.Location.Designation == order.Target.LocationId
&& order.Season == order.Target.Season),
ValidationReason.DestinationMatchesOrigin,
ref convoyOrders,
@ -175,8 +175,8 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
AdjudicatorHelpers.InvalidateIfNotMatching(
order =>
// Map adjacency with respect to province
order.Unit.Location.Adjacents.Any(
adjLocation => adjLocation.Province == order.Target.Province)
world.Map.GetLocation(order.Unit).Adjacents.Any(
adjLocation => adjLocation.Province == world.Map.GetLocation(order.Target).Province)
// Turn adjacency
&& Math.Abs(order.Unit.Season.Turn - order.Target.Season.Turn) <= 1
// Timeline adjacency
@ -195,7 +195,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
// Support-move orders are invalid if the unit supports a move to any location in its own
// province.
AdjudicatorHelpers.InvalidateIfNotMatching(
order => order.Unit.Province != order.Province,
order => world.Map.GetLocation(order.Unit).Province != order.Province,
ValidationReason.NoSupportMoveAgainstSelf,
ref supportMoveOrders,
ref validationResults);
@ -207,7 +207,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
AdjudicatorHelpers.InvalidateIfNotMatching(
order =>
// Map adjacency with respect to province
order.Unit.Location.Adjacents.Any(
world.Map.GetLocation(order.Unit).Adjacents.Any(
adjLocation => adjLocation.Province == order.Province)
// Turn adjacency
&& Math.Abs(order.Unit.Season.Turn - order.Season.Turn) <= 1
@ -366,7 +366,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
if (isDislodged.Outcome == false)
{
// Non-dislodged units continue into the future.
Unit next = order.Unit.Next(order.Unit.Location, future);
Unit next = order.Unit.Next(world.Map.GetLocation(order.Unit), future);
logger.Log(3, "Advancing unit to {0}", next);
createdUnits.Add(next);
}
@ -375,7 +375,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
// Create a retreat for each dislodged unit.
// TODO check valid retreats and disbands
logger.Log(3, "Creating retreat for {0}", order.Unit);
var validRetreats = order.Unit.Location.Adjacents
var validRetreats = world.Map.GetLocation(order.Unit).Adjacents
.Select(loc => (future, loc))
.ToList();
RetreatingUnit retreat = new(order.Unit, validRetreats);
@ -633,7 +633,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator
// If the origin and destination are adjacent, then there is a path.
if (// Map adjacency
decision.Order.Unit.Location.Adjacents.Contains(decision.Order.Location)
world.Map.GetLocation(decision.Order.Unit).Adjacents.Contains(decision.Order.Location)
// Turn adjacency
&& Math.Abs(decision.Order.Unit.Season.Turn - decision.Order.Season.Turn) <= 1
// Timeline adjacency

View File

@ -30,13 +30,13 @@ public static class PathFinder
// also have coasts, and between those coasts there is a path of adjacent sea provinces
// (not coastal) that are occupied by fleets. The move order is valid even if the fleets
// belong to another power or were not given convoy orders; it will simply fail.
IDictionary<(Location location, Season season), Unit> fleets = world.Units
IDictionary<(string location, Season season), Unit> fleets = world.Units
.Where(unit => unit.Type == UnitType.Fleet)
.ToDictionary(unit => (unit.Location, unit.Season));
.ToDictionary(unit => (unit.LocationId, unit.Season));
// Verify that the origin is a coastal province.
if (movingUnit.Location.Type != LocationType.Land) return false;
IEnumerable<Location> originCoasts = movingUnit.Province.Locations
if (world.Map.GetLocation(movingUnit).Type != LocationType.Land) return false;
IEnumerable<Location> originCoasts = world.Map.GetLocation(movingUnit).Province.Locations
.Where(location => location.Type == LocationType.Water);
if (!originCoasts.Any()) return false;
@ -69,7 +69,7 @@ public static class PathFinder
// If not, add this location to the to-visit set if it isn't a coast, has a fleet,
// and hasn't already been visited.
if (!adjLocation.Province.Locations.Any(l => l.Type == LocationType.Land)
&& fleets.ContainsKey((adjLocation, adjSeason))
&& fleets.ContainsKey((adjLocation.Designation, adjSeason))
&& !visited.Contains((adjLocation, adjSeason)))
{
toVisit.Enqueue((adjLocation, adjSeason));

View File

@ -39,6 +39,11 @@ public class Location
public IEnumerable<Location> Adjacents => this.AdjacentList;
private List<Location> AdjacentList { get; set; }
/// <summary>
/// The unique name of this location in the map.
/// </summary>
public string Designation => $"{this.ProvinceName}/{this.Abbreviation}";
public Location(Province province, string name, string abbreviation, LocationType type)
{
this.Province = province;

View File

@ -17,6 +17,8 @@ public class Map
private List<Province> _Provinces { get; }
private Dictionary<string, Location> LocationLookup { get; }
/// <summary>
/// The game powers.
/// </summary>
@ -29,6 +31,10 @@ public class Map
Type = type;
_Provinces = provinces.ToList();
_Powers = powers.ToList();
LocationLookup = Provinces
.SelectMany(province => province.Locations)
.ToDictionary(location => location.Designation);
}
/// <summary>
@ -41,29 +47,27 @@ public class Map
/// 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 = provinces.SingleOrDefault(
p => p!.Name.ToUpperInvariant() == provinceNameUpper
|| p.Abbreviations.Any(a => a.ToUpperInvariant() == provinceNameUpper),
null);
if (foundProvince == null) throw new KeyNotFoundException(
$"Province {provinceName} not found");
return foundProvince;
}
=> provinces.SingleOrDefault(
p => p!.Name.Equals(provinceName, StringComparison.InvariantCultureIgnoreCase)
|| p.Abbreviations.Any(
a => a.Equals(provinceName, StringComparison.InvariantCultureIgnoreCase)),
null)
?? throw new KeyNotFoundException($"Province {provinceName} not found");
/// <summary>
/// Get the location in a province matching a predicate. Throws if there is not exactly one
/// such location.
/// </summary>
private Location GetLocation(string provinceName, Func<Location, bool> predicate)
{
Location? foundLocation = GetProvince(provinceName).Locations.SingleOrDefault(
l => l != null && predicate(l), null);
if (foundLocation == null) throw new KeyNotFoundException(
$"No such location in {provinceName}");
return foundLocation;
}
=> GetProvince(provinceName).Locations.SingleOrDefault(
l => l != null && predicate(l), null)
?? throw new KeyNotFoundException($"No such location in {provinceName}");
public Location GetLocation(string designation)
=> LocationLookup[designation];
public Location GetLocation(Unit unit)
=> GetLocation(unit.LocationId);
/// <summary>
/// Get the sole land location of a province.

View File

@ -17,6 +17,8 @@ public class Unit
/// </summary>
public Location Location { get; }
public string LocationId => Location.Designation;
/// <summary>
/// The province where the unit is.
/// </summary>

View File

@ -328,7 +328,7 @@ public class World
Province province = Map.GetProvince(provinceName);
season ??= RootSeason;
Unit? foundUnit = this.Units.SingleOrDefault(
u => u!.Province == province && u.Season == season,
u => Map.GetLocation(u!).Province == province && u!.Season == season,
null)
?? throw new KeyNotFoundException($"Unit at {province} at {season} not found");
return foundUnit;

View File

@ -179,7 +179,7 @@ public class MovementAdjudicatorTest
// Confirm the unit was created
Assert.That(updated.Units.Count, Is.EqualTo(2));
Unit second = updated.Units.Single(u => u.Past != null);
Assert.That(second.Location, Is.EqualTo(mun.Order.Unit.Location));
Assert.That(second.LocationId, Is.EqualTo(mun.Order.Unit.LocationId));
}
[Test]

View File

@ -39,8 +39,8 @@ class TestCaseBuilderTest
Assert.That(fleetSTP.Power.Name, Is.EqualTo("Russia"), "Unit created with wrong power");
Assert.That(fleetSTP.Type, Is.EqualTo(UnitType.Fleet), "Unit created with wrong type");
Assert.That(
fleetSTP.Location,
Is.EqualTo(setup.World.Map.GetWater("STP", "wc")),
fleetSTP.LocationId,
Is.EqualTo(setup.World.Map.GetWater("STP", "wc").Designation),
"Unit created on wrong coast");
}
@ -127,8 +127,8 @@ class TestCaseBuilderTest
Is.EqualTo(setup.World.Map.GetPower("Germany")),
"Wrong power");
Assert.That(
orderMun.Order.Unit.Location,
Is.EqualTo(setup.World.Map.GetLand("Mun")),
orderMun.Order.Unit.LocationId,
Is.EqualTo(setup.World.Map.GetLand("Mun").Designation),
"Wrong unit");
Assert.That(

View File

@ -31,8 +31,8 @@ public class UnitTests
Assert.That(u2.Season, Is.EqualTo(a1), "Unexpected unit season");
Assert.That(u3.Season, Is.EqualTo(a2), "Unexpected unit season");
Assert.That(u1.Location, Is.EqualTo(Mun), "Unexpected unit location");
Assert.That(u2.Location, Is.EqualTo(Boh), "Unexpected unit location");
Assert.That(u3.Location, Is.EqualTo(Tyr), "Unexpected unit location");
Assert.That(u1.LocationId, Is.EqualTo(Mun.Designation), "Unexpected unit location");
Assert.That(u2.LocationId, Is.EqualTo(Boh.Designation), "Unexpected unit location");
Assert.That(u3.LocationId, Is.EqualTo(Tyr.Designation), "Unexpected unit location");
}
}