diff --git a/MultiversalDiplomacy/Adjudicate/Decision/MovementDecisions.cs b/MultiversalDiplomacy/Adjudicate/Decision/MovementDecisions.cs index b5e56b5..c1cee56 100644 --- a/MultiversalDiplomacy/Adjudicate/Decision/MovementDecisions.cs +++ b/MultiversalDiplomacy/Adjudicate/Decision/MovementDecisions.cs @@ -7,7 +7,7 @@ namespace MultiversalDiplomacy.Adjudicate.Decision; public class MovementDecisions { - public Dictionary IsDislodged { get; } + public Dictionary IsDislodged { get; } public Dictionary HasPath { get; } public Dictionary GivesSupport { get; } public Dictionary<(string, string), HoldStrength> HoldStrength { get; } @@ -113,7 +113,7 @@ public class MovementDecisions && SplitKey(other.Location).province == world.Map.GetLocation(me.Unit).Province.Name; bool IsSupportFor(SupportMoveOrder me, MoveOrder move) - => me.Target == move.Unit + => me.Target.Key == move.Unit.Key && me.Season == move.Season && me.Location.Key == move.Location; @@ -136,7 +136,7 @@ public class MovementDecisions .OfType() .Where(other => IsIncoming(order, other)) .ToList(); - IsDislodged[order.Unit] = new(order, incoming); + IsDislodged[order.Unit.Key] = new(order, incoming); if (order is MoveOrder move) { diff --git a/MultiversalDiplomacy/Adjudicate/MovementPhaseAdjudicator.cs b/MultiversalDiplomacy/Adjudicate/MovementPhaseAdjudicator.cs index 573996a..75bf374 100644 --- a/MultiversalDiplomacy/Adjudicate/MovementPhaseAdjudicator.cs +++ b/MultiversalDiplomacy/Adjudicate/MovementPhaseAdjudicator.cs @@ -163,7 +163,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator // Support-hold orders are invalid if the unit supports itself. AdjudicatorHelpers.InvalidateIfNotMatching( - order => order.Unit != order.Target, + order => order.Unit.Key != order.Target.Key, ValidationReason.NoSelfSupport, ref supportHoldOrders, ref validationResults); @@ -241,13 +241,13 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator // were not addressed by 4.D.1-2 and will be handled according to 4.D.3, i.e. replaced with // hold orders. Note that this happens last, after all other invalidations have been // applied in order to comply with what 4.D.3 specifies about illegal orders. - List duplicateOrderedUnits = unitOrders - .GroupBy(o => o.Unit) + List duplicateOrderedUnits = unitOrders + .GroupBy(o => o.Unit.Key) .Where(orderGroup => orderGroup.Count() > 1) .Select(orderGroup => orderGroup.Key) .ToList(); List duplicateOrders = unitOrders - .Where(o => duplicateOrderedUnits.Contains(o.Unit)) + .Where(o => duplicateOrderedUnits.Contains(o.Unit.Key)) .ToList(); List validOrders = unitOrders.Except(duplicateOrders).ToList(); validationResults = validationResults @@ -259,9 +259,9 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator List allOrderableUnits = world.Units .Where(unit => !world.Timelines.GetFutures(unit.Season).Any()) .ToList(); - HashSet orderedUnits = validOrders.Select(order => order.Unit).ToHashSet(); + HashSet orderedUnits = validOrders.Select(order => order.Unit.Key).ToHashSet(); List unorderedUnits = allOrderableUnits - .Where(unit => !orderedUnits.Contains(unit)) + .Where(unit => !orderedUnits.Contains(unit.Key)) .ToList(); List implicitHolds = unorderedUnits .Select(unit => new HoldOrder(unit.Power, unit)) @@ -308,9 +308,9 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator Dictionary moves = decisions .OfType() .ToDictionary(dm => dm.Order); - Dictionary dislodges = decisions + Dictionary dislodges = decisions .OfType() - .ToDictionary(dm => dm.Order.Unit); + .ToDictionary(dm => dm.Order.Unit.Key); // All moves to a particular season in a single phase result in the same future. Keep a // record of when a future season has been created. @@ -395,7 +395,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator OrderHistory history = newHistory[unitOrder.Unit.Season.Key]; // TODO does this add every order to every season?? history.Orders.Add(unitOrder); - history.IsDislodgedOutcomes[unitOrder.Unit.Key] = dislodges[unitOrder.Unit].Outcome == true; + history.IsDislodgedOutcomes[unitOrder.Unit.Key] = dislodges[unitOrder.Unit.Key].Outcome == true; if (unitOrder is MoveOrder moveOrder) { history.DoesMoveOutcomes[moveOrder.Unit.Key] = moves[moveOrder].Outcome == true; @@ -520,7 +520,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator foreach (UnitOrder order in decision.Orders) { // TODO these aren't timeline-specific - IsDislodged dislodged = decisions.IsDislodged[order.Unit]; + IsDislodged dislodged = decisions.IsDislodged[order.Unit.Key]; progress |= ResolveDecision(dislodged, world, decisions, depth + 1); if (history.IsDislodgedOutcomes.TryGetValue(order.Unit.Key, out bool previous) && dislodged.Resolved @@ -694,7 +694,7 @@ public class MovementPhaseAdjudicator : IPhaseAdjudicator } // Support is also cut if the unit is dislodged. - IsDislodged dislodge = decisions.IsDislodged[decision.Order.Unit]; + IsDislodged dislodge = decisions.IsDislodged[decision.Order.Unit.Key]; progress |= ResolveDecision(dislodge, world, decisions, depth + 1); if (dislodge.Outcome == true) { diff --git a/MultiversalDiplomacy/Adjudicate/PathFinder.cs b/MultiversalDiplomacy/Adjudicate/PathFinder.cs index 34d7a10..50b8868 100644 --- a/MultiversalDiplomacy/Adjudicate/PathFinder.cs +++ b/MultiversalDiplomacy/Adjudicate/PathFinder.cs @@ -12,19 +12,23 @@ 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, order.Target, order.Location, order.Season); + => ConvoyPathExists(world, world.Map.GetLocation(order.Target), order.Location, order.Season); /// /// Determines if a convoy path exists for a move order. /// public static bool ConvoyPathExists(World world, MoveOrder order) - => ConvoyPathExists(world, order.Unit, world.Map.GetLocation(order.Location), order.Season); + => ConvoyPathExists( + world, + world.Map.GetLocation(order.Unit), + world.Map.GetLocation(order.Location), + order.Season); private static bool ConvoyPathExists( World world, - Unit movingUnit, Location unitLocation, - Season unitSeason) + Location destLocation, + Season destSeason) { // A convoy path exists between two locations if both are land locations in provinces that // also have coasts, and between those coasts there is a path of adjacent sea provinces @@ -35,14 +39,14 @@ public static class PathFinder .ToDictionary(unit => (unit.Location, unit.Season)); // Verify that the origin is a coastal province. - if (world.Map.GetLocation(movingUnit).Type != LocationType.Land) return false; - IEnumerable originCoasts = world.Map.GetLocation(movingUnit).Province.Locations + if (unitLocation.Type != LocationType.Land) return false; + IEnumerable originCoasts = unitLocation.Province.Locations .Where(location => location.Type == LocationType.Water); if (!originCoasts.Any()) return false; // Verify that the destination is a coastal province. - if (unitLocation.Type != LocationType.Land) return false; - IEnumerable destCoasts = unitLocation.Province.Locations + if (destLocation.Type != LocationType.Land) return false; + IEnumerable destCoasts = destLocation.Province.Locations .Where(location => location.Type == LocationType.Water); if (!destCoasts.Any()) return false; @@ -50,7 +54,7 @@ public static class PathFinder // locations added to the to-visit set, but the logic will still work with these as // starting points. Queue<(Location location, Season season)> toVisit = new( - originCoasts.Select(location => (location, unitSeason))); + originCoasts.Select(location => (location, destSeason))); HashSet<(Location, Season)> visited = new(); // Begin pathfinding. @@ -64,7 +68,7 @@ public static class PathFinder foreach ((Location adjLocation, Season adjSeason) in adjacents) { // If the destination is adjacent, then a path exists. - if (destCoasts.Contains(adjLocation) && unitSeason == adjSeason) return true; + if (destCoasts.Contains(adjLocation) && destSeason == adjSeason) return true; // 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. diff --git a/MultiversalDiplomacy/Orders/ConvoyOrder.cs b/MultiversalDiplomacy/Orders/ConvoyOrder.cs index fc2a4d1..892823b 100644 --- a/MultiversalDiplomacy/Orders/ConvoyOrder.cs +++ b/MultiversalDiplomacy/Orders/ConvoyOrder.cs @@ -37,6 +37,6 @@ public class ConvoyOrder : UnitOrder public override string ToString() { - return $"{this.Unit} C {this.Target} -> {(this.Province, this.Season).ToShort()}"; + return $"{this.Unit} con {this.Target} -> {(this.Province, this.Season).ToShort()}"; } } diff --git a/MultiversalDiplomacy/Orders/SupportHoldOrder.cs b/MultiversalDiplomacy/Orders/SupportHoldOrder.cs index 7e5ccd0..774fa04 100644 --- a/MultiversalDiplomacy/Orders/SupportHoldOrder.cs +++ b/MultiversalDiplomacy/Orders/SupportHoldOrder.cs @@ -14,6 +14,6 @@ public class SupportHoldOrder : SupportOrder public override string ToString() { - return $"{this.Unit} S {this.Target}"; + return $"{this.Unit} sup {this.Target}"; } } \ No newline at end of file diff --git a/MultiversalDiplomacy/Orders/SupportMoveOrder.cs b/MultiversalDiplomacy/Orders/SupportMoveOrder.cs index fdf1ad9..c265824 100644 --- a/MultiversalDiplomacy/Orders/SupportMoveOrder.cs +++ b/MultiversalDiplomacy/Orders/SupportMoveOrder.cs @@ -36,6 +36,6 @@ public class SupportMoveOrder : SupportOrder public override string ToString() { - return $"{this.Unit} S {this.Target} -> {(this.Province, this.Season).ToShort()}"; + return $"{this.Unit} sup {this.Target} -> {(this.Province, this.Season).ToShort()}"; } } \ No newline at end of file diff --git a/MultiversalDiplomacyTests/MDATC_A.cs b/MultiversalDiplomacyTests/MDATC_A.cs index ee3fac9..4cba62a 100644 --- a/MultiversalDiplomacyTests/MDATC_A.cs +++ b/MultiversalDiplomacyTests/MDATC_A.cs @@ -101,7 +101,7 @@ public class TimeTravelTest world.RetreatingUnits.Count, Is.EqualTo(1), "Expected A Tyr a0 to be in retreat"); - Assert.That(world.RetreatingUnits.First().Unit, Is.EqualTo(tyr0.Order.Unit)); + Assert.That(world.RetreatingUnits.First().Unit.Key, Is.EqualTo(tyr0.Order.Unit.Key)); } [Test] diff --git a/MultiversalDiplomacyTests/MovementAdjudicatorTest.cs b/MultiversalDiplomacyTests/MovementAdjudicatorTest.cs index b7d66ff..1e014e8 100644 --- a/MultiversalDiplomacyTests/MovementAdjudicatorTest.cs +++ b/MultiversalDiplomacyTests/MovementAdjudicatorTest.cs @@ -206,7 +206,7 @@ public class MovementAdjudicatorTest // Confirm the unit was created in the future Unit u2 = updated.GetUnitAt("Mun", s2); Assert.That(updated.Units.Count, Is.EqualTo(2)); - Assert.That(u2, Is.Not.EqualTo(mun1.Order.Unit)); + Assert.That(u2.Key, Is.Not.EqualTo(mun1.Order.Unit.Key)); Assert.That(u2.Past, Is.EqualTo(mun1.Order.Unit.Key)); Assert.That(u2.Season, Is.EqualTo(s2)); @@ -256,7 +256,7 @@ public class MovementAdjudicatorTest // Confirm the unit was created in the future Unit u2 = updated.GetUnitAt("Tyr", s2); Assert.That(updated.Units.Count, Is.EqualTo(2)); - Assert.That(u2, Is.Not.EqualTo(mun1.Order.Unit)); + Assert.That(u2.Key, Is.Not.EqualTo(mun1.Order.Unit.Key)); Assert.That(u2.Past, Is.EqualTo(mun1.Order.Unit.Key)); Assert.That(u2.Season, Is.EqualTo(s2)); diff --git a/MultiversalDiplomacyTests/OrderReference.cs b/MultiversalDiplomacyTests/OrderReference.cs index e28c7c8..f5fff03 100644 --- a/MultiversalDiplomacyTests/OrderReference.cs +++ b/MultiversalDiplomacyTests/OrderReference.cs @@ -58,7 +58,7 @@ public abstract class OrderReference if (this.Order is UnitOrder unitOrder) { var replacementOrder = this.Builder.ValidationResults.Where( - v => v.Order is UnitOrder uo && uo != unitOrder && uo.Unit == unitOrder.Unit); + v => v.Order is UnitOrder uo && uo != unitOrder && uo.Unit.Key == unitOrder.Unit.Key); if (replacementOrder.Any()) { return replacementOrder.Single(); @@ -142,7 +142,7 @@ public abstract class OrderReference if (this.Order is UnitOrder unitOrder) { var retreat = this.Builder.World.RetreatingUnits.Where( - ru => ru.Unit == unitOrder.Unit); + ru => ru.Unit.Key == unitOrder.Unit.Key); if (retreat.Any()) { return retreat.Single(); diff --git a/MultiversalDiplomacyTests/SerializationTest.cs b/MultiversalDiplomacyTests/SerializationTest.cs index b8d87e9..cbadda2 100644 --- a/MultiversalDiplomacyTests/SerializationTest.cs +++ b/MultiversalDiplomacyTests/SerializationTest.cs @@ -94,11 +94,8 @@ public class SerializationTest Assert.That(setup.World.OrderHistory[s0.Key].DoesMoveOutcomes.Count, Is.GreaterThan(0), "Missing moves"); Assert.That(setup.World.OrderHistory[s0.Key].IsDislodgedOutcomes.Count, Is.GreaterThan(0), "Missing dislodges"); - // Assert.Ignore("Serialization doesn't fully work yet"); - // Serialize and deserialize the world string serialized = JsonSerializer.Serialize(setup.World, Options); - Console.WriteLine(serialized); World reserialized = JsonSerializer.Deserialize(serialized, Options) ?? throw new AssertionException("Failed to reserialize world"); @@ -120,16 +117,16 @@ public class SerializationTest setup.ValidateOrders(); Assert.That(mun1, Is.Valid); var adjudications = setup.AdjudicateOrders(); - foreach (var adj in adjudications) - { - Console.WriteLine($"{adj}"); - } - Assert.That(mun1, Is.NotCut); + + AttackStrength mun0attack = adjudications.OfType().Single(); + Assert.That(mun0attack.Supports, Is.Not.Empty, "Support not tracked"); + DoesMove mun0move = adjudications.OfType().Single(move => move.Order.Unit.Key == mun0.Order.Unit.Key); Assert.That(mun0move.Outcome, Is.True); + IsDislodged tyr0dislodge = adjudications.OfType().Single(dis => dis.Order.Unit.Key == tyr0.Order.Unit.Key); - Assert.That(tyr0dislodge, Is.True); + Assert.That(tyr0dislodge.Outcome, Is.True); // Confirm that an alternate future is created. World world = setup.UpdateWorld(); @@ -143,6 +140,6 @@ public class SerializationTest world.RetreatingUnits.Count, Is.EqualTo(1), "Expected A Tyr a0 to be in retreat"); - Assert.That(world.RetreatingUnits.First().Unit, Is.EqualTo(tyr0.Order.Unit)); + Assert.That(world.RetreatingUnits.First().Unit.Key, Is.EqualTo(tyr0.Order.Unit.Key)); } }