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: find prod pylon #139

Merged
merged 4 commits into from
Jun 1, 2024
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
1 change: 0 additions & 1 deletion src/ares/behaviors/combat/group/stutter_group_back.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ def execute(self, ai: "AresBot", config: dict, mediator: ManagerMediator) -> boo
sample_unit: Unit = sorted_units[0]

if self.group_weapons_on_cooldown(self.group, stutter_forward=False):

group_safe: bool = True
for unit in self.group:
if not mediator.is_position_safe(
Expand Down
13 changes: 12 additions & 1 deletion src/ares/build_runner/build_order_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,14 @@ def _parse_string_command(
if _duplicates := self.extract_integer_from_target(target):
duplicates = _duplicates

if command == BuildOrderOptions.CHRONO and not step.target:
raise Exception(
f"No target found for chrono build step command. \n"
f"Valid example: "
f"``` 16 chrono @ nexus ``` \n"
f"Found: {raw_step}"
)

step.start_at_supply = supply
for i in range(duplicates):
build_order.append(step)
Expand Down Expand Up @@ -389,7 +397,10 @@ def extract_integer_from_target(target: str) -> Optional[int]:
def _get_target_for_step(target: str) -> Union[str, UnitID]:
"""Set the target for the step."""
try:
return UnitID[target]
if target == BuildOrderOptions.CORE:
return UnitID.CYBERNETICSCORE
else:
return UnitID[target]
except KeyError:
try:
return BuildOrderTargetOptions[target]
Expand Down
6 changes: 4 additions & 2 deletions src/ares/build_runner/build_order_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ def set_build_completed(self) -> None:

def set_step_complete(self, value: UnitID) -> None:
if (
value == self.build_order[self.build_step].command
self.build_step < len(self.build_order)
and value == self.build_order[self.build_step].command
and self.current_step_started
):
self.current_step_complete = True
Expand Down Expand Up @@ -415,8 +416,9 @@ async def get_position(
]
if len(close_enemy_to_ramp) > 0:
at_wall = False
base_location = self._get_target(target)
return self.mediator.request_building_placement(
base_location=self.ai.start_location,
base_location=base_location,
structure_type=structure_type,
wall=at_wall,
within_psionic_matrix=within_psionic_matrix,
Expand Down
73 changes: 64 additions & 9 deletions src/ares/managers/placement_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,17 @@ def request_building_placement(
# prioritize production pylons if they exist
elif structure_type == UnitID.PYLON:
if available := [
a
for a in available
if self.placements_dict[location][building_size][a]["optimal_pylon"]
# don't wall in, user should intentionally pass wall parameter
and not self.placements_dict[location][building_size][a]["is_wall"]
]:
final_placement = min(
available,
key=lambda k: cy_distance_to_squared(k, base_location),
)
elif available := [
a
for a in available
if self.placements_dict[location][building_size][a][
Expand All @@ -500,8 +511,20 @@ def request_building_placement(
)

if self.ai.race == Race.Protoss and within_psionic_matrix:
build_near: Point2 = location
two_by_twos: dict = self.placements_dict[location][
BuildingSize.TWO_BY_TWO
]
if optimal_pylon := [
a
for a in two_by_twos
if self.placements_dict[location][BuildingSize.TWO_BY_TWO][a][
"optimal_pylon"
]
]:
build_near = optimal_pylon[0]
final_placement = self._find_placement_near_pylon(
available, base_location, pylon_build_progress
available, build_near, pylon_build_progress
)
if not final_placement:
logger.warning(
Expand Down Expand Up @@ -821,7 +844,7 @@ def _solve_protoss_building_formation(self):
start_x: int = int(el.x - 4.5)
start_y: int = int(el.y - 4.5)
self.points_to_avoid_grid[start_y : start_y + 9, start_x : start_x + 9] = 1
max_dist = 16
max_dist: int = 16
# calculate the wall positions first
if el == self.ai.start_location:
max_dist = 22
Expand All @@ -837,8 +860,8 @@ def _solve_protoss_building_formation(self):
# find production pylon positions first
production_pylon_positions = cy_find_building_locations(
kernel=np.ones((2, 2), dtype=np.uint8),
x_stride=6,
y_stride=6,
x_stride=7,
y_stride=7,
x_bounds=raw_x_bounds,
y_bounds=raw_y_bounds,
creep_grid=creep_grid,
Expand Down Expand Up @@ -867,10 +890,6 @@ def _solve_protoss_building_formation(self):
avoid_y : avoid_y + 2, avoid_x : avoid_x + 2
] = 1

# increase distance from townhall that should be avoided
start_x: int = int(el.x - 4.5)
start_y: int = int(el.y - 4.5)
self.points_to_avoid_grid[start_y : start_y + 9, start_x : start_x + 9] = 1
three_by_three_positions = cy_find_building_locations(
kernel=np.ones((3, 3), dtype=np.uint8),
x_stride=3,
Expand Down Expand Up @@ -928,6 +947,38 @@ def _solve_protoss_building_formation(self):
BuildingSize.TWO_BY_TWO, el, point2_pos
)

# find optimal pylon to build around (fits most 3x3)
self._find_optimal_pylon_for_base(el)

def _find_optimal_pylon_for_base(self, el: Point2) -> None:
two_by_twos: dict[Point2:dict] = self.placements_dict[el][
BuildingSize.TWO_BY_TWO
]
prod_pylons: list[Point2] = [
placement
for placement in two_by_twos
if two_by_twos[placement]["production_pylon"]
]
three_by_threes: dict[Point2:dict] = self.placements_dict[el][
BuildingSize.THREE_BY_THREE
]

best_pylon_pos: Point2 = el
most_three_by_threes: int = 0

for pylon_position in prod_pylons:
num_three_by_threes: int = 0
for three_by_three in three_by_threes:
if cy_distance_to_squared(pylon_position, three_by_three) < 42.25:
num_three_by_threes += 1
if num_three_by_threes > most_three_by_threes:
most_three_by_threes = num_three_by_threes
best_pylon_pos = pylon_position

self.placements_dict[el][BuildingSize.TWO_BY_TWO][best_pylon_pos][
"optimal_pylon"
] = True

def _solve_zerg_building_formation(self):
# TODO: Implement zerg placements
pass
Expand All @@ -940,6 +991,7 @@ def _add_placement_position(
production_pylon: bool = False,
wall: bool = False,
bunker: bool = False,
optimal_pylon: bool = False,
) -> None:
"""Add calculated position to placements dict."""
self.placements_dict[expansion_location][building_size][position] = {
Expand All @@ -951,6 +1003,7 @@ def _add_placement_position(
"time_requested": 0.0,
"production_pylon": production_pylon,
"bunker": bunker,
"optimal_pylon": optimal_pylon,
}

def _calculate_main_ramp_placements(self, el: Point2) -> None:
Expand Down Expand Up @@ -1035,7 +1088,9 @@ async def _draw_building_placements(self):
self.ai.draw_text_on_world(position, f"{placement}")
pos_min = Point3((placement.x - 1.0, placement.y - 1.0, z))
pos_max = Point3((placement.x + 1.0, placement.y + 1.0, z + 1))
if info["production_pylon"]:
if info["optimal_pylon"]:
colour = Point3((255, 0, 0))
elif info["production_pylon"]:
colour = Point3((0, 255, 0))
else:
colour = Point3((0, 0, 255))
Expand Down
Loading