Improve prioritization of garrison targeting.

Garrison groups should be preferred with the following priority:

1. Groups blocking base capture
2. Groups at bases connected to an active front line
3. Rear guard units

Previously they were being prioritized based on the distance to the
closest friendy control point, which is similar to this but an
aggressively placed carrier could throw it off.
This commit is contained in:
Dan Albert 2021-07-12 16:59:49 -07:00
parent cd558daf5a
commit 7e4390d743
5 changed files with 70 additions and 14 deletions

View File

@ -0,0 +1,63 @@
from __future__ import annotations
from collections import Iterator
from dataclasses import dataclass
from game.theater import ConflictTheater
from game.theater.theatergroundobject import VehicleGroupGroundObject
from game.utils import meters, nautical_miles
@dataclass
class Garrisons:
blocking_capture: list[VehicleGroupGroundObject]
defending_front_line: list[VehicleGroupGroundObject]
reserves: list[VehicleGroupGroundObject]
@property
def in_priority_order(self) -> Iterator[VehicleGroupGroundObject]:
yield from self.blocking_capture
yield from self.defending_front_line
yield from self.reserves
def eliminate(self, garrison: VehicleGroupGroundObject) -> None:
if garrison in self.blocking_capture:
self.blocking_capture.remove(garrison)
if garrison in self.defending_front_line:
self.defending_front_line.remove(garrison)
if garrison in self.reserves:
self.reserves.remove(garrison)
def __contains__(self, item: VehicleGroupGroundObject) -> bool:
return item in self.in_priority_order
@classmethod
def from_theater(cls, theater: ConflictTheater, player_owned: bool) -> Garrisons:
"""Categorize garrison groups based on target priority.
Any garrisons blocking base capture are the highest priority, followed by other
garrisons at front-line bases, and finally any garrisons in reserve at other
bases.
"""
blocking = []
defending = []
reserves = []
for cp in theater.control_points_for(player_owned):
garrisons = [
tgo
for tgo in cp.ground_objects
if isinstance(tgo, VehicleGroupGroundObject)
]
if not cp.has_active_frontline:
reserves.extend(garrisons)
continue
for garrison in garrisons:
# Not sure what distance DCS uses, but assuming it's about 2NM since
# that's roughly the distance of the circle on the map.
if meters(garrison.distance_to(cp)) < nautical_miles(2):
blocking.append(garrison)
else:
defending.append(garrison)
return Garrisons(blocking, defending, reserves)

View File

@ -65,14 +65,6 @@ class ObjectiveFinder:
yield ground_object
def threatening_vehicle_groups(self) -> Iterator[VehicleGroupGroundObject]:
"""Iterates over enemy vehicle groups near friendly control points.
Groups are sorted by their closest proximity to any friendly control
point (airfield or fleet).
"""
return self._targets_by_range(self.enemy_vehicle_groups())
def enemy_ships(self) -> Iterator[NavalGroundObject]:
for cp in self.enemy_control_points():
for ground_object in cp.ground_objects:

View File

@ -7,5 +7,5 @@ from game.htn import CompoundTask, Method
class AttackGarrisons(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
for garrison in state.enemy_garrisons:
for garrison in state.enemy_garrisons.in_priority_order:
yield [PlanBai(garrison)]

View File

@ -17,7 +17,7 @@ class PlanBai(PackagePlanningTask[VehicleGroupGroundObject]):
return self.target_area_preconditions_met(state)
def apply_effects(self, state: TheaterState) -> None:
state.enemy_garrisons.remove(self.target)
state.enemy_garrisons.eliminate(self.target)
def propose_flights(self, doctrine: Doctrine) -> None:
self.propose_flight(FlightType.BAI, 2, doctrine.mission_ranges.offensive)

View File

@ -1,16 +1,17 @@
from __future__ import annotations
import dataclasses
import itertools
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Union
from game.commander.garrisons import Garrisons
from game.commander.objectivefinder import ObjectiveFinder
from game.data.doctrine import Doctrine
from game.htn import WorldState
from game.theater import ControlPoint, FrontLine, MissionTarget
from game.theater.theatergroundobject import (
TheaterGroundObject,
VehicleGroupGroundObject,
NavalGroundObject,
IadsGroundObject,
)
@ -32,7 +33,7 @@ class TheaterState(WorldState["TheaterState"]):
enemy_convoys: list[Convoy]
enemy_shipping: list[CargoShip]
enemy_ships: list[NavalGroundObject]
enemy_garrisons: list[VehicleGroupGroundObject]
enemy_garrisons: Garrisons
oca_targets: list[ControlPoint]
strike_targets: list[TheaterGroundObject[Any]]
enemy_barcaps: list[ControlPoint]
@ -69,7 +70,7 @@ class TheaterState(WorldState["TheaterState"]):
enemy_convoys=list(self.enemy_convoys),
enemy_shipping=list(self.enemy_shipping),
enemy_ships=list(self.enemy_ships),
enemy_garrisons=list(self.enemy_garrisons),
enemy_garrisons=dataclasses.replace(self.enemy_garrisons),
oca_targets=list(self.oca_targets),
strike_targets=list(self.strike_targets),
enemy_barcaps=list(self.enemy_barcaps),
@ -97,7 +98,7 @@ class TheaterState(WorldState["TheaterState"]):
enemy_convoys=list(finder.convoys()),
enemy_shipping=list(finder.cargo_ships()),
enemy_ships=list(finder.enemy_ships()),
enemy_garrisons=list(finder.threatening_vehicle_groups()),
enemy_garrisons=Garrisons.from_theater(game.theater, not player),
oca_targets=list(finder.oca_targets(min_aircraft=20)),
strike_targets=list(finder.strike_targets()),
enemy_barcaps=list(game.theater.control_points_for(not player)),