Let the TheaterCommander manage front line stance.

This improves the AI behavior by choosing the stances non-randomly:

* Breakthrough will be used if the base is expected to be capturable and
  the coalition outnumbers the enemy by 20%.
* Elimination will be used if the coalition has at least as many units
  as the enemy.
* Defensive will be used if the coalition has at least half as many
  units as the enemy.
* Retreat will be used if the coalition is significantly outnumbers.

This also exposes the option to the player.
This commit is contained in:
Dan Albert
2021-07-12 20:31:38 -07:00
parent 575aca5886
commit 0a416ab758
35 changed files with 361 additions and 57 deletions

View File

@@ -12,6 +12,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanAewc(PackagePlanningTask[MissionTarget]):
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
return self.target in state.aewc_targets
def apply_effects(self, state: TheaterState) -> None:

View File

@@ -13,6 +13,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanAntiShip(PackagePlanningTask[NavalGroundObject]):
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
if self.target not in state.threatening_air_defenses:
return False
return self.target_area_preconditions_met(state, ignore_iads=True)

View File

@@ -12,6 +12,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanAntiShipping(PackagePlanningTask[CargoShip]):
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
if self.target not in state.enemy_shipping:
return False
return self.target_area_preconditions_met(state)

View File

@@ -12,6 +12,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanBai(PackagePlanningTask[VehicleGroupGroundObject]):
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
if self.target not in state.enemy_garrisons:
return False
return self.target_area_preconditions_met(state)

View File

@@ -19,6 +19,8 @@ class PlanBarcap(TheaterCommanderTask):
target: ControlPoint
def preconditions_met(self, state: TheaterState) -> bool:
if state.player and not state.ato_automation_enabled:
return False
return self.target in state.vulnerable_control_points
def apply_effects(self, state: TheaterState) -> None:

View File

@@ -0,0 +1,37 @@
from __future__ import annotations
from game.commander.tasks.frontlinestancetask import FrontLineStanceTask
from game.commander.theaterstate import TheaterState
from game.theater import ControlPoint
from game.theater.theatergroundobject import VehicleGroupGroundObject
from game.utils import meters
from gen.ground_forces.combat_stance import CombatStance
class BreakthroughAttack(FrontLineStanceTask):
@property
def stance(self) -> CombatStance:
return CombatStance.BREAKTHROUGH
@property
def have_sufficient_front_line_advantage(self) -> bool:
return self.ground_force_balance >= 1.2
@property
def opposing_garrisons_eliminated(self) -> bool:
# TODO: Should operate on TheaterState to account for BAIs planned this turn.
for tgo in self.enemy_cp.ground_objects:
if not isinstance(tgo, VehicleGroupGroundObject):
continue
if meters(tgo.distance_to(self.enemy_cp)) < ControlPoint.CAPTURE_DISTANCE:
return False
return True
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
return self.opposing_garrisons_eliminated
def apply_effects(self, state: TheaterState) -> None:
super().apply_effects(state)
state.active_front_lines.remove(self.front_line)

View File

@@ -12,6 +12,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanCas(PackagePlanningTask[FrontLine]):
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
return self.target in state.vulnerable_front_lines
def apply_effects(self, state: TheaterState) -> None:

View File

@@ -12,6 +12,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanConvoyInterdiction(PackagePlanningTask[Convoy]):
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
if self.target not in state.enemy_convoys:
return False
return self.target_area_preconditions_met(state)

View File

@@ -13,6 +13,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanDead(PackagePlanningTask[IadsGroundObject]):
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
if (
self.target not in state.threatening_air_defenses
and self.target not in state.detecting_air_defenses

View File

@@ -0,0 +1,14 @@
from __future__ import annotations
from game.commander.tasks.frontlinestancetask import FrontLineStanceTask
from gen.ground_forces.combat_stance import CombatStance
class DefensiveStance(FrontLineStanceTask):
@property
def stance(self) -> CombatStance:
return CombatStance.DEFENSIVE
@property
def have_sufficient_front_line_advantage(self) -> bool:
return self.ground_force_balance >= 0.5

View File

@@ -0,0 +1,14 @@
from __future__ import annotations
from game.commander.tasks.frontlinestancetask import FrontLineStanceTask
from gen.ground_forces.combat_stance import CombatStance
class EliminationAttack(FrontLineStanceTask):
@property
def stance(self) -> CombatStance:
return CombatStance.ELIMINATION
@property
def have_sufficient_front_line_advantage(self) -> bool:
return self.ground_force_balance >= 1.0

View File

@@ -14,6 +14,8 @@ class PlanOcaStrike(PackagePlanningTask[ControlPoint]):
aircraft_cold_start: bool
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
if self.target not in state.oca_targets:
return False
return self.target_area_preconditions_met(state)

View File

@@ -12,6 +12,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanRefueling(PackagePlanningTask[MissionTarget]):
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
return self.target in state.refueling_targets
def apply_effects(self, state: TheaterState) -> None:

View File

@@ -0,0 +1,14 @@
from __future__ import annotations
from game.commander.tasks.frontlinestancetask import FrontLineStanceTask
from gen.ground_forces.combat_stance import CombatStance
class RetreatStance(FrontLineStanceTask):
@property
def stance(self) -> CombatStance:
return CombatStance.RETREAT
@property
def have_sufficient_front_line_advantage(self) -> bool:
return True

View File

@@ -13,6 +13,8 @@ from gen.flights.flight import FlightType
@dataclass
class PlanStrike(PackagePlanningTask[TheaterGroundObject[Any]]):
def preconditions_met(self, state: TheaterState) -> bool:
if not super().preconditions_met(state):
return False
if self.target not in state.strike_targets:
return False
return self.target_area_preconditions_met(state)