Move calculation of aircraft amounts into game.

The planner needs to know how much space is still expected to be
available next turn.
This commit is contained in:
Dan Albert 2020-11-20 18:25:03 -08:00
parent f8b2dbe283
commit c4b8a41742
7 changed files with 44 additions and 48 deletions

View File

@ -352,11 +352,13 @@ class Event:
logging.info(info.text)
class UnitsDeliveryEvent(Event):
informational = True
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
def __init__(self, attacker_name: str, defender_name: str,
from_cp: ControlPoint, to_cp: ControlPoint,
game: Game) -> None:
super(UnitsDeliveryEvent, self).__init__(game=game,
location=to_cp.position,
from_cp=from_cp,
@ -364,17 +366,16 @@ class UnitsDeliveryEvent(Event):
attacker_name=attacker_name,
defender_name=defender_name)
self.units: Dict[UnitType, int] = {}
self.units: Dict[Type[UnitType], int] = {}
def __str__(self):
def __str__(self) -> str:
return "Pending delivery to {}".format(self.to_cp)
def deliver(self, units: Dict[UnitType, int]):
def deliver(self, units: Dict[Type[UnitType], int]) -> None:
for k, v in units.items():
self.units[k] = self.units.get(k, 0) + v
def skip(self):
def skip(self) -> None:
for k, v in self.units.items():
info = Information("Ally Reinforcement", str(k.id) + " x " + str(v) + " at " + self.to_cp.name, self.game.turn)
self.game.informations.append(info)

View File

@ -316,7 +316,7 @@ class Game:
if i > 50 or budget_for_aircraft <= 0:
break
target_cp = random.choice(potential_cp_armor)
if target_cp.base.total_planes >= MAX_AIRCRAFT:
if target_cp.base.total_aircraft >= MAX_AIRCRAFT:
continue
unit = random.choice(potential_units)
price = db.PRICES[unit] * 2

View File

@ -4,9 +4,8 @@ import math
import typing
from typing import Dict, Type
from dcs.planes import PlaneType
from dcs.task import CAP, CAS, Embarking, PinpointStrike, Task
from dcs.unittype import UnitType, VehicleType
from dcs.unittype import FlyingType, UnitType, VehicleType
from dcs.vehicles import AirDefence, Armor
from game import db
@ -21,20 +20,16 @@ BASE_MIN_STRENGTH = 0
class Base:
aircraft = {} # type: typing.Dict[PlaneType, int]
armor = {} # type: typing.Dict[VehicleType, int]
aa = {} # type: typing.Dict[AirDefence, int]
strength = 1 # type: float
def __init__(self):
self.aircraft = {}
self.armor = {}
self.aa = {}
self.aircraft: Dict[FlyingType, int] = {}
self.armor: Dict[VehicleType, int] = {}
self.aa: Dict[AirDefence, int] = {}
self.commision_points: Dict[Type, float] = {}
self.strength = 1
@property
def total_planes(self) -> int:
def total_aircraft(self) -> int:
return sum(self.aircraft.values())
@property
@ -83,7 +78,7 @@ class Base:
logging.info("{} for {} ({}): {}".format(self, for_type, count, result))
return result
def _find_best_planes(self, for_type: Task, count: int) -> typing.Dict[PlaneType, int]:
def _find_best_planes(self, for_type: Task, count: int) -> typing.Dict[FlyingType, int]:
return self._find_best_unit(self.aircraft, for_type, count)
def _find_best_armor(self, for_type: Task, count: int) -> typing.Dict[Armor, int]:
@ -155,7 +150,7 @@ class Base:
if task:
count = sum([v for k, v in self.aircraft.items() if db.unit_task(k) == task])
else:
count = self.total_planes
count = self.total_aircraft
count = int(math.ceil(count * PLANES_SCRAMBLE_FACTOR * self.strength))
return min(min(max(count, PLANES_SCRAMBLE_MIN_BASE), int(PLANES_SCRAMBLE_MAX_BASE * multiplier)), count)
@ -167,18 +162,18 @@ class Base:
# previous logic removed because we always want the full air defense capabilities.
return self.total_aa
def scramble_sweep(self, multiplier: float) -> typing.Dict[PlaneType, int]:
def scramble_sweep(self, multiplier: float) -> typing.Dict[FlyingType, int]:
return self._find_best_planes(CAP, self.scramble_count(multiplier, CAP))
def scramble_last_defense(self):
# return as many CAP-capable aircraft as we can since this is the last defense of the base
# (but not more than 20 - that's just nuts)
return self._find_best_planes(CAP, min(self.total_planes, 20))
return self._find_best_planes(CAP, min(self.total_aircraft, 20))
def scramble_cas(self, multiplier: float) -> typing.Dict[PlaneType, int]:
def scramble_cas(self, multiplier: float) -> typing.Dict[FlyingType, int]:
return self._find_best_planes(CAS, self.scramble_count(multiplier, CAS))
def scramble_interceptors(self, multiplier: float) -> typing.Dict[PlaneType, int]:
def scramble_interceptors(self, multiplier: float) -> typing.Dict[FlyingType, int]:
return self._find_best_planes(CAP, self.scramble_count(multiplier, CAP))
def assemble_attack(self) -> typing.Dict[Armor, int]:

