コード例 #1
0
    def __best_building_site_barracks(self, ai_stat: AI_GameStatus) -> Tuple[Priority, Tuple[int, int]]:
        """building sites should be a claimed tile and not next to a resource"""
        if ai_stat.me.resources >= ai_stat.cost_building_construction[BuildingType.BARRACKS]:
            candidates = []
            for c in self.claimed_tiles:
                if not c.is_buildable:  # if tile is not buildable, forget it
                    continue
                res_next_to_can = basic.num_resources_on_adjacent(c)

                if res_next_to_can == 0:
                    # very hard constraint (go by value would be better)
                    #if self.compass.get_threat_level(c).value <= ThreatLevel.LOW_RISK.value:
                        # build barracks only in safe zone
                    if c not in self.danger_zone:
                        candidates.append(c)
            if DETAILED_DEBUG:
                debug(f"possible candidates for a barracks: {len(candidates)}")
            if len(candidates) > 0:
                idx = random.randint(0, len(candidates) - 1)
                c = 0
                for e in candidates:
                    if idx == c:
                        return Priority.P_MEDIUM, e.offset_coordinates
                    c = c + 1
        return Priority.P_NO, (-1, -1)
コード例 #2
0
 def __init__(self):
     from src.ai.ai_blueprint import AI
     self.dict_of_ais: Dict[int, AI] = {}
     debug("AI Game interface has been initialized")
     self.__has_finished = False     # protected variable to avoid modifying it, which would lead to RC
     self.ref_to_move: Optional[AI_Move] = None
     self.time_begin = 0
     self.time_end = 0
コード例 #3
0
 def __init__(self, name, other_players_ids: [int]):
     """the name of the ai"""
     self.name = name
     """each ai can do (not required) diplomacy"""
     self.diplomacy: AI_Diplo = AI_Diplo(other_players_ids, self.name)
     """this is used for development.
     instead of printing all AI info to the console, one can use the dump to display stats in-game"""
     self.__dump: str = ""
     debug("AI (" + str(name) + ") is running")
コード例 #4
0
 def stop_animation(self, drawable: Union[Army]):
     for s in self.move_animations:
         s.valid = False
     tbr = None
     for s in self.move_animations:
         if s.drawable.sprite.position == drawable.sprite.position:
             tbr = s
     if tbr:
         tbr.valid = False
         debug("removing drawable from animation")
         self.move_animations.remove(tbr)
コード例 #5
0
    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)
