Prioritize ammo depots when appropriate.

The AI will now prioritize targeting ammo depots if the current
deployable enemy forces outnumber the friendly cap by 50% or more.
This commit is contained in:
Dan Albert
2021-07-13 17:06:25 -07:00
parent 9568bc7ea6
commit 587034ad03
5 changed files with 89 additions and 24 deletions

View File

@@ -4,10 +4,13 @@ from dataclasses import dataclass
from game.commander.tasks.compound.destroyenemygroundunits import (
DestroyEnemyGroundUnits,
)
from game.commander.tasks.compound.reduceenemyfrontlinecapacity import (
ReduceEnemyFrontLineCapacity,
)
from game.commander.tasks.primitive.breakthroughattack import BreakthroughAttack
from game.commander.theaterstate import TheaterState
from game.htn import CompoundTask, Method
from game.theater import FrontLine
from game.theater import FrontLine, ControlPoint
@dataclass(frozen=True)
@@ -17,3 +20,32 @@ class CaptureBase(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
yield [BreakthroughAttack(self.front_line, state.context.coalition.player)]
yield [DestroyEnemyGroundUnits(self.front_line)]
if self.worth_destroying_ammo_depots(state):
yield [ReduceEnemyFrontLineCapacity(self.enemy_cp(state))]
def enemy_cp(self, state: TheaterState) -> ControlPoint:
return self.front_line.control_point_hostile_to(state.context.coalition.player)
def units_deployable(self, state: TheaterState, player: bool) -> int:
cp = self.front_line.control_point_friendly_to(player)
ammo_depots = list(state.ammo_dumps_at(cp))
return cp.deployable_front_line_units_with(len(ammo_depots))
def unit_cap(self, state: TheaterState, player: bool) -> int:
cp = self.front_line.control_point_friendly_to(player)
ammo_depots = list(state.ammo_dumps_at(cp))
return cp.front_line_capacity_with(len(ammo_depots))
def enemy_has_ammo_dumps(self, state: TheaterState) -> bool:
return bool(state.ammo_dumps_at(self.enemy_cp(state)))
def worth_destroying_ammo_depots(self, state: TheaterState) -> bool:
if not self.enemy_has_ammo_dumps(state):
return False
friendly_cap = self.unit_cap(state, state.context.coalition.player)
enemy_deployable = self.units_deployable(state, state.context.coalition.player)
# If the enemy can currently deploy 50% more units than we possibly could, it's
# worth killing an ammo depot.
return enemy_deployable / friendly_cap > 1.5

View File

@@ -1,19 +1,16 @@
from collections import Iterator
from dataclasses import dataclass
from game.commander.tasks.primitive.aggressiveattack import AggressiveAttack
from game.commander.tasks.primitive.cas import PlanCas
from game.commander.tasks.primitive.eliminationattack import EliminationAttack
from game.commander.tasks.primitive.strike import PlanStrike
from game.commander.theaterstate import TheaterState
from game.htn import CompoundTask, Method
from game.theater import FrontLine
from game.theater import ControlPoint
@dataclass(frozen=True)
class DestroyEnemyGroundUnits(CompoundTask[TheaterState]):
front_line: FrontLine
class ReduceEnemyFrontLineCapacity(CompoundTask[TheaterState]):
control_point: ControlPoint
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
yield [EliminationAttack(self.front_line, state.context.coalition.player)]
yield [AggressiveAttack(self.front_line, state.context.coalition.player)]
yield [PlanCas(self.front_line)]
for ammo_dump in state.ammo_dumps_at(self.control_point):
yield [PlanStrike(ammo_dump)]

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
import dataclasses
import itertools
import math
from collections import Iterator
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Union, Optional
@@ -18,6 +19,7 @@ from game.theater.theatergroundobject import (
NavalGroundObject,
IadsGroundObject,
VehicleGroupGroundObject,
BuildingGroundObject,
)
from game.threatzones import ThreatZones
from gen.ground_forces.combat_stance import CombatStance
@@ -88,6 +90,15 @@ class TheaterState(WorldState["TheaterState"]):
def eliminate_garrison(self, target: VehicleGroupGroundObject) -> None:
self.enemy_garrisons[target.control_point].eliminate(target)
def ammo_dumps_at(
self, control_point: ControlPoint
) -> Iterator[BuildingGroundObject]:
for target in self.strike_targets:
if target.control_point != control_point:
continue
if target.is_ammo_depot:
yield target
def clone(self) -> TheaterState:
# Do not use copy.deepcopy. Copying every TGO, control point, etc is absurdly
# expensive.