Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: general tweaks and fixes #95

Merged
merged 6 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 46 additions & 26 deletions src/ares/behaviors/macro/mining.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,6 @@ def execute(self, ai: "AresBot", config: dict, mediator: ManagerMediator) -> boo
resource_position: Optional[Point2] = None
resource_tag: int = -1

# keeping worker safe is first priority
if self.keep_safe and (
# lib zone / nukes etc
not pos_safe(grid=avoidance_grid, position=worker_position)
# retreat based on self.flee_at_health_perc value
or (
worker.health_percentage <= health_perc
and not pos_safe(grid=grid, position=worker_position)
)
):
self._keep_worker_safe(mediator, grid, worker, worker_position)
continue

assigned_mineral_patch: bool = worker_tag in worker_to_mineral_patch_dict
assigned_gas_building: bool = worker_tag in worker_to_geyser_dict
dist_to_resource: float = 15.0
Expand All @@ -149,6 +136,26 @@ def execute(self, ai: "AresBot", config: dict, mediator: ManagerMediator) -> boo
mediator.remove_gas_building(gas_building_tag=resource_tag)
continue

# keeping worker safe is first priority
if self.keep_safe and (
# lib zone / nukes etc
not pos_safe(grid=avoidance_grid, position=worker_position)
# retreat based on self.flee_at_health_perc value
or (
worker.health_percentage <= health_perc
and not pos_safe(grid=grid, position=worker_position)
)
):
self._keep_worker_safe(
mediator,
grid,
worker,
resource,
resource_position,
dist_to_resource,
)
continue

if main_enemy_ground_threats and self._worker_attacking_enemy(
ai, dist_to_resource, worker
):
Expand Down Expand Up @@ -218,29 +225,36 @@ def _keep_worker_safe(
mediator: ManagerMediator,
grid: np.ndarray,
worker: Unit,
worker_position: Point2,
resource: Unit,
resource_position: Point2,
dist_to_resource: float,
) -> None:
"""Logic for keeping workers in danger safe.

Parameters
----------
mediator : ManagerMediator
mediator :
ManagerMediator used for getting information from other managers.
grid : np.ndarray
grid :
Ground grid with enemy influence.
worker :
Worker to keep safe.
worker_position :
Pass this in for optimization purposes,
as may have already retrieved it.
resource :
Resource worker is assigned to.
resource_position :
Resource this worker is assigned to.
dist_to_resource :
How far away are we from intended resource?

Returns
-------

"""
worker.move(
mediator.find_closest_safe_spot(from_pos=worker_position, grid=grid)
)
if dist_to_resource > 5.0:
worker.gather(resource)
else:
worker.move(
mediator.find_closest_safe_spot(from_pos=resource_position, grid=grid)
)

