diff --git a/MultiversalDiplomacy/Adjudicate/Decision/MovementDecisions.cs b/MultiversalDiplomacy/Adjudicate/Decision/MovementDecisions.cs index c1cee56..ef0b1e2 100644 --- a/MultiversalDiplomacy/Adjudicate/Decision/MovementDecisions.cs +++ b/MultiversalDiplomacy/Adjudicate/Decision/MovementDecisions.cs @@ -94,7 +94,7 @@ public class MovementDecisions .ToList(); (string province, string season) UnitPoint(Unit unit) - => (world.Map.GetLocation(unit.Location).Province.Name, unit.Season.Key); + => (unit.GetProvince(world).Name, unit.Season.Key); (string province, string season) MovePoint(MoveOrder move) => (SplitKey(move.Location).province, move.Season.Key); @@ -102,7 +102,7 @@ public class MovementDecisions foreach (UnitOrder order in relevantOrders) { HoldStrength[UnitPoint(order.Unit)] = new( - world.Map.GetLocation(order.Unit.Location).Province, + order.Unit.GetProvince(world), order.Unit.Season, order); } @@ -110,7 +110,7 @@ public class MovementDecisions bool IsIncoming(UnitOrder me, MoveOrder other) => me != other && other.Season == me.Unit.Season - && SplitKey(other.Location).province == world.Map.GetLocation(me.Unit).Province.Name; + && SplitKey(other.Location).province == me.Unit.GetProvince(world).Name; bool IsSupportFor(SupportMoveOrder me, MoveOrder move) => me.Target.Key == move.Unit.Key @@ -120,8 +120,8 @@ public class MovementDecisions bool AreOpposing(MoveOrder one, MoveOrder two) => one.Season == two.Unit.Season && two.Season == one.Unit.Season - && SplitKey(one.Location).province == world.Map.GetLocation(two.Unit).Province.Name - && SplitKey(two.Location).province == world.Map.GetLocation(one.Unit).Province.Name; + && SplitKey(one.Location).province == two.Unit.GetProvince(world).Name + && SplitKey(two.Location).province == one.Unit.GetProvince(world).Name; bool AreCompeting(MoveOrder one, MoveOrder two) => one != two @@ -174,7 +174,7 @@ public class MovementDecisions // Ensure a hold strength decision exists for the target's province. HoldStrength.Ensure(UnitPoint(support.Target), () => new( - world.Map.GetLocation(support.Target.Location).Province, + support.Target.GetProvince(world), support.Target.Season)); if (support is SupportHoldOrder supportHold) diff --git a/MultiversalDiplomacy/Adjudicate/MovementPhaseAdjudicator.cs b/MultiversalDiplomacy/Adjudicate/MovementPhaseAdjudicator.cs index 75bf374..4b8b667 100644 --- a/MultiversalDiplomacy/Adjudicate/MovementPhaseAdjudicator.cs +++ b/MultiversalDiplomacy/Adjudicate/MovementPhaseAdjudicator.cs @@ -92,7 +92,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator ILookup moveOrdersByAdjacency = moveOrders .ToLookup(order => // Map adjacency - world.Map.GetLocation(order.Unit).Adjacents.Select(loc => loc.Key).Contains(order.Location) + order.Unit.GetLocation(world).Adjacents.Select(loc => loc.Key).Contains(order.Location) // Turn adjacency && Math.Abs(order.Unit.Season.Turn - order.Season.Turn) <= 1 // Timeline adjacency @@ -177,8 +177,8 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator AdjudicatorHelpers.InvalidateIfNotMatching( order => // Map adjacency with respect to province - world.Map.GetLocation(order.Unit).Adjacents.Any( - adjLocation => adjLocation.Province == world.Map.GetLocation(order.Target).Province) + order.Unit.GetLocation(world).Adjacents.Any( + adjLocation => adjLocation.Province == order.Target.GetLocation(world).Province) // Turn adjacency && Math.Abs(order.Unit.Season.Turn - order.Target.Season.Turn) <= 1 // Timeline adjacency @@ -197,7 +197,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 => world.Map.GetLocation(order.Unit).Province != order.Province, + order => order.Unit.GetProvince(world) != order.Province, ValidationReason.NoSupportMoveAgainstSelf, ref supportMoveOrders, ref validationResults); @@ -209,7 +209,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator AdjudicatorHelpers.InvalidateIfNotMatching( order => // Map adjacency with respect to province - world.Map.GetLocation(order.Unit).Adjacents.Any( + order.Unit.GetLocation(world).Adjacents.Any( adjLocation => adjLocation.Province == order.Province) // Turn adjacency && Math.Abs(order.Unit.Season.Turn - order.Season.Turn) <= 1 @@ -370,7 +370,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator if (isDislodged.Outcome == false) { // Non-dislodged units continue into the future. - Unit next = order.Unit.Next(world.Map.GetLocation(order.Unit).Key, future); + Unit next = order.Unit.Next(order.Unit.GetLocation(world).Key, future); logger.Log(3, "Advancing unit to {0}", next); createdUnits.Add(next); } @@ -379,7 +379,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 = world.Map.GetLocation(order.Unit).Adjacents + var validRetreats = order.Unit.GetLocation(world).Adjacents .Select(loc => (future, loc)) .ToList(); RetreatingUnit retreat = new(order.Unit, validRetreats); @@ -640,7 +640,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator // If the origin and destination are adjacent, then there is a path. if (// Map adjacency - world.Map.GetLocation(decision.Order.Unit).Adjacents.Select(loc => loc.Key).Contains(decision.Order.Location) + decision.Order.Unit.GetLocation(world).Adjacents.Select(loc => loc.Key).Contains(decision.Order.Location) // Turn adjacency && Math.Abs(decision.Order.Unit.Season.Turn - decision.Order.Season.Turn) <= 1 // Timeline adjacency diff --git a/MultiversalDiplomacy/Adjudicate/PathFinder.cs b/MultiversalDiplomacy/Adjudicate/PathFinder.cs index 50b8868..e64d5e2 100644 --- a/MultiversalDiplomacy/Adjudicate/PathFinder.cs +++ b/MultiversalDiplomacy/Adjudicate/PathFinder.cs @@ -12,7 +12,7 @@ public static class PathFinder /// Determines if a convoy path exists for a move in a convoy order. /// public static bool ConvoyPathExists(World world, ConvoyOrder order) - => ConvoyPathExists(world, world.Map.GetLocation(order.Target), order.Location, order.Season); + => ConvoyPathExists(world, order.Target.GetLocation(world), order.Location, order.Season); /// /// Determines if a convoy path exists for a move order. @@ -20,7 +20,7 @@ public static class PathFinder public static bool ConvoyPathExists(World world, MoveOrder order) => ConvoyPathExists( world, - world.Map.GetLocation(order.Unit), + order.Unit.GetLocation(world), world.Map.GetLocation(order.Location), order.Season); diff --git a/MultiversalDiplomacy/Model/Map.cs b/MultiversalDiplomacy/Model/Map.cs index d1f76a0..3fd0277 100644 --- a/MultiversalDiplomacy/Model/Map.cs +++ b/MultiversalDiplomacy/Model/Map.cs @@ -72,9 +72,6 @@ public class Map public Location GetLocation(string designation) => LocationLookup[designation]; - public Location GetLocation(Unit unit) - => GetLocation(unit.Location); - /// /// Get the sole land location of a province. /// diff --git a/MultiversalDiplomacy/Model/OrderParser.cs b/MultiversalDiplomacy/Model/OrderParser.cs index b8c223d..6a9e01b 100644 --- a/MultiversalDiplomacy/Model/OrderParser.cs +++ b/MultiversalDiplomacy/Model/OrderParser.cs @@ -259,7 +259,7 @@ public class OrderParser(World world) // and the location is ignored. This also satisfies DATC 4.B.5, which requires that a wrong coast for the // subject be ignored. subject = world.Units.FirstOrDefault(unit - => world.Map.GetLocation(unit!.Location).ProvinceName == province.Name + => unit!.GetProvince(world).Name == province.Name && unit!.Season.Timeline == timeline && unit!.Season.Turn == turn, null); @@ -338,7 +338,7 @@ public class OrderParser(World world) // If the order location didn't disambiguate the coasts, either because it's missing or it's nonsense, the // order can be disambiguated by there being one accessible coast from the order source. if (destLocationKey is null) { - Location source = world.Map.GetLocation(subject.Location); + Location source = subject.GetLocation(world); var accessibleLocations = destProvince.Locations.Where(loc => loc.Adjacents.Contains(source)); if (accessibleLocations.Count() == 1) destLocationKey ??= accessibleLocations.Single().Key; } @@ -434,7 +434,7 @@ public class OrderParser(World world) // If the order location didn't disambiguate the coasts, either because it's missing or it's nonsense, the // order can be disambiguated by there being one accessible coast from the order source. if (destLocationKey is null) { - Location source = world.Map.GetLocation(target.Location); + Location source = target.GetLocation(world); var accessibleLocations = destProvince.Locations.Where(loc => loc.Adjacents.Contains(source)); if (accessibleLocations.Count() == 1) destLocationKey ??= accessibleLocations.Single().Key; } diff --git a/MultiversalDiplomacy/Model/Unit.cs b/MultiversalDiplomacy/Model/Unit.cs index cba5d0c..305d39f 100644 --- a/MultiversalDiplomacy/Model/Unit.cs +++ b/MultiversalDiplomacy/Model/Unit.cs @@ -62,4 +62,8 @@ public class Unit /// public Unit Next(string location, Season season) => new(past: this.Key, location, season, this.Power, this.Type); + + public Location GetLocation(World world) => world.Map.GetLocation(Location); + + public Province GetProvince(World world) => GetLocation(world).Province; } diff --git a/MultiversalDiplomacy/Model/World.cs b/MultiversalDiplomacy/Model/World.cs index 4aa6b9f..db4d3e9 100644 --- a/MultiversalDiplomacy/Model/World.cs +++ b/MultiversalDiplomacy/Model/World.cs @@ -231,7 +231,7 @@ public class World Province province = Map.GetProvince(provinceName); season ??= Season.First; Unit? foundUnit = this.Units.SingleOrDefault( - u => Map.GetLocation(u!).Province == province && u!.Season == season, + u => u!.GetProvince(this) == province && u!.Season == season, null) ?? throw new KeyNotFoundException($"Unit at {province} at {season} not found"); return foundUnit; diff --git a/MultiversalDiplomacy/Script/AdjudicationQueryScriptHandler.cs b/MultiversalDiplomacy/Script/AdjudicationQueryScriptHandler.cs index 5128a64..43ca9e3 100644 --- a/MultiversalDiplomacy/Script/AdjudicationQueryScriptHandler.cs +++ b/MultiversalDiplomacy/Script/AdjudicationQueryScriptHandler.cs @@ -100,7 +100,7 @@ public class AdjudicationQueryScriptHandler( => val.Valid && val.Order is HoldOrder hold && hold.Unit.Season == season - && World.Map.GetLocation(hold.Unit.Location).ProvinceName == province.Name); + && hold.Unit.GetProvince(World).Name == province.Name); if (!matchingHolds.Any()) return ScriptResult.Fail("No matching holds"); return ScriptResult.Succeed(this); @@ -129,7 +129,7 @@ public class AdjudicationQueryScriptHandler( var matching = Validations.Where(val => val.Order is UnitOrder order && order.Unit.Season == season - && World.Map.GetLocation(order.Unit.Location).ProvinceName == province.Name); + && order.Unit.GetProvince(World).Name == province.Name); if (!matching.Any()) return ScriptResult.Fail("No matching validations"); if (args[0] == "order-valid" && !matching.First().Valid) { @@ -181,7 +181,7 @@ public class AdjudicationQueryScriptHandler( var matchingDislodges = Adjudications.Where(adj => adj is IsDislodged dislodge && dislodge.Order.Unit.Season == season - && World.Map.GetLocation(dislodge.Order.Unit.Location).ProvinceName == province.Name); + && dislodge.Order.Unit.GetProvince(World).Name == province.Name); if (!matchingDislodges.Any()) return ScriptResult.Fail("No matching dislodge decisions"); var isDislodged = matchingDislodges.Cast().First(); @@ -219,7 +219,7 @@ public class AdjudicationQueryScriptHandler( var matchingMoves = Adjudications.Where(adj => adj is DoesMove moves && moves.Order.Unit.Season == season - && World.Map.GetLocation(moves.Order.Unit.Location).ProvinceName == province.Name); + && moves.Order.Unit.GetProvince(World).Name == province.Name); if (!matchingMoves.Any()) return ScriptResult.Fail("No matching movement decisions"); var doesMove = matchingMoves.Cast().First(); @@ -257,7 +257,7 @@ public class AdjudicationQueryScriptHandler( var matchingSupports = Adjudications.Where(adj => adj is GivesSupport sup && sup.Order.Unit.Season == season - && World.Map.GetLocation(sup.Order.Unit.Location).ProvinceName == province.Name); + && sup.Order.Unit.GetProvince(World).Name == province.Name); if (!matchingSupports.Any()) return ScriptResult.Fail("No matching support decisions"); var supports = matchingSupports.Cast().First(); diff --git a/MultiversalDiplomacyTests/OrderReference.cs b/MultiversalDiplomacyTests/OrderReference.cs index f5fff03..e058cfc 100644 --- a/MultiversalDiplomacyTests/OrderReference.cs +++ b/MultiversalDiplomacyTests/OrderReference.cs @@ -108,7 +108,7 @@ public abstract class OrderReference DefendStrength defend => defend.Order == this.Order, PreventStrength prevent => prevent.Order == this.Order, HoldStrength hold => this.Order is UnitOrder unitOrder - && hold.Province == Builder.World.Map.GetLocation(unitOrder.Unit).Province, + && hold.Province == unitOrder.Unit.GetProvince(Builder.World), _ => false, }).ToList(); return adjudications; diff --git a/MultiversalDiplomacyTests/TestCaseBuilder.cs b/MultiversalDiplomacyTests/TestCaseBuilder.cs index c6148d2..d0c0208 100644 --- a/MultiversalDiplomacyTests/TestCaseBuilder.cs +++ b/MultiversalDiplomacyTests/TestCaseBuilder.cs @@ -263,7 +263,7 @@ public class TestCaseBuilder foreach (Unit unit in this.World.Units) { if (unit.Power == power - && World.Map.GetLocation(unit).Province == location.Province + && unit.GetProvince(World) == location.Province && unit.Season == season) { return unit; diff --git a/MultiversalDiplomacyTests/TestCaseBuilderTest.cs b/MultiversalDiplomacyTests/TestCaseBuilderTest.cs index d653cf1..c7b93f5 100644 --- a/MultiversalDiplomacyTests/TestCaseBuilderTest.cs +++ b/MultiversalDiplomacyTests/TestCaseBuilderTest.cs @@ -68,7 +68,7 @@ class TestCaseBuilderTest List orders = setup.Orders.OfType().ToList(); Func OrderForProvince(string name) - => order => setup.World.Map.GetLocation(order.Unit).Province.Name == name; + => order => order.Unit.GetProvince(setup.World).Name == name; UnitOrder orderBer = orders.Single(OrderForProvince("Berlin")); Assert.That(orderBer, Is.InstanceOf(), "Unexpected order type");