2022-03-27 21:36:49 +00:00
|
|
|
using MultiversalDiplomacy.Model;
|
|
|
|
using MultiversalDiplomacy.Orders;
|
|
|
|
|
|
|
|
namespace MultiversalDiplomacy.Adjudicate.Decision;
|
|
|
|
|
|
|
|
public class MovementDecisions
|
|
|
|
{
|
|
|
|
public Dictionary<Unit, IsDislodged> IsDislodged { get; }
|
|
|
|
public Dictionary<MoveOrder, HasPath> HasPath { get; }
|
|
|
|
public Dictionary<SupportOrder, GivesSupport> GivesSupport { get; }
|
2024-08-15 05:40:40 +00:00
|
|
|
public Dictionary<(Province, string), HoldStrength> HoldStrength { get; }
|
2022-03-27 21:36:49 +00:00
|
|
|
public Dictionary<MoveOrder, AttackStrength> AttackStrength { get; }
|
|
|
|
public Dictionary<MoveOrder, DefendStrength> DefendStrength { get; }
|
|
|
|
public Dictionary<MoveOrder, PreventStrength> PreventStrength { get; }
|
|
|
|
public Dictionary<MoveOrder, DoesMove> DoesMove { get; }
|
2024-08-15 05:32:48 +00:00
|
|
|
public Dictionary<string, AdvanceTimeline> AdvanceTimeline { get; }
|
2022-03-27 21:36:49 +00:00
|
|
|
|
|
|
|
public IEnumerable<AdjudicationDecision> Values =>
|
2022-11-09 00:25:47 +00:00
|
|
|
IsDislodged.Values.Cast<AdjudicationDecision>()
|
|
|
|
.Concat(HasPath.Values)
|
|
|
|
.Concat(GivesSupport.Values)
|
|
|
|
.Concat(HoldStrength.Values)
|
|
|
|
.Concat(AttackStrength.Values)
|
|
|
|
.Concat(DefendStrength.Values)
|
|
|
|
.Concat(PreventStrength.Values)
|
|
|
|
.Concat(DoesMove.Values)
|
|
|
|
.Concat(AdvanceTimeline.Values);
|
2022-03-27 21:36:49 +00:00
|
|
|
|
2022-03-30 03:40:19 +00:00
|
|
|
public MovementDecisions(World world, List<Order> orders)
|
2022-03-27 21:36:49 +00:00
|
|
|
{
|
2022-11-09 00:25:47 +00:00
|
|
|
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<UnitOrder>().ToLookup(order => order.Unit.Season);
|
|
|
|
foreach (var group in submittedOrdersBySeason)
|
2022-03-30 03:40:19 +00:00
|
|
|
{
|
2024-08-15 13:52:08 +00:00
|
|
|
AdvanceTimeline[group.Key.Key] = new(group.Key, group);
|
2022-03-30 03:40:19 +00:00
|
|
|
}
|
|
|
|
|
2022-11-09 00:25:47 +00:00
|
|
|
// 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.
|
2022-03-30 03:40:19 +00:00
|
|
|
foreach (Order order in orders)
|
|
|
|
{
|
|
|
|
switch (order)
|
|
|
|
{
|
|
|
|
case MoveOrder move:
|
2022-11-09 00:25:47 +00:00
|
|
|
AdvanceTimeline.Ensure(
|
2024-08-15 05:32:48 +00:00
|
|
|
move.Season,
|
2024-08-15 05:28:56 +00:00
|
|
|
() => new(world.Seasons[move.Season], world.OrderHistory[move.Season].Orders));
|
2024-08-15 05:32:48 +00:00
|
|
|
AdvanceTimeline[move.Season].Orders.Add(move);
|
2022-03-30 03:40:19 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SupportHoldOrder supportHold:
|
2022-11-09 00:25:47 +00:00
|
|
|
AdvanceTimeline.Ensure(
|
2024-08-15 13:52:08 +00:00
|
|
|
supportHold.Target.Season.Key,
|
|
|
|
() => new(supportHold.Target.Season, world.OrderHistory[supportHold.Target.Season.Key].Orders));
|
|
|
|
AdvanceTimeline[supportHold.Target.Season.Key].Orders.Add(supportHold);
|
2022-03-30 03:40:19 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SupportMoveOrder supportMove:
|
2022-11-09 00:25:47 +00:00
|
|
|
AdvanceTimeline.Ensure(
|
2024-08-15 13:52:08 +00:00
|
|
|
supportMove.Target.Season.Key,
|
|
|
|
() => new(supportMove.Target.Season, world.OrderHistory[supportMove.Target.Season.Key].Orders));
|
|
|
|
AdvanceTimeline[supportMove.Target.Season.Key].Orders.Add(supportMove);
|
2022-11-09 00:25:47 +00:00
|
|
|
AdvanceTimeline.Ensure(
|
2024-08-15 13:52:08 +00:00
|
|
|
supportMove.Season.Key,
|
|
|
|
() => new(supportMove.Season, world.OrderHistory[supportMove.Season.Key].Orders));
|
|
|
|
AdvanceTimeline[supportMove.Season.Key].Orders.Add(supportMove);
|
2022-03-30 03:40:19 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-11-09 00:25:47 +00:00
|
|
|
|
|
|
|
// Get the orders in the affected timelines.
|
2022-11-09 02:55:27 +00:00
|
|
|
List<UnitOrder> relevantOrders = AdvanceTimeline.Values
|
|
|
|
.SelectMany(at => at.Orders)
|
|
|
|
.Distinct()
|
|
|
|
.ToList();
|
2022-11-09 00:25:47 +00:00
|
|
|
|
2024-08-15 05:40:40 +00:00
|
|
|
(Province province, string season) UnitPoint(Unit unit)
|
2024-08-15 13:52:08 +00:00
|
|
|
=> (world.Map.GetLocation(unit.Location).Province, unit.Season.Key);
|
2024-08-15 05:40:40 +00:00
|
|
|
(Province province, string season) MovePoint(MoveOrder move)
|
|
|
|
=> (move.Province, move.Season);
|
2024-08-14 15:15:10 +00:00
|
|
|
|
2022-11-09 00:25:47 +00:00
|
|
|
// Create a hold strength decision with an associated order for every province with a unit.
|
|
|
|
foreach (UnitOrder order in relevantOrders)
|
2022-03-30 03:40:19 +00:00
|
|
|
{
|
2024-08-15 05:40:40 +00:00
|
|
|
HoldStrength[UnitPoint(order.Unit)] = new(
|
|
|
|
world.Map.GetLocation(order.Unit.Location).Province,
|
|
|
|
order.Unit.Season,
|
|
|
|
order);
|
2022-03-30 03:40:19 +00:00
|
|
|
}
|
|
|
|
|
2024-08-14 15:15:10 +00:00
|
|
|
bool IsIncoming(UnitOrder me, MoveOrder other)
|
|
|
|
=> me != other
|
2024-08-15 05:28:56 +00:00
|
|
|
&& world.Seasons[other.Season] == me.Unit.Season
|
2024-08-14 15:15:10 +00:00
|
|
|
&& other.Province == world.Map.GetLocation(me.Unit).Province;
|
|
|
|
|
|
|
|
bool AreOpposing(MoveOrder one, MoveOrder two)
|
2024-08-15 13:52:08 +00:00
|
|
|
=> one.Season == two.Unit.Season.Key
|
|
|
|
&& two.Season == one.Unit.Season.Key
|
2024-08-14 15:15:10 +00:00
|
|
|
&& one.Province == world.Map.GetLocation(two.Unit).Province
|
|
|
|
&& two.Province == world.Map.GetLocation(one.Unit).Province;
|
|
|
|
|
2022-11-09 00:25:47 +00:00
|
|
|
// Create all other relevant decisions for each order in the affected timelines.
|
|
|
|
foreach (UnitOrder order in relevantOrders)
|
2022-03-27 21:36:49 +00:00
|
|
|
{
|
|
|
|
// Create a dislodge decision for this unit.
|
2022-11-09 00:25:47 +00:00
|
|
|
List<MoveOrder> incoming = relevantOrders
|
2022-03-27 21:36:49 +00:00
|
|
|
.OfType<MoveOrder>()
|
2024-08-14 15:15:10 +00:00
|
|
|
.Where(other => IsIncoming(order, other))
|
2022-03-27 21:36:49 +00:00
|
|
|
.ToList();
|
2022-11-09 00:25:47 +00:00
|
|
|
IsDislodged[order.Unit] = new(order, incoming);
|
2022-03-27 21:36:49 +00:00
|
|
|
|
|
|
|
if (order is MoveOrder move)
|
|
|
|
{
|
|
|
|
// Find supports corresponding to this move.
|
2022-11-09 00:25:47 +00:00
|
|
|
List<SupportMoveOrder> supports = relevantOrders
|
2022-03-27 21:36:49 +00:00
|
|
|
.OfType<SupportMoveOrder>()
|
|
|
|
.Where(support => support.IsSupportFor(move))
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
// Determine if this move is a head-to-head battle.
|
2022-11-09 00:25:47 +00:00
|
|
|
MoveOrder? opposingMove = relevantOrders
|
2022-03-27 21:36:49 +00:00
|
|
|
.OfType<MoveOrder>()
|
2024-08-14 15:15:10 +00:00
|
|
|
.FirstOrDefault(other => AreOpposing(move, other!), null);
|
2022-03-27 21:36:49 +00:00
|
|
|
|
|
|
|
// Find competing moves.
|
2022-11-09 00:25:47 +00:00
|
|
|
List<MoveOrder> competing = relevantOrders
|
2022-03-27 21:36:49 +00:00
|
|
|
.OfType<MoveOrder>()
|
2022-03-30 20:00:51 +00:00
|
|
|
.Where(move.IsCompeting)
|
2022-03-27 21:36:49 +00:00
|
|
|
.ToList();
|
|
|
|
|
|
|
|
// Create the move-related decisions.
|
2022-11-09 00:25:47 +00:00
|
|
|
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);
|
2022-03-27 21:36:49 +00:00
|
|
|
|
|
|
|
// Ensure a hold strength decision exists for the destination.
|
2024-08-15 05:40:40 +00:00
|
|
|
HoldStrength.Ensure(MovePoint(move), () => new(move.Province, world.Seasons[move.Season]));
|
2022-03-27 21:36:49 +00:00
|
|
|
}
|
|
|
|
else if (order is SupportOrder support)
|
|
|
|
{
|
|
|
|
// Create the support decision.
|
2022-11-09 00:25:47 +00:00
|
|
|
GivesSupport[support] = new(support, incoming);
|
2022-03-27 21:36:49 +00:00
|
|
|
|
|
|
|
// Ensure a hold strength decision exists for the target's province.
|
2024-08-15 05:40:40 +00:00
|
|
|
HoldStrength.Ensure(UnitPoint(support.Target), () => new(
|
|
|
|
world.Map.GetLocation(support.Target.Location).Province,
|
|
|
|
support.Target.Season));
|
2022-03-27 21:36:49 +00:00
|
|
|
|
|
|
|
if (support is SupportHoldOrder supportHold)
|
|
|
|
{
|
2024-08-15 05:28:56 +00:00
|
|
|
HoldStrength[UnitPoint(support.Target)].Supports.Add(supportHold);
|
2022-03-27 21:36:49 +00:00
|
|
|
}
|
|
|
|
else if (support is SupportMoveOrder supportMove)
|
|
|
|
{
|
|
|
|
// Ensure a hold strength decision exists for the target's destination.
|
2024-08-15 05:40:40 +00:00
|
|
|
HoldStrength.Ensure(
|
2024-08-15 13:52:08 +00:00
|
|
|
(supportMove.Province, supportMove.Season.Key),
|
2024-08-15 05:40:40 +00:00
|
|
|
() => new(supportMove.Province, supportMove.Season));
|
2022-03-27 21:36:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|