コード例 #6
0
 def evaluate_move_recruitment(self, ai_stat: AI_GameStatus) -> List[Union[RecruitmentOption, RaiseArmyOption]]:
     options = []
     if ai_stat.me.population >= ai_stat.me.population_limit:
         return options
     if len(ai_stat.map.army_list) == 0:
         options.append(AI_Mazedonian.RaiseArmyOption(Priority.P_HIGH))
         return options  # cannot recruit if no army available
     # calculate offset to desired population by build order
     prio_merc = Priority.P_LOW
     prio_knight = Priority.P_LOW
     offset = self.build_order.population - ai_stat.me.population
     if offset > 0:
         prio_merc = Priority.increase(prio_merc)
         prio_knight = Priority.increase(prio_knight)
     else:
         prio_merc = Priority.decrease(prio_merc)
         prio_knight = Priority.decrease(prio_knight)
     # compare to desired army composition:
     army = ai_stat.map.army_list[0]
     if army.population > 0:
         percentage_merc_wanted = self.army_comp.ac[0] / (self.army_comp.ac[0] + self.army_comp.ac[0])
         percentage_merc_actual = army.mercenaries / (army.mercenaries + army.knights)
         percentage_knig_wanted = self.army_comp.ac[1] / (self.army_comp.ac[0] + self.army_comp.ac[0])
         percentage_knig_actual = army.knights / (army.mercenaries + army.knights)
         if DETAILED_DEBUG:
             debug(f"merc: {percentage_merc_wanted} - {percentage_merc_actual} | knight: {percentage_knig_wanted} - {percentage_knig_actual}")
         if (percentage_knig_wanted - percentage_knig_actual) < (percentage_merc_wanted - percentage_merc_actual):
             prio_merc = Priority.increase(prio_merc)
         else:
             prio_knight = Priority.increase(prio_knight)
     else:   # in case the population is 0, we cannot compute the above. Just increase the priority
         prio_knight = Priority.increase(prio_knight)
         prio_merc = Priority.increase(prio_merc)
     # mercenary
     cost_unit_me = ai_stat.cost_unit_recruitment[UnitType.MERCENARY]
     if ai_stat.me.population + cost_unit_me.population <= ai_stat.me.population_limit:
         if ai_stat.me.resources >= cost_unit_me.resources:
             if ai_stat.me.culture >= cost_unit_me.culture:
                 options.append(RecruitmentOption(UnitType.MERCENARY, prio_merc))
             else:
                 if DETAILED_DEBUG:
                     hint("not enough culture to recruit a mercenary. actual: {} required: {}".format(
                         ai_stat.me.culture, cost_unit_me.culture))
         else:
             if DETAILED_DEBUG:
                 hint("not enough resources to recruit a mercenary. actual: {} required: {}".format(
                     ai_stat.me.resources, cost_unit_me.resources))
     else:
         if DETAILED_DEBUG:
             hint("not enough free population to recruit a mercenary")
     # knight
     cost_unit_kn = ai_stat.cost_unit_recruitment[UnitType.KNIGHT]
     if ai_stat.me.population + cost_unit_kn.population <= ai_stat.me.population_limit:
         if ai_stat.me.resources >= cost_unit_kn.resources:
             if ai_stat.me.culture >= cost_unit_kn.culture:
                 options.append(RecruitmentOption(UnitType.KNIGHT, prio_knight))
             else:
                 if DETAILED_DEBUG:
                     hint("not enough culture to recruit a knight. actual: {} required: {}".format(
                         ai_stat.me.culture, cost_unit_kn.culture))
         else:
             if DETAILED_DEBUG:
                 hint("not enough resources to recruit a knight. actual: {} required: {}".format(
                     ai_stat.me.resources, cost_unit_kn.resources))
     else:
         if DETAILED_DEBUG:
             hint("not enough free population to recruit a knight")
     return options
コード例 #7
0
    def evaluate_move_building(self, ai_stat: AI_GameStatus) -> List[BuildOption]:
        def normalize(value: int) -> Priority:
            if value <= 0:
                return Priority.P_NO
            elif value <= 1:
                return Priority.P_LOW
            elif value <= 2:
                return Priority.P_MEDIUM
            elif value <= 4:
                return Priority.P_HIGH
            return Priority.P_CRITICAL

        # get current bo (somewhat a code duplicate, but offset is required)
        current_bo = self.build_order
        offset_to_bo, _ = self.__compare_to_bo(current_bo, ai_stat)

        if current_bo != None:
            # hint("Currently active build order: {}".format(current_bo.name))
            # value_farm = -1
            prio_racks = Priority.P_NO
            value_hut = -1
            # site_farm = (-1, -1)
            farm_opt = BuildOption(BuildingType.FARM, (-1, -1), [], Priority.P_NO)
            racks_opt = BuildOption(BuildingType.BARRACKS, (-1, -1), [], Priority.P_NO)
            hut_opt = BuildOption(BuildingType.HUT, (-1, -1), [], Priority.P_NO)
            site_racks = (-1, -1)
            site_hut = (-1, -1)
            for t, v in offset_to_bo:
                # hint(f"for type: {t}, v: {v}")
                if v > 0:  # only check this building type if it is part of the bo
                    if t == BuildingType.FARM:
                        farm_opt: BuildOption = self.__best_building_site_farm(ai_stat)
                        if v >= 2:
                            if farm_opt.score.value > 0:  # not Priority.P_NO
                                farm_opt.score = Priority.increase(farm_opt.score)
                    elif t == BuildingType.HUT:
                        value_hut, site_hut = self.__best_building_site_hut(ai_stat)
                        hut_opt = BuildOption(BuildingType.HUT, site_hut, [], normalize(value_hut))
                        v = v + self.inactive_huts
                        if v >= 2:
                            if hut_opt.score.value > 0:  # not Priority.P_NO
                                hut_opt.score = Priority.increase(hut_opt.score)
                    elif t == BuildingType.BARRACKS:
                        prio_racks, site_racks = self.__best_building_site_barracks(ai_stat)
                        racks_opt = BuildOption(BuildingType.BARRACKS, site_racks, [], prio_racks)
                        if v >= 2:
                            if prio_racks.value > 0:  # not Priority.P_NO
                                racks_opt.score = Priority.increase(racks_opt.score)
                    else:
                        debug("Build order contains unknown building type -> " + str(t))
            if ai_stat.me.food < self.food_lower_limit:
                if farm_opt is None:  # we force to look for a site even if the BO does not allow for it
                    farm_opt = value_farm, site_farm = self.__best_building_site_farm(ai_stat)

            # if farm_opt:
            #     hint(f"amount of fields {len(farm_opt.associated_tiles)}")

            return [farm_opt, hut_opt, racks_opt]
        else:
            debug("No building order found. This is not supported so far. Need guidance!")
            return []
