Avoid negative AI produrement budgets.

This also fixes the double charging that the AI was suffering from, so
they have a much better chance of actually affording planes now.

Fixes https://github.com/Khopa/dcs_liberation/issues/504
This commit is contained in:
Dan Albert 2020-12-05 21:50:30 -08:00
parent f396ff7f12
commit 7226359e64

View File

@ -1,12 +1,11 @@
from __future__ import annotations from __future__ import annotations
import logging
import math import math
import random import random
from typing import List, TYPE_CHECKING, Type from typing import List, Optional, TYPE_CHECKING, Type
from dcs.task import CAP, CAS from dcs.task import CAP, CAS
from dcs.unittype import FlyingType, UnitType from dcs.unittype import FlyingType, UnitType, VehicleType
from game import db from game import db
from game.factions.faction import Faction from game.factions.faction import Faction
@ -62,6 +61,14 @@ class ProcurementAi:
) )
return budget return budget
def random_affordable_ground_unit(
self, budget: int) -> Optional[Type[VehicleType]]:
affordable_units = [u for u in self.faction.frontline_units if
db.PRICES[u] <= budget]
if not affordable_units:
return None
return random.choice(affordable_units)
def reinforce_front_line(self, budget: int) -> int: def reinforce_front_line(self, budget: int) -> int:
if not self.faction.frontline_units: if not self.faction.frontline_units:
return budget return budget
@ -71,19 +78,16 @@ class ProcurementAi:
if not candidates: if not candidates:
return budget return budget
# TODO: No need to limit? while budget > 0:
for _ in range(50): cp = random.choice(candidates)
if budget <= 0: unit = self.random_affordable_ground_unit(budget)
if unit is None:
# Can't afford any more units.
break break
cp = random.choice(candidates) budget -= db.PRICES[unit]
unit = random.choice(self.faction.frontline_units) cp.base.armor[unit] = cp.base.armor.get(unit, 0) + 1
price = db.PRICES[unit] * 2 self.reinforcement_message(unit, cp, group_size=1)
# TODO: Don't allow negative budget.
# Build a list of only affordable units and choose from those.
budget -= price * 2
cp.base.armor[unit] = cp.base.armor.get(unit, 0) + 2
self.reinforcement_message(unit, cp)
if cp.base.total_armor >= armor_limit: if cp.base.total_armor >= armor_limit:
candidates.remove(cp) candidates.remove(cp)
@ -92,6 +96,17 @@ class ProcurementAi:
return budget return budget
def random_affordable_aircraft_group(
self, budget: int, size: int) -> Optional[Type[FlyingType]]:
unit_pool = [u for u in self.faction.aircrafts
if u in db.UNIT_BY_TASK[CAS] or u in db.UNIT_BY_TASK[CAP]]
affordable_units = [u for u in unit_pool
if db.PRICES[u] * size <= budget]
if not affordable_units:
return None
return random.choice(affordable_units)
def purchase_aircraft(self, budget: int) -> int: def purchase_aircraft(self, budget: int) -> int:
aircraft_limit = int(25 * self.game.settings.multiplier) aircraft_limit = int(25 * self.game.settings.multiplier)
candidates = self.airbase_candidates(aircraft_limit) candidates = self.airbase_candidates(aircraft_limit)
@ -103,19 +118,17 @@ class ProcurementAi:
if not unit_pool: if not unit_pool:
return budget return budget
# TODO: No need to limit? while budget > 0:
for _ in range(50): cp = random.choice(candidates)
if budget <= 0: group_size = 2
unit = self.random_affordable_aircraft_group(budget, group_size)
if unit is None:
# Can't afford any more aircraft.
break break
cp = random.choice(candidates) budget -= db.PRICES[unit] * group_size
unit = random.choice(unit_pool) cp.base.aircraft[unit] = cp.base.aircraft.get(unit, 0) + group_size
price = db.PRICES[unit] * 2 self.reinforcement_message(unit, cp, group_size)
# TODO: Don't allow negative budget.
# Build a list of only affordable units and choose from those.
budget -= price * 2
cp.base.aircraft[unit] = cp.base.aircraft.get(unit, 0) + 2
self.reinforcement_message(unit, cp)
if cp.base.total_aircraft >= aircraft_limit: if cp.base.total_aircraft >= aircraft_limit:
candidates.remove(cp) candidates.remove(cp)
@ -125,8 +138,9 @@ class ProcurementAi:
return budget return budget
def reinforcement_message(self, unit_type: Type[UnitType], def reinforcement_message(self, unit_type: Type[UnitType],
control_point: ControlPoint) -> None: control_point: ControlPoint,
description = f"{unit_type.id} x 2 at {control_point.name}" group_size: int) -> None:
description = f"{unit_type.id} x {group_size} at {control_point.name}"
if self.is_player: if self.is_player:
self.game.message(f"Our reinforcements: {description}") self.game.message(f"Our reinforcements: {description}")
else: else: