Skip to content

Commit

Permalink
started work on the new build order implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
FredNoonienSingh committed Jan 6, 2025
1 parent fd2ce9c commit bf1dd0a
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 51 deletions.
1 change: 0 additions & 1 deletion bot/HarstemsAunt/army_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ def regroup(self) -> None:

self.units.furthest_to(self.position).move(self.position)


# TODO: #29 very basic, needs to be Adjusted to account for different, Unit types
def defend(self, position:Union[Point2,Point3,Unit]) -> None:
enemy_units = self.bot.enemy_units.closer_than(25, position)
Expand Down
107 changes: 107 additions & 0 deletions bot/HarstemsAunt/build_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from typing import Union, List, Enum

from sc2.unit import Unit
from sc2.units import Units
from sc2.bot_ai import BotAI
from sc2.position import Point2, Point3
from sc2.ids.unit_typeid import UnitTypeId

from .common import ALL_STRUCTURES, logger

class InstructionType(Enum):
UNIT_PRODUCTION = 1
BUILD_STRUCTURE = 2

class BuildInstruction:
""" Instruction for the Bot to build a structure, not sure yet how to add upgrades, and multiple instructions at once
and how to take unit production into account, i already got a set of all Structures in .common maybe i just add every instruction
including upgrades and unit production.
"""

def __init__(self, type_id:UnitTypeId, position:Union[Point2,Point3,Unit], accuracy:float) -> None:
self.type_id = type_id
self.position = position
self.accuracy = accuracy

@property
def instruction_type(self) -> InstructionType:
if self.type_id in ALL_STRUCTURES:
return InstructionType.BUILD_STRUCTURE
return InstructionType.UNIT_PRODUCTION

def __repr__(self):
if self.instruction_type == InstructionType.BUILD_STRUCTURE:
return f"build {self.type_id} at {self.position}"
if self.instruction_type == InstructionType.UNIT_PRODUCTION:
return f"train {self.type_id}"

class BuildOrder:

def __init__(self, bot:BotAI):
self.bot = bot

@property
def step(self) -> int:
return 0

@property
def constructed_structures(self) -> List[UnitTypeId]:
return []

@property
def opponent_builds_air(self) -> bool:
return False

@opponent_builds_air.setter
def opponent_builds_air(self, status:bool) -> None:
self.opponent_builds_air = status

@property
def opponent_uses_cloak(self) -> bool:
return False

@opponent_uses_cloak.setter
def opponent_uses_cloak(self, status:bool) -> None:
self.opponent_uses_cloak = status

def increment_step(self) -> None:
self.step += 1

def add_constructed_structure(self, structure:UnitTypeId) -> None:
self.constructed_structures.append(structure)

def get_build_pos(self) -> Union[Point2, Point3, Unit]:
# First Pylon Position
if not self.bot.structures(UnitTypeId.PYLON):
return self.bot.main_base_ramp.protoss_wall_pylon
if self.bot.structures(UnitTypeId.PYLON):
minerals:Units = self.bot.expansion_locations_dict[self.bot.start_location].mineral_field
return self.bot.start_location.towards(self.bot.start_location.furthest(minerals).position, 10)
return self.bot.start_location.towards(self.bot.game_info.map_center,2)

async def update(self):
# Does only be set once
if not self.opponent_builds_air:
if self.bot.enemy_units.filter(lambda unit: unit.is_flying \
and not unit.can_attack):
self.opponent_builds_air = True
await self.bot.chat_send("I see you got an AirForce, i can do that too")

# Does only be set once
if not self.opponent_uses_cloak:
if self.bot.enemy_units\
.filter(lambda unit: unit.is_cloaked and unit.can_attack \
or unit.is_burrowed and unit.can_attack):
self.opponent_uses_cloak = True
await self.bot.chat_send("Stop hiding and fight like a honorable ... ähm... Robot?\ndo computers have honor ?")

self.debug_build_pos(self.get_build_pos())

def debug_build_pos(self, pos:Union[Point2, Point3]):
z = self.bot.get_terrain_z_height(pos)+1
x,y = pos.x, pos.y
pos_3d = Point3((x,y,z))
self.bot.client.debug_sphere_out(pos_3d ,2, (255,255,0))

