Factor AI purchases out of Game.

For now this is mostly behavior preserving. I slightly improved the
ability to buy units when multiple front lines exist by removing full
bases as candidates, but it should be a minor change at best. A larger
improvement will come later.

This is also written such that it will work for the player as well. The
procurer currently runs for the player but with all the options off, so
it does nothing. The next patch allows adds options for the player to
use auto-procurement.
This commit is contained in:
Dan Albert
2020-12-05 20:42:02 -08:00
parent bac47dad83
commit 1adee0af17
4 changed files with 262 additions and 109 deletions

View File

@@ -25,8 +25,9 @@ from .event.event import Event, UnitsDeliveryEvent
from .event.frontlineattack import FrontlineAttackEvent
from .factions.faction import Faction
from .infos.information import Information
from .procurement import ProcurementAi
from .settings import Settings
from .theater import Airfield, ConflictTheater, ControlPoint, OffMapSpawn
from .theater import ConflictTheater, ControlPoint
from .unitmap import UnitMap
from .weather import Conditions, TimeOfDay
@@ -90,6 +91,9 @@ class Game:
self.__destroyed_units: List[str] = []
self.savepath = ""
self.budget = PLAYER_BUDGET_INITIAL
# The enemy currently doesn't buy anything on turn zero; they get
# pre-populated airbases that are generated by the new game generator.
self.enemy_budget = 0
self.current_unit_id = 0
self.current_group_id = 0
@@ -158,9 +162,21 @@ class Game:
reward += REWARDS[g.category]
return int(reward * self.settings.player_income_multiplier)
def _budget_player(self):
def process_player_income(self):
self.budget += self.budget_reward_amount
def process_enemy_income(self):
if not hasattr(self, "enemy_budget"):
self.enemy_budget = 0
production = 0.0
for enemy_point in self.theater.enemy_points():
for g in enemy_point.ground_objects:
if g.category in REWARDS.keys() and not g.is_dead:
production = production + REWARDS[g.category]
self.enemy_budget += production * self.settings.enemy_income_multiplier
def units_delivery_event(self, to_cp: ControlPoint) -> UnitsDeliveryEvent:
event = UnitsDeliveryEvent(attacker_name=self.player_name,
defender_name=self.player_name,
@@ -212,8 +228,25 @@ class Game:
for control_point in self.theater.controlpoints:
control_point.process_turn()
self._enemy_reinforcement()
self._budget_player()
self.process_enemy_income()
self.enemy_budget = ProcurementAi(
self,
for_player=False,
faction=self.enemy_faction,
manage_runways=True,
manage_front_line=True,
manage_aircraft=True
).spend_budget(self.enemy_budget)
self.process_player_income()
self.budget = ProcurementAi(
self,
for_player=True,
faction=self.player_faction,
manage_runways=False,
manage_front_line=False,
manage_aircraft=False
).spend_budget(self.budget)
if not no_action and self.turn > 1:
for cp in self.theater.player_points():
@@ -255,90 +288,8 @@ class Game:
gplanner.plan_groundwar()
self.ground_planners[cp.id] = gplanner
def _enemy_reinforcement(self):
"""
Compute and commision reinforcement for enemy bases
"""
MAX_ARMOR = 30 * self.settings.multiplier
MAX_AIRCRAFT = 25 * self.settings.multiplier
production = 0.0
for enemy_point in self.theater.enemy_points():
for g in enemy_point.ground_objects:
if g.category in REWARDS.keys() and not g.is_dead:
production = production + REWARDS[g.category]
budget = production * self.settings.enemy_income_multiplier
for control_point in self.theater.enemy_points():
if budget < db.RUNWAY_REPAIR_COST:
break
if control_point.runway_can_be_repaired:
control_point.begin_runway_repair()
budget -= db.RUNWAY_REPAIR_COST
self.informations.append(Information(
f"OPFOR has begun repairing the runway at {control_point}"))
budget_for_armored_units = budget / 2
budget_for_aircraft = budget / 2
potential_cp_armor = []
for cp in self.theater.enemy_points():
for cpe in cp.connected_points:
if cpe.captured and cp.base.total_armor < MAX_ARMOR:
potential_cp_armor.append(cp)
if len(potential_cp_armor) == 0:
potential_cp_armor = self.theater.enemy_points()
potential_cp_armor = [p for p in potential_cp_armor if
not isinstance(p, OffMapSpawn)]
i = 0
potential_units = db.FACTIONS[self.enemy_name].frontline_units
print("Enemy Recruiting")
print(potential_cp_armor)
print(budget_for_armored_units)
print(potential_units)
if len(potential_units) > 0 and len(potential_cp_armor) > 0:
while budget_for_armored_units > 0:
i = i + 1
if i > 50 or budget_for_armored_units <= 0:
break
target_cp = random.choice(potential_cp_armor)
if target_cp.base.total_armor >= MAX_ARMOR:
continue
unit = random.choice(potential_units)
price = db.PRICES[unit] * 2
budget_for_armored_units -= price * 2
target_cp.base.armor[unit] = target_cp.base.armor.get(unit, 0) + 2
info = Information("Enemy Reinforcement", unit.id + " x 2 at " + target_cp.name, self.turn)
print(str(info))
self.informations.append(info)
if budget_for_armored_units > 0:
budget_for_aircraft += budget_for_armored_units
potential_units = [u for u in db.FACTIONS[self.enemy_name].aircrafts
if u in db.UNIT_BY_TASK[CAS] or u in db.UNIT_BY_TASK[CAP]]
if len(potential_units) > 0 and len(potential_cp_armor) > 0:
while budget_for_aircraft > 0:
i = i + 1
if i > 50 or budget_for_aircraft <= 0:
break
target_cp = random.choice(potential_cp_armor)
if target_cp.base.total_aircraft >= MAX_AIRCRAFT:
continue
unit = random.choice(potential_units)
price = db.PRICES[unit] * 2
budget_for_aircraft -= price * 2
target_cp.base.aircraft[unit] = target_cp.base.aircraft.get(unit, 0) + 2
info = Information("Enemy Reinforcement", unit.id + " x 2 at " + target_cp.name, self.turn)
print(str(info))
self.informations.append(info)
def message(self, text: str) -> None:
self.informations.append(Information(text, turn=self.turn))
@property
def current_turn_time_of_day(self) -> TimeOfDay: