def __best_building_site_farm(self, ai_stat: AI_GameStatus) -> BuildOption:
        """building sites get scored by how many buildable fields there are next to it"""
        best_score = -1
        best_site = (-1, -1)
        best_cd: Optional[List[CardinalDirection]] = None
        best_tl = None
        fields = []
        p = Priority.P_NO
        is_next_to_res = False  # just for printing
        if ai_stat.me.resources >= ai_stat.cost_building_construction[BuildingType.FARM]:
            for ai_t in ai_stat.map.buildable_tiles:
                tmp = False  # just for printing
                possible_fields = []
                score = 0
                for n in essentials.get_neighbours_on_set(ai_t, ai_stat.map.buildable_tiles):
                    if basic.num_resources_on_adjacent(n) == 0:
                        possible_fields.append(n)
                    if essentials.is_obj_in_list(n, ai_stat.map.scoutable_tiles):
                        score += 1
                score += len(possible_fields)
                amount_of_fields = min(3, len(possible_fields))
                sampled = random.sample(possible_fields, amount_of_fields)
                score += len(essentials.get_neighbours_on_set(ai_t, ai_stat.map.scoutable_tiles)) / 2
                # if build site is next to a resource --> reduce value by 1 for each resource field
                score = score - basic.num_resources_on_adjacent(ai_t)
                # make the score dependent on safety level of region
                #score = score - self.compass.get_threat_level(ai_t).value * 2
                if ai_t in self.danger_zone:
                    score += - 10

                if best_score < score:
                    best_score = score
                    is_next_to_res = tmp
                    best_site = ai_t.offset_coordinates
                    best_cd = self.compass.get_cardinal_direction_obj(ai_t, self.compass.center_tile)
                    best_tl = self.compass.get_threat_level(ai_t)
                    fields.clear()
                    for s in sampled:
                        fields.append(s.offset_coordinates)

        if is_next_to_res:
            debug("The farm will be next to a resource (apparently there is no better spot)")

        # translate score to priority (normalization step)
        if len(fields) >= 3:
            p = Priority.P_HIGH
        elif len(fields) >= 2:
            p = Priority.P_MEDIUM
        elif len(fields) >= 1:
            p = Priority.P_LOW
        # hint(f"                            type eval: {type(p)}")
        return BuildOption(BuildingType.FARM, best_site, fields, p, cardinal_direction=best_cd, threat_level=best_tl)
Example #2
0
def evasive_movement(current_tile: Tile, target_tile: Tile,
                     domain: List[Tile]) -> Tuple[Optional[Tile], int]:
    """
    This movement calculates the next step which maximizes the distance to the target_tile
    Use with method to avoid collision between the entity placed on the current_tile, with a potentially
    hostile entity located on the target_tile

    :param current_tile: tile of the evading entity
    :param target_tile: tile of the hostile entity
    :param domain: the search domain, typically a subset of the walkable tiles
    :return: None, if no path is found, otherwise the next step and the distance of the resulting distance to target
    """
    if not current_tile:
        error("current tile is None")
        return None, -1
    if not target_tile:
        error("target tile is None")
        return None, -1
    longest_path: Tuple[int, Optional[Tile]] = (-1, None)
    for nei in essentials.get_neighbours_on_set(current_tile, domain):
        step, dist = next_step_to_target(nei, target_tile, domain)
        if step:
            if dist > longest_path[0]:
                longest_path = (dist, nei)

    return longest_path[1], longest_path[0]
 def __count_inactive_huts(self, ai_stat) -> int:
     count = 0
     for b in ai_stat.map.building_list:
         if b.type == BuildingType.HUT:
             has_res = False
             for n in essentials.get_neighbours_on_set(b.base_tile, ai_stat.map.discovered_tiles):
                 if n.has_resource():
                     has_res = True
             if not has_res:
                 count += 1
     return count
Example #4
0
 def evaluate_move_recruit_unit(
     self, ai_stat: AI_GameStatus
 ) -> Union[None, RaiseArmyOption, RecruitmentOption]:
     if len(ai_stat.map.army_list) == 0:
         for b in ai_stat.map.building_list:
             nei = essentials.get_neighbours_on_set(
                 b, ai_stat.map.walkable_tiles
             )  # buildable -> to avoid opp armies
             if len(nei) == 0:
                 continue
             x = random.sample(nei, 1)[0]
             return RaiseArmyOption(x.offset_coordinates, Priority.P_MEDIUM)
     for t_u in self.properties['units']:
         if ai_stat.me.population + ai_stat.cost_unit_recruitment[
                 t_u].population <= ai_stat.me.population_limit:
             if ai_stat.me.resources >= ai_stat.cost_unit_recruitment[
                     t_u].resources:
                 if ai_stat.me.culture >= ai_stat.cost_unit_recruitment[
                         t_u].culture:
                     return RecruitmentOption(t_u, Priority.P_MEDIUM)
     return None