def next_instruction(self) -> BuildInstruction:
return BuildInstruction(UnitTypeId.PYLON, self.get_build_pos())
76 changes: 26 additions & 50 deletions bot/HarstemsAunt/macro.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
from sc2.ids.unit_typeid import UnitTypeId

from .utils import Utils
from .common import INITIAL_TECH,UNIT_COMPOSIOTION
from .build_order import BuildOrder, BuildInstruction
from .common import INITIAL_TECH,UNIT_COMPOSIOTION,logger

async def warp_in_unit(bot: BotAI,unit:UnitTypeId,warp_in_position:Union[Point2,Point3,Unit]) -> bool:
async def warp_in_unit(bot: BotAI,unit:UnitTypeId,\
warp_in_position:Union[Point2,Point3,Unit]) -> bool:
pos:Point2= warp_in_position.position.to2.random_on_distance(4)
placement = await bot.find_placement(AbilityId.WARPGATETRAIN_STALKER, pos, placement_step=1)

Expand Down Expand Up @@ -51,6 +53,9 @@ def __init__(self,bot:BotAI) -> None:
self.temp:list = []
self.mined_out_bases: list = []

# Move to Bot_class
self.build_order:BuildOrder = BuildOrder(self.bot)

@property
def unit_composition(self) -> list:
return UNIT_COMPOSIOTION.get(self.bot.race)
Expand All @@ -66,48 +71,25 @@ def base_count(self) -> int:
async def __call__(self):
await self.chronoboost()
self.get_upgrades()
await self.build_infrastructure()

await self.build_order.update()

if self.bot.alert:
self.handle_alerts(self.bot.alert)
if self.bot.units(UnitTypeId.CYBERNETICSCORE) or self.bot.already_pending(UnitTypeId.CYBERNETICSCORE):
await expand(self.bot)
await self.expand()
self.build_probes()

def get_build_pos(self) -> Union[Point2, Point3, Unit]:
return Point2((0,0))

def get_build_worker(self) -> Unit:
return self.bot.workers.closest_to(self.get_build_pos())
return self.bot.workers.closest_to(self.build_order.get_build_pos())

async def build_infrastructure(self):
enemy_race: Race = self.bot.enemy_race
tech_buildings: list = INITIAL_TECH.get(enemy_race)
tech_0: UnitTypeId = tech_buildings[0]
tech_1: UnitTypeId = tech_buildings[1]

build_pos = self.get_build_pos()
worker = self.get_build_worker()
bot = self.bot

if not bot.structures(UnitTypeId.PYLON) and can_build_structure(bot, UnitTypeId.PYLON):
await bot.build(UnitTypeId.PYLON, build_worker=worker, near=build_pos, max_distance=0)
if not bot.structures(UnitTypeId.GATEWAY) and not bot.structures(UnitTypeId.WARPGATE) \
and can_build_structure(bot, UnitTypeId.GATEWAY):
await bot.build(UnitTypeId.GATEWAY, build_worker=worker, near=build_pos)
return
if not bot.structures(UnitTypeId.CYBERNETICSCORE) and can_build_structure(bot, UnitTypeId.CYBERNETICSCORE) \
and not bot.already_pending(UnitTypeId.CYBERNETICSCORE):
await bot.build(UnitTypeId.CYBERNETICSCORE, build_worker=worker, near=build_pos)
return
if (len(bot.structures(UnitTypeId.GATEWAY)) + len(bot.structures(UnitTypeId.WARPGATE))) < 2 and can_build_structure(bot, UnitTypeId.GATEWAY)\
and bot.structures(tech_0):
await bot.build(UnitTypeId.GATEWAY, build_worker=worker, near=build_pos)
if not bot.structures(tech_0) and can_build_structure(bot, tech_0) and not bot.already_pending(tech_0):
await bot.build(tech_0, build_worker=worker, near=build_pos)
if bot.structures(tech_0) and can_build_structure(bot, tech_1) and not bot.structures(tech_1):
await bot.build(tech_1, build_worker=worker, near=build_pos)
if (len(bot.structures(UnitTypeId.GATEWAY)) + len(bot.structures(UnitTypeId.WARPGATE))) < 2 and can_build_structure(bot, UnitTypeId.GATEWAY)\
and bot.structures(tech_1):
await bot.build(UnitTypeId.GATEWAY, build_worker=worker, near=build_pos)

