mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Move the AI to the normal procurement system.
The procurement AI now uses the same system as the players. Orders are placed and take a turn to fulfill. This has a few advantages: * We no longer need special case purchase logic for the turn 0 population of opfor airbases. * Players using auto-purchase can cancel orders they don't like.
This commit is contained in:
parent
7226359e64
commit
d519aa1dad
@ -374,7 +374,11 @@ class UnitsDeliveryEvent(Event):
|
|||||||
|
|
||||||
def skip(self) -> None:
|
def skip(self) -> None:
|
||||||
for k, v in self.units.items():
|
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)
|
if self.to_cp.captured:
|
||||||
self.game.informations.append(info)
|
name = "Ally "
|
||||||
|
else:
|
||||||
|
name = "Enemy "
|
||||||
|
self.game.message(
|
||||||
|
f"{name} reinforcements: {k.id} x {v} at {self.to_cp.name}")
|
||||||
|
|
||||||
self.to_cp.base.commision_units(self.units)
|
self.to_cp.base.commision_units(self.units)
|
||||||
|
|||||||
43
game/game.py
43
game/game.py
@ -91,9 +91,7 @@ class Game:
|
|||||||
self.__destroyed_units: List[str] = []
|
self.__destroyed_units: List[str] = []
|
||||||
self.savepath = ""
|
self.savepath = ""
|
||||||
self.budget = PLAYER_BUDGET_INITIAL
|
self.budget = PLAYER_BUDGET_INITIAL
|
||||||
# The enemy currently doesn't buy anything on turn zero; they get
|
self.enemy_budget = PLAYER_BUDGET_INITIAL
|
||||||
# pre-populated airbases that are generated by the new game generator.
|
|
||||||
self.enemy_budget = 0
|
|
||||||
self.current_unit_id = 0
|
self.current_unit_id = 0
|
||||||
self.current_group_id = 0
|
self.current_group_id = 0
|
||||||
|
|
||||||
@ -110,6 +108,8 @@ class Game:
|
|||||||
cp.pending_unit_deliveries = self.units_delivery_event(cp)
|
cp.pending_unit_deliveries = self.units_delivery_event(cp)
|
||||||
|
|
||||||
self.sanitize_sides()
|
self.sanitize_sides()
|
||||||
|
# Turn 0 procurement.
|
||||||
|
self.plan_procurement()
|
||||||
self.on_load()
|
self.on_load()
|
||||||
|
|
||||||
def generate_conditions(self) -> Conditions:
|
def generate_conditions(self) -> Conditions:
|
||||||
@ -229,24 +229,8 @@ class Game:
|
|||||||
control_point.process_turn()
|
control_point.process_turn()
|
||||||
|
|
||||||
self.process_enemy_income()
|
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.process_player_income()
|
||||||
self.budget = ProcurementAi(
|
|
||||||
self,
|
|
||||||
for_player=True,
|
|
||||||
faction=self.player_faction,
|
|
||||||
manage_runways=self.settings.automate_runway_repair,
|
|
||||||
manage_front_line=self.settings.automate_front_line_reinforcements,
|
|
||||||
manage_aircraft=self.settings.automate_aircraft_reinforcements
|
|
||||||
).spend_budget(self.budget)
|
|
||||||
|
|
||||||
if not no_action and self.turn > 1:
|
if not no_action and self.turn > 1:
|
||||||
for cp in self.theater.player_points():
|
for cp in self.theater.player_points():
|
||||||
@ -288,6 +272,27 @@ class Game:
|
|||||||
gplanner.plan_groundwar()
|
gplanner.plan_groundwar()
|
||||||
self.ground_planners[cp.id] = gplanner
|
self.ground_planners[cp.id] = gplanner
|
||||||
|
|
||||||
|
self.plan_procurement()
|
||||||
|
|
||||||
|
def plan_procurement(self) -> None:
|
||||||
|
self.budget = ProcurementAi(
|
||||||
|
self,
|
||||||
|
for_player=True,
|
||||||
|
faction=self.player_faction,
|
||||||
|
manage_runways=self.settings.automate_runway_repair,
|
||||||
|
manage_front_line=self.settings.automate_front_line_reinforcements,
|
||||||
|
manage_aircraft=self.settings.automate_aircraft_reinforcements
|
||||||
|
).spend_budget(self.budget)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
def message(self, text: str) -> None:
|
def message(self, text: str) -> None:
|
||||||
self.informations.append(Information(text, turn=self.turn))
|
self.informations.append(Information(text, turn=self.turn))
|
||||||
|
|
||||||
|
|||||||
@ -86,8 +86,8 @@ class ProcurementAi:
|
|||||||
break
|
break
|
||||||
|
|
||||||
budget -= db.PRICES[unit]
|
budget -= db.PRICES[unit]
|
||||||
cp.base.armor[unit] = cp.base.armor.get(unit, 0) + 1
|
assert cp.pending_unit_deliveries is not None
|
||||||
self.reinforcement_message(unit, cp, group_size=1)
|
cp.pending_unit_deliveries.deliver({unit: 1})
|
||||||
|
|
||||||
if cp.base.total_armor >= armor_limit:
|
if cp.base.total_armor >= armor_limit:
|
||||||
candidates.remove(cp)
|
candidates.remove(cp)
|
||||||
@ -127,8 +127,8 @@ class ProcurementAi:
|
|||||||
break
|
break
|
||||||
|
|
||||||
budget -= db.PRICES[unit] * group_size
|
budget -= db.PRICES[unit] * group_size
|
||||||
cp.base.aircraft[unit] = cp.base.aircraft.get(unit, 0) + group_size
|
assert cp.pending_unit_deliveries is not None
|
||||||
self.reinforcement_message(unit, cp, group_size)
|
cp.pending_unit_deliveries.deliver({unit: group_size})
|
||||||
|
|
||||||
if cp.base.total_aircraft >= aircraft_limit:
|
if cp.base.total_aircraft >= aircraft_limit:
|
||||||
candidates.remove(cp)
|
candidates.remove(cp)
|
||||||
@ -137,15 +137,6 @@ class ProcurementAi:
|
|||||||
|
|
||||||
return budget
|
return budget
|
||||||
|
|
||||||
def reinforcement_message(self, unit_type: Type[UnitType],
|
|
||||||
control_point: ControlPoint,
|
|
||||||
group_size: int) -> None:
|
|
||||||
description = f"{unit_type.id} x {group_size} at {control_point.name}"
|
|
||||||
if self.is_player:
|
|
||||||
self.game.message(f"Our reinforcements: {description}")
|
|
||||||
else:
|
|
||||||
self.game.message(f"OPFOR reinforcements: {description}")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def owned_points(self) -> List[ControlPoint]:
|
def owned_points(self) -> List[ControlPoint]:
|
||||||
if self.is_player:
|
if self.is_player:
|
||||||
|
|||||||
@ -91,7 +91,6 @@ class GameGenerator:
|
|||||||
# Reset name generator
|
# Reset name generator
|
||||||
namegen.reset()
|
namegen.reset()
|
||||||
self.prepare_theater()
|
self.prepare_theater()
|
||||||
self.populate_red_airbases()
|
|
||||||
game = Game(player_name=self.player,
|
game = Game(player_name=self.player,
|
||||||
enemy_name=self.enemy,
|
enemy_name=self.enemy,
|
||||||
theater=self.theater,
|
theater=self.theater,
|
||||||
@ -134,51 +133,6 @@ class GameGenerator:
|
|||||||
else:
|
else:
|
||||||
cp.captured = True
|
cp.captured = True
|
||||||
|
|
||||||
def populate_red_airbases(self) -> None:
|
|
||||||
for control_point in self.theater.enemy_points():
|
|
||||||
if control_point.captured:
|
|
||||||
continue
|
|
||||||
self.populate_red_airbase(control_point)
|
|
||||||
|
|
||||||
def populate_red_airbase(self, control_point: ControlPoint) -> None:
|
|
||||||
# Force reset cp on generation
|
|
||||||
control_point.base.aircraft = {}
|
|
||||||
control_point.base.armor = {}
|
|
||||||
control_point.base.aa = {}
|
|
||||||
control_point.base.commision_points = {}
|
|
||||||
control_point.base.strength = 1
|
|
||||||
|
|
||||||
# The tasks here are confusing. PinpointStrike for some reason means
|
|
||||||
# ground units.
|
|
||||||
for task in [PinpointStrike, CAP, CAS, AirDefence]:
|
|
||||||
if isinstance(control_point, OffMapSpawn):
|
|
||||||
# Off-map spawn locations start with no aircraft.
|
|
||||||
continue
|
|
||||||
|
|
||||||
if IMPORTANCE_HIGH <= control_point.importance <= IMPORTANCE_LOW:
|
|
||||||
raise ValueError(
|
|
||||||
f"CP importance must be between {IMPORTANCE_LOW} and "
|
|
||||||
f"{IMPORTANCE_HIGH}, is {control_point.importance}")
|
|
||||||
|
|
||||||
importance_factor = ((control_point.importance - IMPORTANCE_LOW) /
|
|
||||||
(IMPORTANCE_HIGH - IMPORTANCE_LOW))
|
|
||||||
# noinspection PyTypeChecker
|
|
||||||
unit_types = db.choose_units(task, importance_factor, UNIT_VARIETY,
|
|
||||||
self.enemy)
|
|
||||||
if not unit_types:
|
|
||||||
continue
|
|
||||||
|
|
||||||
count_log = math.log(control_point.importance + 0.01,
|
|
||||||
UNIT_COUNT_IMPORTANCE_LOG)
|
|
||||||
count = max((COUNT_BY_TASK[task] *
|
|
||||||
self.generator_settings.multiplier *
|
|
||||||
(count_log + 1)),
|
|
||||||
1)
|
|
||||||
|
|
||||||
count_per_type = max(int(float(count) / len(unit_types)), 1)
|
|
||||||
for unit_type in unit_types:
|
|
||||||
control_point.base.commision_units({unit_type: count_per_type})
|
|
||||||
|
|
||||||
|
|
||||||
class LocationFinder:
|
class LocationFinder:
|
||||||
def __init__(self, game: Game, control_point: ControlPoint) -> None:
|
def __init__(self, game: Game, control_point: ControlPoint) -> None:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user