コード例 #8
0
    def evaluate_state(self, ai_stat: AI_GameStatus):
        old_state = self.state
        if self.state == AI_Mazedonian.AI_State.PASSIVE:
            if len(self.hostile_player) > 0:
                def_count = 0
                agg_count = 0
                for h_p in self.hostile_player:
                    opp_s = self.opponent_strength[h_p]
                    if opp_s == AI_Mazedonian.Strength.EQUAL:
                        if self.equal_strength_defencive:
                            def_count = def_count + 1
                        else:
                            agg_count = agg_count + 1
                    elif opp_s == AI_Mazedonian.Strength.WEAKER:
                        agg_count = agg_count + 1
                    elif opp_s == AI_Mazedonian.Strength.STRONGER:
                        def_count = def_count + 1
                    elif opp_s == AI_Mazedonian.Strength.UNKNOWN:
                        if self.unknown_strength_defencive:
                            def_count = def_count + 1
                        else:
                            agg_count = agg_count + 1
                if def_count > agg_count:
                    self.state = AI_Mazedonian.AI_State.DEFENSIVE
                elif def_count > agg_count:
                    self.state = AI_Mazedonian.AI_State.AGGRESSIVE
                else:
                    if self.tend_to_be_defencive:
                        self.state = AI_Mazedonian.AI_State.DEFENSIVE
                    else:
                        self.state = AI_Mazedonian.AI_State.AGGRESSIVE
        elif self.state == AI_Mazedonian.AI_State.AGGRESSIVE:
            if len(self.hostile_player) == 0:
                self.state = AI_Mazedonian.AI_State.PASSIVE
            if len(self.priolist_targets) == 0:
                self.state = AI_Mazedonian.AI_State.DEFENSIVE

        elif self.state == AI_Mazedonian.AI_State.DEFENSIVE:
            if len(ai_stat.map.building_list) < self.previous_amount_of_buildings:  # we got attacked
                if len(self.hostile_player) == 0:
                    debug("Problem with AI, we got attacked but no hostile players?!")
                else:
                    self.state = AI_Mazedonian.AI_State.AGGRESSIVE
            if len(self.hostile_player) == 0:
                self.state = AI_Mazedonian.AI_State.PASSIVE

        # crusade ?
        if self.protocol is AI_Mazedonian.Protocol.LATE_GAME and self.state != AI_Mazedonian.AI_State.CRUSADE:
            if ai_stat.me.food > 100 and ai_stat.me.population > 20:
                target_player_id = self.diplomacy.get_player_with_lowest_dv()
                if target_player_id != -1:
                    if len(self.priolist_targets) > 0:
                        self.state = AI_Mazedonian.AI_State.CRUSADE
                        self.crusade_target_id = target_player_id
                        self.crusade_time = 15
                else:
                    self._dump("unable to find ID for crusade")

        if self.state is AI_Mazedonian.AI_State.CRUSADE:
            self.crusade_time -= 1
            if self.crusade_time < 0:
                self.state = AI_Mazedonian.AI_State.PASSIVE
                self.crusade_target_id = -1


        self._dump(f"State: {old_state} -> {self.state}")
コード例 #9
0
 def print_active_trades(self):
     debug("Current Trades:")
     for tid, trade in self.trades.items():
         debug(f"ID: {tid}, T: {trade.owner}, {trade.type.name}, {trade.offer}, {trade.demand} LT: {trade.life_time}.")