|
|
|
@ -307,33 +307,44 @@ public class OrderParser(World world)
|
|
|
|
|
: subject.Season.Turn;
|
|
|
|
|
|
|
|
|
|
var destProvince = world.Map.Provinces.Single(province => province.Is(move.destProvince));
|
|
|
|
|
string? destLocationKey = null;
|
|
|
|
|
|
|
|
|
|
// DATC 4.B.6 requires that "irrelevant" locations like army to Spain nc be ignored.
|
|
|
|
|
// To satisfy this, any location of the wrong type is categorically ignored, so for an army the
|
|
|
|
|
// "north coast" location effectively doesn't exist here.
|
|
|
|
|
// DATC 4.B specifies how to interpret orders with missing or incorrect locations. These issues arise because
|
|
|
|
|
// of provinces with multiple locations of the same type, i.e. two-coast provinces in Classical. In general,
|
|
|
|
|
// DATC's only concern is to disambiguate the order, failing the order only when it is ineluctably ambiguous
|
|
|
|
|
// (4.B.1) or explicitly incorrect (4.B.3). Irrelevant or nonexistent locations can be ignored.
|
|
|
|
|
|
|
|
|
|
// If there is only one possible location for the moving unit, that location is used. The idea of land and
|
|
|
|
|
// water locations is an implementation detail of 5dplomacy and not part of the Diplomacy rules, so they will
|
|
|
|
|
// usually be omitted, and so moving an army to any land province or a fleet to a non-multi-coast province is
|
|
|
|
|
// naturally unambiguous even without the location.
|
|
|
|
|
var unitLocations = destProvince.Locations.Where(loc => loc.Type switch {
|
|
|
|
|
LocationType.Land => subject.Type == UnitType.Army,
|
|
|
|
|
LocationType.Water => subject.Type == UnitType.Fleet,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
// DATC 4.6.B also requires that unknown coasts be ignored. To satisfy this, an additional filter by name.
|
|
|
|
|
// Doing both of these filters means "A - Spain/nc" is as meaningful as "F - Spain/wc".
|
|
|
|
|
var matchingLocations = unitLocations.Where(loc => loc.Is(move.destLocation));
|
|
|
|
|
|
|
|
|
|
// If one location matched, use that location. If the coast is inaccessible to the subject, the order will
|
|
|
|
|
// be invalidated by a path check later to satisfy DATC 4.B.3.
|
|
|
|
|
string? destLocationKey = matchingLocations.FirstOrDefault(defaultValue: null)?.Key;
|
|
|
|
|
if (!unitLocations.Any()) return false; // If *no* locations match, the move is illegal
|
|
|
|
|
if (unitLocations.Count() == 1) destLocationKey ??= unitLocations.Single().Key;
|
|
|
|
|
|
|
|
|
|
// If more than one location is possible for the unit, the order must be disambiguated by the dest location
|
|
|
|
|
// or the physical realities of which coast is accessible. DATC 4.B.3 makes an order illegal if the location
|
|
|
|
|
// is specified but it isn't an accessible coast, so successfully specifying a location takes precedence over
|
|
|
|
|
// there being one accessible coast.
|
|
|
|
|
if (destLocationKey is null) {
|
|
|
|
|
// If no location matched, location was omitted, nonexistent, or the wrong type.
|
|
|
|
|
// If one location is accessible, DATC 4.B.2 requires that it be used.
|
|
|
|
|
// If more than one location is accessible, DATC 4.B.1 requires the order fail.
|
|
|
|
|
|
|
|
|
|
// TODO check which locations are accessible per the above
|
|
|
|
|
destLocationKey = unitLocations.First().Key;
|
|
|
|
|
|
|
|
|
|
// return false;
|
|
|
|
|
var matchingLocations = unitLocations.Where(loc => loc.Is(move.destLocation));
|
|
|
|
|
if (matchingLocations.Any()) destLocationKey ??= matchingLocations.Single().Key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
var accessibleLocations = destProvince.Locations.Where(loc => loc.Adjacents.Contains(source));
|
|
|
|
|
if (accessibleLocations.Count() == 1) destLocationKey ??= accessibleLocations.Single().Key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the order is still ambiguous, fail per DATC 4.B.1.
|
|
|
|
|
if (destLocationKey is null) return false;
|
|
|
|
|
|
|
|
|
|
order = new MoveOrder(power, subject, new(destTimeline, destTurn), destLocationKey);
|
|
|
|
|
return true;
|
|
|
|
@ -392,36 +403,47 @@ public class OrderParser(World world)
|
|
|
|
|
: target.Season.Turn;
|
|
|
|
|
|
|
|
|
|
var destProvince = world.Map.Provinces.Single(province => province.Is(support.destProvince));
|
|
|
|
|
string? destLocationKey = null;
|
|
|
|
|
|
|
|
|
|
// DATC 4.B.6 requires that "irrelevant" locations like army to Spain nc be ignored.
|
|
|
|
|
// To satisfy this, any location of the wrong type is categorically ignored, so for an army the
|
|
|
|
|
// "north coast" location effectively doesn't exist here.
|
|
|
|
|
// Note that target is used instead of subject, since it is possible to support a move to an inaccessible
|
|
|
|
|
// coast as long as the subject can reach the province and the target can reach the location.
|
|
|
|
|
// DATC 4.B specifies how to interpret orders with missing or incorrect locations. These issues arise because
|
|
|
|
|
// of provinces with multiple locations of the same type, i.e. two-coast provinces in Classical. In general,
|
|
|
|
|
// DATC's only concern is to disambiguate the order, failing the order only when it is ineluctably ambiguous
|
|
|
|
|
// (4.B.1) or explicitly incorrect (4.B.3). Irrelevant or nonexistent locations can be ignored.
|
|
|
|
|
|
|
|
|
|
// If there is only one possible location for the moving unit, that location is used. The idea of land and
|
|
|
|
|
// water locations is an implementation detail of 5dplomacy and not part of the Diplomacy rules, so they will
|
|
|
|
|
// usually be omitted, and so moving an army to any land province or a fleet to a non-multi-coast province is
|
|
|
|
|
// naturally unambiguous even without the location.
|
|
|
|
|
var unitLocations = destProvince.Locations.Where(loc => loc.Type switch {
|
|
|
|
|
LocationType.Land => target.Type == UnitType.Army,
|
|
|
|
|
LocationType.Water => target.Type == UnitType.Fleet,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
// DATC 4.6.B also requires that unknown coasts be ignored. To satisfy this, an additional filter by name.
|
|
|
|
|
// Doing both of these filters means "A - Spain/nc" is as meaningful as "F - Spain/wc".
|
|
|
|
|
var matchingLocations = unitLocations.Where(loc => loc.Is(support.destLocation));
|
|
|
|
|
|
|
|
|
|
// If one location matched, use that location. If the coast is inaccessible to the target, the order will
|
|
|
|
|
// be invalidated by a path check later to satisfy DATC 4.B.3.
|
|
|
|
|
string? destLocationKey = matchingLocations.FirstOrDefault(defaultValue: null)?.Key;
|
|
|
|
|
if (!unitLocations.Any()) return false; // If *no* locations match, the move is illegal
|
|
|
|
|
if (unitLocations.Count() == 1) destLocationKey ??= unitLocations.Single().Key;
|
|
|
|
|
|
|
|
|
|
// If more than one location is possible for the unit, the order must be disambiguated by the dest location
|
|
|
|
|
// or the physical realities of which coast is accessible. DATC 4.B.3 makes an order illegal if the location
|
|
|
|
|
// is specified but it isn't an accessible coast, so successfully specifying a location takes precedence over
|
|
|
|
|
// there being one accessible coast.
|
|
|
|
|
if (destLocationKey is null) {
|
|
|
|
|
// If no location matched, location was omitted, nonexistent, or the wrong type.
|
|
|
|
|
// If one location is accessible, DATC 4.B.2 requires that it be used.
|
|
|
|
|
// If more than one location is accessible, DATC 4.B.1 requires the order fail.
|
|
|
|
|
|
|
|
|
|
// TODO check which locations are accessible per the above
|
|
|
|
|
destLocationKey = unitLocations.First().Key;
|
|
|
|
|
|
|
|
|
|
// return false;
|
|
|
|
|
var matchingLocations = unitLocations.Where(loc => loc.Is(support.destLocation));
|
|
|
|
|
if (matchingLocations.Any()) destLocationKey ??= matchingLocations.Single().Key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
var accessibleLocations = destProvince.Locations.Where(loc => loc.Adjacents.Contains(source));
|
|
|
|
|
if (accessibleLocations.Count() == 1) destLocationKey ??= accessibleLocations.Single().Key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the order is still ambiguous, fail per DATC 4.B.1. This also satisfies 4.B.4, which prefers for
|
|
|
|
|
// programmatic adjudicators with order validation to require the coasts instead of interpreting the ambiguous
|
|
|
|
|
// support by referring to the move order it supports.
|
|
|
|
|
if (destLocationKey is null) return false;
|
|
|
|
|
|
|
|
|
|
var destLocation = world.Map.GetLocation(destLocationKey);
|
|
|
|
|
order = new SupportMoveOrder(power, subject, target, new(destTimeline, destTurn), destLocation);
|
|
|
|
|
return true;
|
|
|
|
|