Esempio n. 1
0
 def get_empty_geysers(self, gas_structs):
     return [
         vg for vg in list_flatten([
             self.vespene_geyser.closer_than(15, th)
             for th in self.townhalls.ready
         ]) if gas_structs.empty or gas_structs.closer_than(1.0, vg).empty
     ]
Esempio n. 2
0
 def unallocated(self, unit_types=None, urgency=Urgency.NONE):
   units = self.units.ready(unit_types) if unit_types else self.units.ready.filter(lambda u: not is_worker(u))
   return units.tags_not_in(list_flatten([
     list(module.allocated) if module.urgency >= urgency
     else []
     for module in self.modules
   ]))
Esempio n. 3
0
    def generate_targets(self):
        # if the situation is anything other than a single base in the main,
        # this *might* be hit once but that scout is going home soon
        base = self.enemy_structures(BaseStructures).first

        def distance_to_enemy(ramp):
            return ramp.top_center.distance_to(base)

        # TODO: figure out ramp better
        likely_main_ramp = min(self.game_info.map_ramps, key=distance_to_enemy)

        def distance_to_ramp(base):
            return base.distance_to(likely_main_ramp.bottom_center)

        possible_naturals = [
            position for position in self.expansion_locations_dict.keys()
            if position.is_further_than(1.0, base.position)
        ]
        likely_natural = min(possible_naturals, key=distance_to_ramp)

        corners = [
            Point2([8, 8]),
            Point2([8, -8]),
            Point2([-8, -8]),
            Point2([-8, 8])
        ]
        self.targets = list_flatten([[pos + base.position for pos in corners],
                                     [likely_natural]])
Esempio n. 4
0
 def _get_large_positions(self, near):
   acceptable_positions = self.plans[near.tag].large_positions if near else list_flatten([ p.large_positions for p in self.plans.values() ])
   return [
     p
     for p in acceptable_positions
     if all(self.bot.has_creep(p + offset) for offset in _3X3_OFFSETS)
     and not self.structures.closer_than(1.0, p).exists
   ]
Esempio n. 5
0
 def _get_non_pylon_positions(self, near):
     acceptable_positions = self.plans[
         near.tag].structure_positions if near else list_flatten(
             [p.structure_positions for p in self.plans.values()])
     return [
         p for p in acceptable_positions
         if self.state.psionic_matrix.covers(p)
         and not self.structures.closer_than(1.0, p).exists
     ]
Esempio n. 6
0
 def _get_small_positions(self):
   existing_structures = [ structure.position for structure in self.structures(_SMALL_STRUCTURES) ]
   acceptable_positions = [
     p
     for p in list_flatten([ p.small_positions for p in self.plans.values() ])
     if p not in existing_structures
     and all(self.bot.has_creep(p + offset) for offset in _2X2_OFFSETS)
   ]
   random.shuffle(acceptable_positions)
   return [p for p in acceptable_positions if not self.structures.closer_than(1.0, p).exists]
Esempio n. 7
0
    def _get_pylon_positions(self):
        base_locations = [nex.position for nex in self.townhalls]
        existing_pylons = [
            pylon.position for pylon in self.structures(UnitTypeId.PYLON)
        ]
        acceptable_positions = [
            p for p in list_flatten(
                [p.pylon_positions for p in self.plans.values()])
            if p not in existing_pylons
        ]

        random.shuffle(acceptable_positions)
        # Move god pylons to the front - minimizes POOR PLANNING issues, and gives all bases pylons
        for i in range(len(acceptable_positions)):
            if any(acceptable_positions[i] - base_location in god_pylons
                   for base_location in base_locations):
                acceptable_positions.insert(0, acceptable_positions.pop(i))
        return [
            p for p in acceptable_positions
            if not self.structures.closer_than(1.0, p).exists
        ]
Esempio n. 8
0
 def get_mineable_nodes(self):
     return list_flatten([
         # intentionally includes expansions in progress
         self.mineral_field.closer_than(15, th) for th in self.townhalls
     ])