View File

@ -376,6 +376,15 @@ class ControlPoint(MissionTarget):
return False
return True
@property
def expected_aircraft_next_turn(self) -> int:
total = self.base.total_aircraft
assert self.pending_unit_deliveries
for unit_bought in self.pending_unit_deliveries.units:
if issubclass(unit_bought, FlyingType):
total += self.pending_unit_deliveries.units[unit_bought]
return total
class OffMapSpawn(ControlPoint):
def __init__(self, id: int, name: str, position: Point):

View File

@ -57,7 +57,7 @@ class QBaseMenu2(QDialog):
title = QLabel("<b>" + self.cp.name + "</b>")
title.setAlignment(Qt.AlignLeft | Qt.AlignTop)
title.setProperty("style", "base-title")
unitsPower = QLabel("{} / {} / Runway : {}".format(self.cp.base.total_planes, self.cp.base.total_armor,
unitsPower = QLabel("{} / {} / Runway : {}".format(self.cp.base.total_aircraft, self.cp.base.total_armor,
"Available" if self.cp.has_runway() else "Unavailable"))
self.topLayout.addWidget(title)
self.topLayout.addWidget(unitsPower)

View File

@ -126,13 +126,6 @@ class QRecruitBehaviour:
QRecruitBehaviour.BUDGET_FORMAT.format(self.budget))
def buy(self, unit_type):
if self.maximum_units > 0:
if self.total_units + 1 > self.maximum_units:
logging.info("Not enough space left !")
# TODO : display modal warning
return
price = db.PRICES[unit_type]
if self.budget >= price:
self.pending_deliveries.deliver({unit_type: 1})
@ -158,19 +151,6 @@ class QRecruitBehaviour:
self._update_count_label(unit_type)
self.update_available_budget()
@property
def total_units(self):
total = 0
for unit_type in self.recruitables_types:
total += self.cp.base.total_units(unit_type)
if self.pending_deliveries:
for unit_bought in self.pending_deliveries.units:
if db.unit_task(unit_bought) in self.recruitables_types:
total += self.pending_deliveries.units[unit_bought]
return total
def set_maximum_units(self, maximum_units):
"""
Set the maximum number of units that can be bought

View File

@ -1,3 +1,4 @@
import logging
from typing import Optional, Set
from PySide2.QtCore import Qt
@ -37,7 +38,7 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
self.bought_amount_labels = {}
self.existing_units_labels = {}
self.hangar_status = QHangarStatus(self.total_units, self.cp.available_aircraft_slots)
self.hangar_status = QHangarStatus(self.total_aircraft, self.cp.available_aircraft_slots)
self.init_ui()
@ -80,8 +81,18 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
self.setLayout(main_layout)
def buy(self, unit_type):
if self.maximum_units > 0:
if self.total_aircraft + 1 > self.maximum_units:
logging.debug(f"No space for additional aircraft at {self.cp}.")
return
super().buy(unit_type)
self.hangar_status.update_label(self.total_units, self.cp.available_aircraft_slots)
self.hangar_status.update_label(self.total_aircraft,
self.cp.available_aircraft_slots)
@property
def total_aircraft(self) -> int:
return self.cp.expected_aircraft_next_turn
def sell(self, unit_type: UnitType):
# Don't need to remove aircraft from the inventory if we're canceling
@ -99,7 +110,7 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
"assigned to a mission?", QMessageBox.Ok)
return
super().sell(unit_type)
self.hangar_status.update_label(self.total_units, self.cp.available_aircraft_slots)
self.hangar_status.update_label(self.total_aircraft, self.cp.available_aircraft_slots)
class QHangarStatus(QHBoxLayout):