Add auto-procurment for airlift assets.

https://github.com/Khopa/dcs_liberation/issues/825
This commit is contained in:
Dan Albert 2021-04-23 23:06:31 -07:00
parent 3161ccced3
commit f69450e2ae
4 changed files with 60 additions and 16 deletions

View File

@ -30,7 +30,7 @@ from .factions.faction import Faction
from .income import Income
from .infos.information import Information
from .navmesh import NavMesh
from .procurement import ProcurementAi
from .procurement import AircraftProcurementRequest, ProcurementAi
from .settings import Settings
from .theater import ConflictTheater
from .threatzones import ThreatZones
@ -117,6 +117,9 @@ class Game:
self.conditions = self.generate_conditions()
self.blue_procurement_requests: List[AircraftProcurementRequest] = []
self.red_procurement_requests: List[AircraftProcurementRequest] = []
self.blue_ato = AirTaskingOrder()
self.red_ato = AirTaskingOrder()
@ -131,13 +134,15 @@ class Game:
# Turn 0 procurement. We don't actually have any missions to plan, but
# the planner will tell us what it would like to plan so we can use that
# to drive purchase decisions.
self.transfers.order_airlift_assets()
blue_planner = CoalitionMissionPlanner(self, is_player=True)
blue_planner.plan_missions()
red_planner = CoalitionMissionPlanner(self, is_player=False)
red_planner.plan_missions()
self.plan_procurement(blue_planner, red_planner)
self.plan_procurement()
def __getstate__(self) -> Dict[str, Any]:
state = self.__dict__.copy()
@ -159,6 +164,13 @@ class Game:
return self.blue_ato
return self.red_ato
def procurement_requests_for(
self, player: bool
) -> List[AircraftProcurementRequest]:
if player:
return self.blue_procurement_requests
return self.red_procurement_requests
def generate_conditions(self) -> Conditions:
return Conditions.generate(
self.theater, self.current_day, self.current_turn_time_of_day, self.settings
@ -337,6 +349,7 @@ class Game:
self.compute_threat_zones()
self.ground_planners = {}
self.transfers.order_airlift_assets()
self.transfers.plan_transports()
blue_planner = CoalitionMissionPlanner(self, is_player=True)
@ -351,13 +364,9 @@ class Game:
gplanner.plan_groundwar()
self.ground_planners[cp.id] = gplanner
self.plan_procurement(blue_planner, red_planner)
self.plan_procurement()
def plan_procurement(
self,
blue_planner: CoalitionMissionPlanner,
red_planner: CoalitionMissionPlanner,
) -> None:
def plan_procurement(self) -> None:
# The first turn needs to buy a *lot* of aircraft to fill CAPs, so it
# gets much more of the budget that turn. Otherwise budget (after
# repairs) is split evenly between air and ground. For the default
@ -372,7 +381,7 @@ class Game:
manage_front_line=self.settings.automate_front_line_reinforcements,
manage_aircraft=self.settings.automate_aircraft_reinforcements,
front_line_budget_share=ground_portion,
).spend_budget(self.budget, blue_planner.procurement_requests)
).spend_budget(self.budget, self.blue_procurement_requests)
self.enemy_budget = ProcurementAi(
self,
@ -382,7 +391,7 @@ class Game:
manage_front_line=True,
manage_aircraft=True,
front_line_budget_share=ground_portion,
).spend_budget(self.enemy_budget, red_planner.procurement_requests)
).spend_budget(self.enemy_budget, self.red_procurement_requests)
def message(self, text: str) -> None:
self.informations.append(Information(text, turn=self.turn))

View File

@ -72,7 +72,7 @@ class ProcurementAi:
if not self.is_player:
budget += self.sell_incomplete_squadrons()
if self.manage_aircraft:
budget = self.purchase_aircraft(budget, aircraft_requests)
budget = self.purchase_aircraft(budget)
return budget
def sell_incomplete_squadrons(self) -> float:
@ -192,10 +192,8 @@ class ProcurementAi:
aircraft_for_task(request.task_capability), airbase, request.number, budget
)
def purchase_aircraft(
self, budget: float, aircraft_requests: List[AircraftProcurementRequest]
) -> float:
for request in aircraft_requests:
def purchase_aircraft(self, budget: float) -> float:
for request in self.game.procurement_requests_for(self.is_player):
for airbase in self.best_airbases_for(request):
unit = self.affordable_aircraft_for(request, airbase, budget)
if unit is None:

View File

@ -9,6 +9,8 @@ from typing import Dict, Iterator, List, Optional, TYPE_CHECKING, Type
from dcs.mapping import Point
from dcs.unittype import FlyingType, VehicleType
from game.procurement import AircraftProcurementRequest
from game.utils import nautical_miles
from gen.ato import Package
from gen.flights.ai_flight_planner_db import TRANSPORT_CAPABLE
from gen.flights.flightplan import FlightPlanBuilder
@ -409,3 +411,38 @@ class PendingTransfers:
for transfer in self.pending_transfers:
if transfer.transport is None:
self.arrange_transport(transfer)
def order_airlift_assets(self) -> None:
for control_point in self.game.theater.controlpoints:
self.order_airlift_assets_at(control_point)
@staticmethod
def desired_airlift_capacity(control_point: ControlPoint) -> int:
return 4 if control_point.has_factory else 0
def current_airlift_capacity(self, control_point: ControlPoint) -> int:
inventory = self.game.aircraft_inventory.for_control_point(control_point)
return sum(
count
for unit_type, count in inventory.all_aircraft
if unit_type in TRANSPORT_CAPABLE
)
def order_airlift_assets_at(self, control_point: ControlPoint) -> None:
gap = self.desired_airlift_capacity(
control_point
) - self.current_airlift_capacity(control_point)
if gap <= 0:
return
if gap % 2:
# Always buy in pairs since we're not trying to fill odd squadrons. Purely
# aesthetic.
gap += 1
self.game.procurement_requests_for(player=control_point.captured).append(
AircraftProcurementRequest(
control_point, nautical_miles(200), FlightType.TRANSPORT, gap
)
)

View File

@ -551,7 +551,7 @@ class CoalitionMissionPlanner:
self.objective_finder = ObjectiveFinder(self.game, self.is_player)
self.ato = self.game.blue_ato if is_player else self.game.red_ato
self.threat_zones = self.game.threat_zone_for(not self.is_player)
self.procurement_requests: List[AircraftProcurementRequest] = []
self.procurement_requests = self.game.procurement_requests_for(self.is_player)
def critical_missions(self) -> Iterator[ProposedMission]:
"""Identifies the most important missions to plan this turn.