def _do_standard_mining(self, ai: "AresBot", worker: Unit, resource: Unit) -> None:
worker_tag: int = worker.tag
Expand Down Expand Up @@ -335,9 +349,15 @@ def _long_distance_mining(
target_mineral = cy_closest_to(target_base.position, ai.mineral_field)
# no pending base, find a mineral field
elif self.safe_long_distance_mineral_fields:
target_mineral = self.safe_long_distance_mineral_fields.closest_to(
worker
)
# early game, get closest to natural
if ai.time < 260.0:
target_mineral = cy_closest_to(
mediator.get_own_nat, self.safe_long_distance_mineral_fields
)
else:
target_mineral = cy_closest_to(
worker.position, self.safe_long_distance_mineral_fields
)

if target_mineral:
target_mineral_position: Point2 = target_mineral.position
Expand Down
16 changes: 12 additions & 4 deletions src/ares/build_runner/build_order_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class BuildOrderRunner:
"""

CONSTANT_WORKER_PRODUCTION_TILL: str = "ConstantWorkerProductionTill"
PERSISTENT_WORKER: str = "PersistentWorker"
REQUIRES_TOWNHALL_COMMANDS: set = {
AbilityId.UPGRADETOORBITAL_ORBITALCOMMAND,
AbilityId.UPGRADETOPLANETARYFORTRESS_PLANETARYFORTRESS,
Expand All @@ -76,12 +77,18 @@ def __init__(
self.config: dict = config
self.mediator: ManagerMediator = mediator
self.constant_worker_production_till: int = 0
self.persistent_worker: bool = True
self._chosen_opening: str = chosen_opening
if BUILDS in self.config:
build: list[str] = config[BUILDS][chosen_opening][OPENING_BUILD_ORDER]
self.constant_worker_production_till = config[BUILDS][chosen_opening][
self.CONSTANT_WORKER_PRODUCTION_TILL
]
if self.CONSTANT_WORKER_PRODUCTION_TILL in config[BUILDS][chosen_opening]:
self.constant_worker_production_till = config[BUILDS][chosen_opening][
self.CONSTANT_WORKER_PRODUCTION_TILL
]
if self.PERSISTENT_WORKER in config[BUILDS][chosen_opening]:
self.persistent_worker = config[BUILDS][chosen_opening][
self.PERSISTENT_WORKER
]
else:
build: list[str] = []

Expand Down Expand Up @@ -117,7 +124,8 @@ async def run_build(self) -> None:
"""
Runs the build order.
"""
self._assign_persistent_worker()
if self.persistent_worker:
self._assign_persistent_worker()
if len(self.build_order) > 0:
await self.do_step(self.build_order[self.build_step])

Expand Down
3 changes: 3 additions & 0 deletions src/ares/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ Pathing:
CorrosiveBile:
Cost: 100
Range: 3.0
KD8Charge:
Cost: 10
Range: 4.5
LiberatorZone:
Cost: 500
Range: 6
Expand Down
3 changes: 3 additions & 0 deletions src/ares/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
GROUND_ENEMY_LEAVING_BASES: str = "GroundEnemyLeavingBases"
GROUND_ENEMY_NEAR_BASES: str = "GroundEnemyNearBases"
GROUND_RANGE: str = "GroundRange"
KD8_CHARGE: str = "KD8Charge"
LIBERATOR_ZONE: str = "LiberatorZone"
LURKER_SPINE: str = "LurkerSpine"
MINERAL_BOOST: str = "MineralBoost"
Expand Down Expand Up @@ -355,9 +356,11 @@ class UnitRole(str, Enum):
HARASSING = "HARASSING" # units that are harassing
IDLE = "IDLE" # not doing anything
PERSISTENT_BUILDER = "PERSISTENT_BUILDER" # does not get reassigned automatically
PROXY_WORKER = "PROXY_WORKER"
REPAIRING = "REPAIRING" # repairing scvs
SCOUTING = "SCOUTING"
SURROUNDING = "SURROUNDING" # zerglings currently in a surround
UNDER_REPAIR = "UNDER_REPAIR" # units currently under repair
# control groups, use for anything not specified
CONTROL_GROUP_ONE = "CONTROL_GROUP_ONE"
CONTROL_GROUP_TWO = "CONTROL_GROUP_TWO"
Expand Down
5 changes: 0 additions & 5 deletions src/ares/managers/building_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,6 @@ def _handle_construction_orders(self) -> None:
tags_to_remove.add(worker_tag)
continue

# this happens if no target location is available eg: all expansions taken
if not target:
tags_to_remove.add(worker_tag)
continue

# TODO: find the maximum distance so we don't have to keep adjusting this
distance: float = 3.2 if structure_id in GAS_BUILDINGS else 1.0

Expand Down
20 changes: 20 additions & 0 deletions src/ares/managers/path_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
GROUND_AVOIDANCE,
GROUND_COST,
GROUND_RANGE,
KD8_CHARGE,
LIBERATOR_ZONE,
LURKER_SPINE,
NUKE,
Expand All @@ -45,11 +46,13 @@
RANGE_BUFFER,
SHOW_PATHING_COST,
STORM,
TOWNHALL_TYPES,
UNITS,
ManagerName,
ManagerRequestType,
)
from ares.cython_extensions.combat_utils import cy_is_position_safe
from ares.cython_extensions.geometry import cy_distance_to
from ares.dicts.weight_costs import WEIGHT_COSTS
from ares.managers.manager import Manager
from ares.managers.manager_mediator import IManagerMediator, ManagerMediator
Expand Down Expand Up @@ -667,6 +670,18 @@ def _add_effects(self) -> None:
+ self.config[PATHING][EFFECTS_RANGE_BUFFER],
[self.climber_grid, self.ground_grid, self.ground_avoidance_grid],
)
elif effect.id == "KD8CHARGE":
(
self.climber_grid,
self.ground_grid,
# self.ground_avoidance_grid,
) = self.add_cost_to_multiple_grids(
Point2.center(effect.positions),
effect_values[KD8_CHARGE][COST],
effect_values[KD8_CHARGE][RANGE]
+ self.config[PATHING][EFFECTS_RANGE_BUFFER],
[self.climber_grid, self.ground_grid],
)
# liberator siege
elif effect.id in {
EffectId.LIBERATORTARGETMORPHDELAYPERSISTENT,
Expand Down Expand Up @@ -803,6 +818,11 @@ def _add_structure_influence(self, structure: Unit) -> None:
[self.air_grid, self.air_vs_ground_grid],
)
elif structure.type_id == UnitID.BUNKER:
if self.ai.enemy_structures.filter(
lambda g: g.type_id in TOWNHALL_TYPES
and cy_distance_to(g.position, structure.position) < 9.0
):
return
# add range of marine + 1
(
self.air_grid,
Expand Down
15 changes: 12 additions & 3 deletions src/ares/managers/placement_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ def request_building_placement(
reserve_placement: bool = True,
within_psionic_matrix: bool = False,
pylon_build_progress: float = 1.0,
closest_to: Optional[Point2] = None,
) -> Optional[Point2]:
"""Given a base location and building size find an available placement.

Expand All @@ -254,6 +255,8 @@ def request_building_placement(
Protoss specific -> calculated position have power?
pylon_build_progress : float, optional (default = 1.0)
Only relevant if `within_psionic_matrix = True`
closest_to : Point2, optional
Find placement at base closest to this

Returns
----------
Expand Down Expand Up @@ -333,9 +336,15 @@ def request_building_placement(
return

# get closest available by default
final_placement: Point2 = min(
available, key=lambda k: cy_distance_to(k, base_location)
)
if not closest_to:
final_placement: Point2 = min(
available, key=lambda k: cy_distance_to(k, base_location)
)
else:
final_placement: Point2 = min(
available, key=lambda k: cy_distance_to(k, closest_to)
)

# if wall placement is requested swap final_placement if possible
if wall:
if _available := [
Expand Down
50 changes: 34 additions & 16 deletions src/ares/managers/resource_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
UnitRole,
UnitTreeQueryType,
)
from ares.cython_extensions.geometry import cy_distance_to
from ares.cython_extensions.units_utils import cy_closest_to, cy_sorted_by_distance_to
from ares.managers.manager import Manager
from ares.managers.manager_mediator import IManagerMediator, ManagerMediator
Expand Down Expand Up @@ -244,6 +245,17 @@ async def update(self, iteration: int) -> None:
if th.tag not in self.cached_townhalls.tags and th.is_ready:
self.cached_townhalls.append(th)

# TODO: this is here to fix a rare but, where a building worker is
# selected but somehow it remains in this manager's bookkeeping
# FIX THIS!
if iteration % 32 == 0:
for worker in workers:
if worker.tag in self.manager_mediator.get_building_tracker_dict:
self.remove_worker_from_mineral(worker.tag)
self.manager_mediator.assign_role(
tag=worker.tag, role=UnitRole.BUILDING
)

if self.debug and self.config[DEBUG_OPTIONS][RESOURCE_DEBUG]:
self._print_debug_information()

Expand Down Expand Up @@ -302,6 +314,7 @@ def select_worker(
force_close: bool = False,
select_persistent_builder: bool = False,
only_select_persistent_builder: bool = False,
min_health_perc: float = 0.0,
) -> Optional[Unit]:
"""Select a worker.

Expand All @@ -324,6 +337,8 @@ def select_worker(
If True we can select the persistent_builder if it's available.
only_select_persistent_builder :
If True, don't find an alternative worker
min_health_perc :
Only select workers above this health percentage.

Returns
-------
Expand All @@ -350,7 +365,7 @@ def select_worker(

workers: Units = self.manager_mediator.get_units_from_roles(
roles={UnitRole.GATHERING, UnitRole.IDLE}, unit_type=self.ai.worker_type
)
).filter(lambda u: u.health_percentage >= min_health_perc)
# there is a chance we have no workers
if not workers or not target_position:
return
Expand Down Expand Up @@ -378,7 +393,9 @@ def select_worker(
# seems there are no townhalls with plenty of resources, don't be fussy at
# this point
if not townhalls:
return cy_closest_to(target_position, available_workers)
worker = cy_closest_to(target_position, available_workers)
self.remove_worker_from_mineral(worker.tag)
return worker

# go through townhalls, we loop through the min fields by distance to
# townhall that way there is a good chance we pick a worker at a far mineral
Expand All @@ -394,26 +411,27 @@ def select_worker(
if close_workers := available_workers.filter(
lambda w: w.tag
in self.mineral_patch_to_list_of_workers[mineral.tag]
and cy_distance_to(w.position, townhall.position) < 10.0
):
worker: Unit = cy_closest_to(
target_position, close_workers
)
self.remove_worker_from_mineral(worker.tag)
return worker

# try to get a worker at this patch that is not carrying
# resources
if _workers := available_workers.filter(
lambda w: w.tag
in self.mineral_patch_to_list_of_workers[mineral.tag]
and not w.is_carrying_resource
and not w.is_collecting
):
worker: Unit = _workers.first
# make sure to remove worker, so a new one can be assigned
# to mine
self.remove_worker_from_mineral(worker.tag)
return worker
else:
# try to get a worker at this patch that is not carrying
# resources
if _workers := available_workers.filter(
lambda w: w.tag
in self.mineral_patch_to_list_of_workers[mineral.tag]
and not w.is_carrying_resource
and not w.is_collecting
):
worker: Unit = _workers.first
# make sure to remove worker, so a new one
# can be assigned to mine
self.remove_worker_from_mineral(worker.tag)
return worker

# somehow got here without finding a worker, any worker will do
worker: Unit = cy_closest_to(target_position, available_workers)
Expand Down
Loading