next_step: BuildInstruction = self.build_order.next_instruction()
logger.info(next_step)
if Utils.can_build_structure(self.bot, next_step.structure):
await self.bot.build(next_step.structure,near=next_step.position,max_distance=0,build_worker=self.get_build_worker())

def get_upgrades(self) -> None:
attack = [
Expand All @@ -122,8 +104,9 @@ def get_upgrades(self) -> None:
UpgradeId.PROTOSSGROUNDARMORSLEVEL3
]

# THIS IS A INNER FUNCTION DON'T CHANGE THE INDENTATION AGAIN BECAUSE YOU GOT THE MEMORY OF A GOLDFISH
def upgrade(bot:BotAI, Upgrade_structure:UnitTypeId, Upgrade_id:UpgradeId) -> None:
if bot.structures(Upgrade_structure).idle and can_research_upgrade(bot,Upgrade_id):
if bot.structures(Upgrade_structure).idle and Utils.can_research_upgrade(bot,Upgrade_id):
bot.research(Upgrade_id)

for forge in self.bot.structures(UnitTypeId.FORGE):
Expand Down Expand Up @@ -196,17 +179,12 @@ async def build_army(self) -> None:
async def build_supply(self) -> None:
if not self.bot.can_afford(UnitTypeId.PYLON) or self.bot.supply_cap == 200:
return
if can_build_structure(self.bot,UnitTypeId.PYLON) and not \
if Utils.can_build_structure(self.bot,UnitTypeId.PYLON) and not \
self.bot.already_pending(UnitTypeId.PYLON) and self.bot.supply_left < 8 \
and len(self.bot.structures(UnitTypeId.NEXUS))>= 2 and self.bot.structures(UnitTypeId.CYBERNETICSCORE):
worker:Unit = self.bot.workers.prefer_idle.closest_to(self.get_build_pos())
await self.bot.build(UnitTypeId.PYLON, build_worker=worker, near=self.get_build_pos())

async def build_structure(self,structure:UnitTypeId,build_pos:Union[Point2,Point3,Unit],worker:Unit) -> None:
bot:BotAI = self.bot
if can_build_structure(bot, structure):
await bot.build(structure,near=build_pos,build_worker=worker)

async def build_gas(self,nexus:Unit) -> None:
bot:BotAI = self.bot
vespene: Units = bot.vespene_geyser.closer_than(12, nexus)[0]
Expand All @@ -217,11 +195,9 @@ async def build_gas(self,nexus:Unit) -> None:
worker.build_gas(vespene)

async def take_gas(self) -> None:
if self.bot.townhall.is_ready and self.bot.structures(UnitTypeId.PYLON) \
and self.bot.structures(UnitTypeId.GATEWAY) and\
len(self.bot.structures(UnitTypeId.ASSIMILATOR)) < len(self.bot.structures(UnitTypeId.NEXUS).ready)*2 \
and not self.bot.already_pending(UnitTypeId.ASSIMILATOR):
await build_gas(self.bot, self.townhall)
#TODO: This needs to be changed
for townhall in self.bot.townhalls:
pass

def check_mined_out(self) -> None:
for townhall in self.bot.townhalls:
Expand All @@ -238,8 +214,8 @@ def check_mined_out(self) -> None:
def build_probes(self) -> None:
probe_count:int = len(self.bot.structures(UnitTypeId.NEXUS))*16 + len(self.bot.structures(UnitTypeId.ASSIMILATOR))*3
if self.bot.structures(UnitTypeId.PYLON):
for townhall in self.bot.townhalls.is_idle:
if can_build_unit(self.bot, UnitTypeId.PROBE) and len(self.bot.workers) < probe_count:
for townhall in self.bot.units(UnitTypeId.NEXUS).idle:
if Utils.can_build_unit(self.bot, UnitTypeId.PROBE) and len(self.bot.workers) < probe_count:
townhall.train(UnitTypeId.PROBE)

async def chronoboost(self) -> None:
Expand Down

0 comments on commit bf1dd0a

Please sign in to comment.