using MultiversalDiplomacy.Model; using MultiversalDiplomacy.Orders; namespace MultiversalDiplomacy.Adjudicate.Decision; public class MovementDecisions { public Dictionary IsDislodged { get; } public Dictionary HasPath { get; } public Dictionary GivesSupport { get; } public Dictionary<(Province, Season), HoldStrength> HoldStrength { get; } public Dictionary AttackStrength { get; } public Dictionary DefendStrength { get; } public Dictionary PreventStrength { get; } public Dictionary DoesMove { get; } public Dictionary AdvanceTimeline { get; } public IEnumerable Values => IsDislodged.Values.Cast() .Concat(HasPath.Values) .Concat(GivesSupport.Values) .Concat(HoldStrength.Values) .Concat(AttackStrength.Values) .Concat(DefendStrength.Values) .Concat(PreventStrength.Values) .Concat(DoesMove.Values) .Concat(AdvanceTimeline.Values); public MovementDecisions(World world, List orders) { IsDislodged = new(); HasPath = new(); GivesSupport = new(); HoldStrength = new(); AttackStrength = new(); DefendStrength = new(); PreventStrength = new(); DoesMove = new(); AdvanceTimeline = new(); // The orders argument only contains the submitted orders. The adjudicator will need to adjudicate not only // presently submitted orders, but also previously submitted orders if present orders affect the past. This // necessitates doing some lookups to find all affected seasons. // At a minimum, the submitted orders imply a dislodge decision for each unit, which affects every season those // orders were given to. var submittedOrdersBySeason = orders.Cast().ToLookup(order => order.Unit.Season); foreach (var group in submittedOrdersBySeason) { AdvanceTimeline[group.Key] = new(group.Key, group); } // Create timeline decisions for each season potentially affected by the submitted orders. // Since adjudication is deterministic and pure, if none of the affecting orders succeed, // the adjudication decisions for the extra seasons will resolve the same way and the // advance decision for the timeline will resolve false. foreach (Order order in orders) { switch (order) { case MoveOrder move: AdvanceTimeline.Ensure( move.Season, () => new(move.Season, world.OrderHistory[move.Season].Orders)); AdvanceTimeline[move.Season].Orders.Add(move); break; case SupportHoldOrder supportHold: AdvanceTimeline.Ensure( supportHold.Target.Season, () => new(supportHold.Target.Season, world.OrderHistory[supportHold.Target.Season].Orders)); AdvanceTimeline[supportHold.Target.Season].Orders.Add(supportHold); break; case SupportMoveOrder supportMove: AdvanceTimeline.Ensure( supportMove.Target.Season, () => new(supportMove.Target.Season, world.OrderHistory[supportMove.Target.Season].Orders)); AdvanceTimeline[supportMove.Target.Season].Orders.Add(supportMove); AdvanceTimeline.Ensure( supportMove.Season, () => new(supportMove.Season, world.OrderHistory[supportMove.Season].Orders)); AdvanceTimeline[supportMove.Season].Orders.Add(supportMove); break; } } // Get the orders in the affected timelines. List relevantOrders = AdvanceTimeline.Values.SelectMany(at => at.Orders).ToList(); // Create a hold strength decision with an associated order for every province with a unit. foreach (UnitOrder order in relevantOrders) { HoldStrength[order.Unit.Point] = new(order.Unit.Point, order); } // Create all other relevant decisions for each order in the affected timelines. foreach (UnitOrder order in relevantOrders) { // Create a dislodge decision for this unit. List incoming = relevantOrders .OfType() .Where(order.IsIncoming) .ToList(); IsDislodged[order.Unit] = new(order, incoming); if (order is MoveOrder move) { // Find supports corresponding to this move. List supports = relevantOrders .OfType() .Where(support => support.IsSupportFor(move)) .ToList(); // Determine if this move is a head-to-head battle. MoveOrder? opposingMove = relevantOrders .OfType() .FirstOrDefault(other => other!.IsOpposing(move), null); // Find competing moves. List competing = relevantOrders .OfType() .Where(move.IsCompeting) .ToList(); // Create the move-related decisions. HasPath[move] = new(move); AttackStrength[move] = new(move, supports, opposingMove); DefendStrength[move] = new(move, supports); PreventStrength[move] = new(move, supports, opposingMove); DoesMove[move] = new(move, opposingMove, competing); // Ensure a hold strength decision exists for the destination. HoldStrength.Ensure(move.Point, () => new(move.Point)); } else if (order is SupportOrder support) { // Create the support decision. GivesSupport[support] = new(support, incoming); // Ensure a hold strength decision exists for the target's province. HoldStrength.Ensure(support.Target.Point, () => new(support.Target.Point)); if (support is SupportHoldOrder supportHold) { HoldStrength[support.Target.Point].Supports.Add(supportHold); } else if (support is SupportMoveOrder supportMove) { // Ensure a hold strength decision exists for the target's destination. HoldStrength.Ensure(supportMove.Point, () => new(supportMove.Point)); } } } } }