Set preferred mission types for aircraft.

This commit is contained in:
Dan Albert 2020-10-04 14:23:49 -07:00
parent 93db1254ec
commit 023925d741
2 changed files with 167 additions and 21 deletions

View File

@ -3,9 +3,9 @@ from __future__ import annotations
import logging import logging
import operator import operator
from dataclasses import dataclass from dataclasses import dataclass
from typing import Iterator, List, Optional, Set, TYPE_CHECKING, Tuple from typing import Iterator, List, Optional, Set, TYPE_CHECKING, Tuple, Type
from dcs.unittype import UnitType from dcs.unittype import FlyingType, UnitType
from game import db from game import db
from game.data.radar_db import UNITS_WITH_RADAR from game.data.radar_db import UNITS_WITH_RADAR
@ -15,9 +15,13 @@ 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 (
CAP_CAPABLE, CAP_CAPABLE,
CAP_PREFERRED,
CAS_CAPABLE, CAS_CAPABLE,
CAS_PREFERRED,
SEAD_CAPABLE, SEAD_CAPABLE,
SEAD_PREFERRED,
STRIKE_CAPABLE, STRIKE_CAPABLE,
STRIKE_PREFERRED,
) )
from gen.flights.closestairfields import ( from gen.flights.closestairfields import (
ClosestAirfields, ClosestAirfields,
@ -102,30 +106,63 @@ class AircraftAllocator:
maximum allowed range. If insufficient aircraft are available for the maximum allowed range. If insufficient aircraft are available for the
mission, None is returned. mission, None is returned.
Airfields are searched ordered nearest to farthest from the target and
searched twice. The first search looks for aircraft which prefer the
mission type, and the second search looks for any aircraft which are
capable of the mission type. For example, an F-14 from a nearby carrier
will be preferred for the CAP of an airfield that has only F-16s, but if
the carrier has only F/A-18s the F-16s will be used for CAP instead.
Note that aircraft *will* be removed from the global inventory on Note that aircraft *will* be removed from the global inventory on
success. This is to ensure that the same aircraft are not matched twice success. This is to ensure that the same aircraft are not matched twice
on subsequent calls. If the found aircraft are not used, the caller is on subsequent calls. If the found aircraft are not used, the caller is
responsible for returning them to the inventory. responsible for returning them to the inventory.
""" """
cap_missions = (FlightType.BARCAP, FlightType.CAP, FlightType.TARCAP) result = self.find_aircraft_of_type(
if flight.task in cap_missions: flight, self.preferred_aircraft_for_task(flight.task)
types = CAP_CAPABLE )
elif flight.task == FlightType.CAS: if result is not None:
types = CAS_CAPABLE return result
elif flight.task in (FlightType.DEAD, FlightType.SEAD): return self.find_aircraft_of_type(
types = SEAD_CAPABLE flight, self.capable_aircraft_for_task(flight.task)
elif flight.task == FlightType.STRIKE: )
types = STRIKE_CAPABLE
elif flight.task == FlightType.ESCORT:
types = CAP_CAPABLE
else:
logging.error(f"Unplannable flight type: {flight.task}")
return None
# TODO: Implement mission type weighting for aircraft. @staticmethod
# We should avoid assigning F/A-18s to CAP missions when there are F-15s def preferred_aircraft_for_task(task: FlightType) -> List[Type[FlyingType]]:
# available, since the F/A-18 is capable of performing other tasks that cap_missions = (FlightType.BARCAP, FlightType.CAP, FlightType.TARCAP)
# the F-15 is not capable of. if task in cap_missions:
return CAP_PREFERRED
elif task == FlightType.CAS:
return CAS_PREFERRED
elif task in (FlightType.DEAD, FlightType.SEAD):
return SEAD_PREFERRED
elif task == FlightType.STRIKE:
return STRIKE_PREFERRED
elif task == FlightType.ESCORT:
return CAP_PREFERRED
else:
return []
@staticmethod
def capable_aircraft_for_task(task: FlightType) -> List[Type[FlyingType]]:
cap_missions = (FlightType.BARCAP, FlightType.CAP, FlightType.TARCAP)
if task in cap_missions:
return CAP_CAPABLE
elif task == FlightType.CAS:
return CAS_CAPABLE
elif task in (FlightType.DEAD, FlightType.SEAD):
return SEAD_CAPABLE
elif task == FlightType.STRIKE:
return STRIKE_CAPABLE
elif task == FlightType.ESCORT:
return CAP_CAPABLE
else:
logging.error(f"Unplannable flight type: {task}")
return []
def find_aircraft_of_type(
self, flight: ProposedFlight, types: List[Type[FlyingType]],
) -> Optional[Tuple[ControlPoint, UnitType]]:
airfields_in_range = self.closest_airfields.airfields_within( airfields_in_range = self.closest_airfields.airfields_within(
flight.max_distance flight.max_distance
) )
@ -312,7 +349,8 @@ class ObjectiveFinder:
yield from cp.ground_objects yield from cp.ground_objects
yield from self.front_lines() yield from self.front_lines()
def closest_airfields_to(self, location: MissionTarget) -> ClosestAirfields: @staticmethod
def closest_airfields_to(location: MissionTarget) -> ClosestAirfields:
"""Returns the closest airfields to the given location.""" """Returns the closest airfields to the given location."""
return ObjectiveDistanceCache.get_closest_airfields(location) return ObjectiveDistanceCache.get_closest_airfields(location)

View File

@ -80,6 +80,10 @@ from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.mb339.mb339 import MB_339PAN from pydcs_extensions.mb339.mb339 import MB_339PAN
from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M
# TODO: These lists really ought to be era (faction) dependent.
# Factions which have F-5s, F-86s, and A-4s will should prefer F-5s for CAP, but
# factions that also have F-4s should not.
INTERCEPT_CAPABLE = [ INTERCEPT_CAPABLE = [
MiG_21Bis, MiG_21Bis,
MiG_25PD, MiG_25PD,
@ -150,6 +154,42 @@ CAP_CAPABLE = [
Rafale_M, Rafale_M,
] ]
CAP_PREFERRED = [
MiG_15bis,
MiG_19P,
MiG_21Bis,
MiG_23MLD,
MiG_25PD,
MiG_29A,
MiG_29G,
MiG_29S,
MiG_31,
Su_27,
J_11A,
Su_30,
Su_33,
M_2000C,
Mirage_2000_5,
F_86F_Sabre,
F_14B,
F_15C,
P_51D_30_NA,
P_51D,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
Rafale_M,
]
# Used for CAS (Close air support) and BAI (Battlefield Interdiction) # Used for CAS (Close air support) and BAI (Battlefield Interdiction)
CAS_CAPABLE = [ CAS_CAPABLE = [
@ -228,6 +268,59 @@ CAS_CAPABLE = [
RQ_1A_Predator RQ_1A_Predator
] ]
CAS_PREFERRED = [
Su_17M4,
Su_24M,
Su_24MR,
Su_25,
Su_25T,
Su_25TM,
Su_34,
JF_17,
A_10A,
A_10C,
A_10C_2,
AV8BNA,
F_15E,
Tornado_GR4,
C_101CC,
MB_339PAN,
L_39ZA,
AJS37,
SA342M,
SA342L,
OH_58D,
AH_64A,
AH_64D,
AH_1W,
UH_1H,
Mi_8MT,
Mi_28N,
Mi_24V,
Ka_50,
P_47D_30,
P_47D_30bl1,
P_47D_40,
A_20G,
A_4E_C,
Rafale_A_S,
WingLoong_I,
MQ_9_Reaper,
RQ_1A_Predator
]
# Aircraft used for SEAD / DEAD tasks # Aircraft used for SEAD / DEAD tasks
SEAD_CAPABLE = [ SEAD_CAPABLE = [
F_4E, F_4E,
@ -252,6 +345,12 @@ SEAD_CAPABLE = [
Rafale_A_S Rafale_A_S
] ]
SEAD_PREFERRED = [
F_4E,
Su_25T,
Tornado_IDS,
]
# Aircraft used for Strike mission # Aircraft used for Strike mission
STRIKE_CAPABLE = [ STRIKE_CAPABLE = [
MiG_15bis, MiG_15bis,
@ -309,6 +408,15 @@ STRIKE_CAPABLE = [
] ]
STRIKE_PREFERRED = [
AJS37,
F_15E,
Tornado_GR4,
A_20G,
B_17G,
]
ANTISHIP_CAPABLE = [ ANTISHIP_CAPABLE = [
Su_24M, Su_24M,
Su_17M4, Su_17M4,