Example #5
0
def protective_movement(current_tile: Tile, target_tile: Tile,
                        protected_tile: Tile,
                        domain: List[Tile]) -> Tuple[Optional[Tile], int]:
    """
    If an army/obj chooses to use protective movement, it will stay close to the entity it is protecting
    It will position itself such that it intercepts the incoming hostile entity on the target tile if possible

    :param current_tile: tile of the entity which is protecting (friendly army for instance)
    :param target_tile: tile of the hostile entity
    :param protected_tile: tile of the entity which is to be protected
    :param domain: the search domain, typically a subset of the walkable tiles
    :return: None if there is a problem or no path is found, otherwise the next step and the size of the path
    """
    if not (current_tile and target_tile and protected_tile):
        error("a tile is None")
        return None, -1
    shortest_path: Tuple[int, Optional[Tile]] = (1000, None)
    for nei in essentials.get_neighbours_on_set(protected_tile, domain):
        step, dist = next_step_to_target(nei, target_tile, domain)
        if step:
            if dist < shortest_path[0]:
                shortest_path = (dist, nei)
    return next_step_to_target(current_tile, shortest_path[1], domain)
 def get_army_spawn_loc(self, ai_stat: AI_GameStatus) -> Tuple[int, int]:
     nei: List[Tile] = essentials.get_neighbours_on_set(ai_stat.map.building_list[0].base_tile, ai_stat.map.walkable_tiles)
     idx = random.randint(0, len(nei)-1)
     return nei[idx].offset_coordinates
Example #7
0
    def calculate_army_movement(
            self, ai_stat: AI_GameStatus) -> List[ArmyMovementOption]:
        """
        Villager movement
        In passive state: patrol
        In aggressive state: attack buildings
        In defencive state: protect own village, move to intercept incoming army
        """
        movements: List[ArmyMovementOption] = []
        if len(ai_stat.map.army_list) == 0:
            return movements
        if ai_stat.map.army_list[0].population == 0:
            return movements
        army_tile = ai_stat.map.army_list[0].base_tile
        village_tile = ai_stat.map.building_list[0].base_tile
        # --------------------- Passive movement --------------------
        if self.state is AI_NPC.AI_State.PASSIVE:
            domain = [
                x for x in ai_stat.map.walkable_tiles
                if not x.has_building() and not x.has_army()
            ]
            domain.append(army_tile)
            if not self.patrol_target:
                self.patrol_target = random.sample(domain,
                                                   1)[0].offset_coordinates
            pt = get_tile_by_xy(self.patrol_target,
                                ai_stat.map.discovered_tiles)
            if pt not in domain:
                self._dump(
                    "relocating patrol target, it appears to be blocked")
                pt = random.sample(domain, 1)[0]
            if get_distance(pt, army_tile) > 0:
                self._dump(f"patrol tile: {pt.offset_coordinates}")
                next_step, dist = next_step_to_target(army_tile, pt, domain)
                if next_step:
                    movements.append(
                        ArmyMovementOption(pt, Priority.P_MEDIUM,
                                           next_step.offset_coordinates))
            else:
                self.patrol_target = None

        # --------------------- Defencive movement --------------------
        elif self.state is AI_NPC.AI_State.DEFENSIVE:
            hostile_armies = [
                x for x in ai_stat.map.opp_army_list
                if x.owner in self.hostile_player
            ]
            for h_a in hostile_armies:
                domain_no_buildings = [
                    x for x in ai_stat.map.walkable_tiles
                    if not x.has_building()
                ]
                next_step, dist = protective_movement(army_tile, h_a.base_tile,
                                                      village_tile,
                                                      domain_no_buildings)
                if next_step:
                    movements.append(
                        ArmyMovementOption(self.patrol_target,
                                           Priority.P_MEDIUM,
                                           next_step.offset_coordinates))
            if len(hostile_armies) == 0:
                target = random.sample(
                    get_neighbours_on_set(village_tile,
                                          ai_stat.map.walkable_tiles), 1)[0]
                next_step, dist = next_step_to_target(
                    army_tile, target, ai_stat.map.walkable_tiles)
                if next_step:
                    movements.append(
                        ArmyMovementOption(self.patrol_target,
                                           Priority.P_MEDIUM,
                                           next_step.offset_coordinates))
        # --------------------- Aggressive movement --------------------
        elif self.state is AI_NPC.AI_State.AGGRESSIVE:
            hostile_armies = [
                x for x in ai_stat.map.opp_army_list
                if x.owner in self.hostile_player
            ]
            hostile_buildings = [
                x for x in ai_stat.map.opp_building_list
                if x.owner in self.hostile_player
            ]
            for h_target in list(set().union(hostile_armies,
                                             hostile_buildings)):
                next_step, dist = next_step_to_target(
                    army_tile, h_target.base_tile, ai_stat.map.walkable_tiles)
                if next_step:
                    movements.append(
                        ArmyMovementOption(h_target, Priority.P_MEDIUM,
                                           next_step.offset_coordinates))
        return movements