Plan anti-ship missions automatically.

This commit is contained in:
Dan Albert 2020-11-17 01:08:06 -08:00
parent 2ac92a75a4
commit 69dbe62b70
2 changed files with 75 additions and 29 deletions

View File

@ -5,7 +5,16 @@ import operator
import random import random
from dataclasses import dataclass from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from typing import Iterator, List, Optional, Set, TYPE_CHECKING, Tuple, Type from typing import (
Iterable,
Iterator,
List,
Optional,
Set,
TYPE_CHECKING,
Tuple,
Type,
)
from dcs.unittype import FlyingType, UnitType from dcs.unittype import FlyingType, UnitType
@ -16,6 +25,8 @@ from game.utils import nm_to_meter
from gen import Conflict from gen import Conflict
from gen.ato import Package from gen.ato import Package
from gen.flights.ai_flight_planner_db import ( from gen.flights.ai_flight_planner_db import (
ANTISHIP_CAPABLE,
ANTISHIP_PREFERRED,
CAP_CAPABLE, CAP_CAPABLE,
CAP_PREFERRED, CAP_PREFERRED,
CAS_CAPABLE, CAS_CAPABLE,
@ -46,7 +57,7 @@ from theater import (
# Avoid importing some types that cause circular imports unless type checking. # Avoid importing some types that cause circular imports unless type checking.
from theater.theatergroundobject import ( from theater.theatergroundobject import (
EwrGroundObject, EwrGroundObject,
VehicleGroupGroundObject, NavalGroundObject, VehicleGroupGroundObject,
) )
if TYPE_CHECKING: if TYPE_CHECKING:
@ -141,6 +152,8 @@ class AircraftAllocator:
cap_missions = (FlightType.BARCAP, FlightType.TARCAP) cap_missions = (FlightType.BARCAP, FlightType.TARCAP)
if task in cap_missions: if task in cap_missions:
return CAP_PREFERRED return CAP_PREFERRED
elif task == FlightType.ANTISHIP:
return ANTISHIP_PREFERRED
elif task == FlightType.BAI: elif task == FlightType.BAI:
return CAS_CAPABLE return CAS_CAPABLE
elif task == FlightType.CAS: elif task == FlightType.CAS:
@ -159,6 +172,8 @@ class AircraftAllocator:
cap_missions = (FlightType.BARCAP, FlightType.TARCAP) cap_missions = (FlightType.BARCAP, FlightType.TARCAP)
if task in cap_missions: if task in cap_missions:
return CAP_CAPABLE return CAP_CAPABLE
elif task == FlightType.ANTISHIP:
return ANTISHIP_CAPABLE
elif task == FlightType.BAI: elif task == FlightType.BAI:
return CAS_CAPABLE return CAS_CAPABLE
elif task == FlightType.CAS: elif task == FlightType.CAS:
@ -279,22 +294,10 @@ class ObjectiveFinder:
SAM sites are sorted by their closest proximity to any friendly control SAM sites are sorted by their closest proximity to any friendly control
point (airfield or fleet). point (airfield or fleet).
""" """
sams: List[Tuple[TheaterGroundObject, int]] = [] return self._targets_by_range(self.enemy_sams())
for sam in self.enemy_sams():
ranges: List[int] = []
for cp in self.friendly_control_points():
ranges.append(sam.distance_to(cp))
sams.append((sam, min(ranges)))
sams = sorted(sams, key=operator.itemgetter(1))
for sam, _range in sams:
yield sam
def enemy_vehicle_groups(self) -> Iterator[VehicleGroupGroundObject]: def enemy_vehicle_groups(self) -> Iterator[VehicleGroupGroundObject]:
"""Iterates over all enemy vehicle groups.""" """Iterates over all enemy vehicle groups."""
# Control points might have the same ground object several times, for
# some reason.
found_targets: Set[str] = set()
for cp in self.enemy_control_points(): for cp in self.enemy_control_points():
for ground_object in cp.ground_objects: for ground_object in cp.ground_objects:
if not isinstance(ground_object, VehicleGroupGroundObject): if not isinstance(ground_object, VehicleGroupGroundObject):
@ -303,11 +306,7 @@ class ObjectiveFinder:
if ground_object.is_dead: if ground_object.is_dead:
continue continue
if ground_object.name in found_targets:
continue
yield ground_object yield ground_object
found_targets.add(ground_object.name)
def threatening_vehicle_groups(self) -> Iterator[TheaterGroundObject]: def threatening_vehicle_groups(self) -> Iterator[TheaterGroundObject]:
"""Iterates over enemy vehicle groups near friendly control points. """Iterates over enemy vehicle groups near friendly control points.
@ -315,16 +314,40 @@ class ObjectiveFinder:
Groups are sorted by their closest proximity to any friendly control Groups are sorted by their closest proximity to any friendly control
point (airfield or fleet). point (airfield or fleet).
""" """
groups: List[Tuple[VehicleGroupGroundObject, int]] = [] return self._targets_by_range(self.enemy_vehicle_groups())
for group in self.enemy_vehicle_groups():
def enemy_ships(self) -> Iterator[NavalGroundObject]:
for cp in self.enemy_control_points():
for ground_object in cp.ground_objects:
if not isinstance(ground_object, NavalGroundObject):
continue
if ground_object.is_dead:
continue
yield ground_object
def threatening_ships(self) -> Iterator[TheaterGroundObject]:
"""Iterates over enemy ships 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_ships())
def _targets_by_range(
self,
targets: Iterable[MissionTarget]) -> Iterator[TheaterGroundObject]:
target_ranges: List[Tuple[MissionTarget, int]] = []
for target in targets:
ranges: List[int] = [] ranges: List[int] = []
for cp in self.friendly_control_points(): for cp in self.friendly_control_points():
ranges.append(group.distance_to(cp)) ranges.append(target.distance_to(cp))
groups.append((group, min(ranges))) target_ranges.append((target, min(ranges)))
groups = sorted(groups, key=operator.itemgetter(1)) target_ranges = sorted(target_ranges, key=operator.itemgetter(1))
for group, _range in groups: for target, _range in target_ranges:
yield group yield target
def strike_targets(self) -> Iterator[TheaterGroundObject]: def strike_targets(self) -> Iterator[TheaterGroundObject]:
"""Iterates over enemy strike targets. """Iterates over enemy strike targets.
@ -333,11 +356,17 @@ class ObjectiveFinder:
point (airfield or fleet). point (airfield or fleet).
""" """
targets: List[Tuple[TheaterGroundObject, int]] = [] targets: List[Tuple[TheaterGroundObject, int]] = []
# Control points might have the same ground object several times, for # Building objectives are made of several individual TGOs (one per
# some reason. # building).
found_targets: Set[str] = set() found_targets: Set[str] = set()
for enemy_cp in self.enemy_control_points(): for enemy_cp in self.enemy_control_points():
for ground_object in enemy_cp.ground_objects: for ground_object in enemy_cp.ground_objects:
if isinstance(ground_object, VehicleGroupGroundObject):
# BAI target, not strike target.
continue
if isinstance(ground_object, NavalGroundObject):
# Anti-ship target, not strike target.
continue
if ground_object.is_dead: if ground_object.is_dead:
continue continue
if ground_object.name in found_targets: if ground_object.name in found_targets:
@ -440,6 +469,7 @@ class CoalitionMissionPlanner:
# TODO: Merge into doctrine, also limit by aircraft. # TODO: Merge into doctrine, also limit by aircraft.
MAX_CAP_RANGE = nm_to_meter(100) MAX_CAP_RANGE = nm_to_meter(100)
MAX_CAS_RANGE = nm_to_meter(50) MAX_CAS_RANGE = nm_to_meter(50)
MAX_ANTISHIP_RANGE = nm_to_meter(150)
MAX_BAI_RANGE = nm_to_meter(150) MAX_BAI_RANGE = nm_to_meter(150)
MAX_SEAD_RANGE = nm_to_meter(150) MAX_SEAD_RANGE = nm_to_meter(150)
MAX_STRIKE_RANGE = nm_to_meter(150) MAX_STRIKE_RANGE = nm_to_meter(150)
@ -476,6 +506,13 @@ class CoalitionMissionPlanner:
ProposedFlight(FlightType.ESCORT, 2, self.MAX_SEAD_RANGE), ProposedFlight(FlightType.ESCORT, 2, self.MAX_SEAD_RANGE),
]) ])
for group in self.objective_finder.threatening_ships():
yield ProposedMission(group, [
ProposedFlight(FlightType.ANTISHIP, 2, self.MAX_ANTISHIP_RANGE),
# TODO: Max escort range.
ProposedFlight(FlightType.ESCORT, 2, self.MAX_ANTISHIP_RANGE),
])
for group in self.objective_finder.threatening_vehicle_groups(): for group in self.objective_finder.threatening_vehicle_groups():
yield ProposedMission(group, [ yield ProposedMission(group, [
ProposedFlight(FlightType.BAI, 2, self.MAX_BAI_RANGE), ProposedFlight(FlightType.BAI, 2, self.MAX_BAI_RANGE),

View File

@ -444,6 +444,7 @@ STRIKE_PREFERRED = [
ANTISHIP_CAPABLE = [ ANTISHIP_CAPABLE = [
Su_24M, Su_24M,
Su_17M4, Su_17M4,
FA_18C_hornet,
F_A_18C, F_A_18C,
F_15E, F_15E,
AV8BNA, AV8BNA,
@ -458,7 +459,15 @@ ANTISHIP_CAPABLE = [
Tornado_GR4, Tornado_GR4,
Ju_88A4, Ju_88A4,
Rafale_A_S Rafale_A_S,
]
ANTISHIP_PREFERRED = [
FA_18C_hornet,
F_A_18C,
JF_17,
Rafale_A_S,
Su_24M,
] ]
DRONES = [ DRONES = [