Esempio n. 9
0
    def distribute_workers(self):
        # Only do anything once every 3 seconds
        if self.time - self.last_distribute < 3:
            return

        self.last_distribute = self.time

        # Kinda hard to gather anything without a base
        if not self.townhalls.ready.exists:
            return

        # mineral patches near one of our bases
        acceptable_minerals = self.mineral_field.filter(lambda node: any([
            nex.position.is_closer_than(15, node.position)
            for nex in self.townhalls.ready
        ]))

        workers_per_gas = 1 + min(
            2, int(self.workers.amount / acceptable_minerals.amount))

        if self.minerals < 50 and self.vespene > 300:
            workers_per_gas -= 1

        # gas buildings probably at bases that have been destroyed
        bad_geysers = self.structures(
            self.shared.gas_structure).filter(lambda a: all(
                ex.is_further_than(15, a) for ex in self.owned_expansions.keys(
                )) or a.vespene_contents == 0 or a.assigned_harvesters >
                                              workers_per_gas)

        # gas buildings that don't have enough harvesters
        needy_geysers = self.structures(
            self.shared.gas_structure).ready.tags_not_in([
                a.tag for a in bad_geysers
            ]).filter(lambda a: a.assigned_harvesters < workers_per_gas)

        # tag collections for easy selection and matching
        acceptable_mineral_tags = [f.tag for f in acceptable_minerals]
        needy_mineral_tags = [
            f.tag for f in acceptable_minerals
            if self.townhalls.closest_to(f.position).surplus_harvesters < 0
        ]

        # anywhere else is strictly forbidden
        unacceptable_mineral_tags = [
            f.tag
            for f in self.mineral_field.tags_not_in(acceptable_mineral_tags)
        ]

        bad_workers = self.unallocated(self.shared.worker_types).filter(
            lambda p:
            # Grab these suckers first
            p.is_idle or (p.is_gathering and p.orders[0].target in
                          unacceptable_mineral_tags) or
            (p.is_gathering and p.orders[0].target in bad_geysers) or p.orders[
                0].ability.id in [
                    AbilityId.ATTACK_ATTACKTOWARDS, AbilityId.ATTACK_ATTACK,
                    AbilityId.ATTACK
                ])

        # up to N workers, where N is the number of surplus harvesters, from each base where there are any
        # may not grab them all every time (it gets only the ones returning minerals), but it'll get enough
        excess_workers = Units(
            list_flatten([
                self.workers.filter(lambda w: w.is_carrying_minerals and w.
                                    orders and w.orders[0].target == base.tag)
                [0:base.surplus_harvesters] for base in self.townhalls.filter(
                    lambda base: base.surplus_harvesters > 0)
            ]), self)

        # to fill up your first gas building, you'll need these
        mining_workers = self.workers.filter(
            lambda p:
            # if more are needed, this is okay too
            p.is_gathering and (p.orders[0].target in acceptable_mineral_tags
                                or p.is_carrying_minerals)) - (bad_workers +
                                                               excess_workers)

        usable_workers = bad_workers + excess_workers + mining_workers

        taken_workers = 0

        def get_workers(num):
            nonlocal taken_workers
            if taken_workers + num > usable_workers.amount:
                return []
            taken_workers += num
            return usable_workers[taken_workers - num:taken_workers]

        for needy_geyser in needy_geysers:
            workers = get_workers(workers_per_gas -
                                  needy_geyser.assigned_harvesters)
            for worker in workers:
                self.do(worker.gather(needy_geyser))

        if taken_workers < bad_workers.amount and acceptable_mineral_tags:
            remaining_bad_workers = get_workers(bad_workers.amount -
                                                taken_workers)
            for worker in remaining_bad_workers:
                self.do(
                    worker.gather(
                        self.mineral_field.tags_in(
                            acceptable_mineral_tags).random))

        if taken_workers < bad_workers.amount + excess_workers.amount and needy_mineral_tags:
            remaining_excess_workers = get_workers(bad_workers.amount +
                                                   excess_workers.amount -
                                                   taken_workers)
            for worker in remaining_excess_workers:
                self.do(
                    worker.gather(
                        self.mineral_field.tags_in(needy_mineral_tags).random))