def handle_trade(trade: Trade, player: Player, player_list: List[Player]) -> bool: if trade.owner == player.id: hint(f"cannot accept own trades -> trade will be deleted [{trade.owner}, [{player.id}]]") return False else: other_player = player_list[trade.owner] if trade.type is TradeType.OFFER: if not TradeHub.balance(trade.demand[0], player, -trade.demand[1]): return False # player could not balance # owner will receive demand, at lose the offer TradeHub.balance(trade.demand[0], other_player, +trade.demand[1]) # current player will receive the offer and play the demand TradeHub.balance(trade.demand[0], player, -trade.demand[1]) TradeHub.balance(trade.offer[0], player, +trade.offer[1]) else: amount = 0 trade_cat = None if trade.type is TradeType.GIFT: amount = trade.offer[1] trade_cat = trade.offer[0] elif trade.type is TradeType.CLAIM: amount = - trade.demand[1] trade_cat = trade.demand[0] TradeHub.balance(trade_cat, player, amount) return True
def __init__(self, map_dim: (int, int), style: MapStyle): hint("HexMap size: {}".format(map_dim)) if style != MapStyle.S_V_C: print("HexMap: Only supported style so far: S_V_C") self.map_dim = map_dim self.map: [Hexagon] = [] # linear storage of all hexagons in the map for y in range(map_dim[1]): for x in range(map_dim[0]): self.map.append(Hexagon((x, y)))
def _dump(self, d: str): """Depending on the game settings, this will either dump the output to: - [if SHOW_AI_CTRL]the external AI ctrl window - [if DEBUG_MODE]the console (should not be the first choice, very slow) - [else]nowhere.""" if Definitions.SHOW_AI_CTRL: self.__dump += d + "\n" elif Definitions.DEBUG_MODE: hint(d) else: pass
def update(self, time): tpl = Animator.bilinear_interpolation( self.source, self.destination, self.start_time_ms, self.start_time_ms + self.time_ms, time) if len(tpl) != 2: error( "Animator: serious error, we left the 2d space! len(tpl): " + str(len(tpl))) hint(str(self.source)) hint(str(self.destination)) # if not (type(tpl) == Tuple): # error("Error in Animator -> bilinear interpolation output: " + str(type(tpl))) if self.valid: self.drawable.set_sprite_pos(tpl, self.camera_pos)
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
def setup_weights(self) -> List[Tuple[Callable, float]]: w: List[Tuple[Callable, float]] = [] def w1(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: If AI looses food -> Make building a farm more important!""" if type(elem) is BuildOption: if elem.type == BuildingType.FARM: return self.is_loosing_food return False w.append((w1, 3)) def w1_1(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: addition to w1: if we are gaining food, make building a farm less important""" if type(elem) is BuildOption: if elem.type == BuildingType.FARM: return not self.is_loosing_food return False w.append((w1_1, -1.5)) def w2(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: If AI looses food, recruitment is halted""" if self.is_loosing_food: if type(elem) is RecruitmentOption or type(elem) is ScoutingOption: return True return False w.append((w2, -5)) def w3(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: If AI has no army -> Recruiting an army is important""" if type(elem) is RaiseArmyOption: if len(ai_stat.map.army_list) == 0: return True return False w.append((w3, 3)) def w4(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea, once we have enough resources (and is in passive/def state), make scouting slightly more important""" if type(elem) is ScoutingOption: if ai_stat.me.resources > 10: if self.state == AI_Mazedonian.AI_State.PASSIVE or self.state == AI_Mazedonian.AI_State.DEFENSIVE: return True return False w.append((w4, 1)) def w5(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: reduce significance of scouting in a low eco game""" if type(elem) is ScoutingOption: if ai_stat.me.resources < 10: return True return False w.append((w5, -1)) def w6(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: If AI has more than 70 food, cut down on additional farms""" if type(elem) is BuildOption: if elem.type == BuildingType.FARM: if ai_stat.me.food > 70: return True return False w.append((w6, -1)) def w7(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: slightly decrease scouting and waiting if a lot of resources are available""" if type(elem) is ScoutingOption or type(elem) is WaitOption: if ai_stat.me.resources > 40: return True return False w.append((w7, -1.5)) def w8(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: slightly decrease scouting in early game""" if type(elem) is ScoutingOption: if self.protocol == AI_Mazedonian.Protocol.EARLY_GAME: return True return False w.append((w8, -1)) def w9(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: slightly increase building in early game""" if type(elem) is BuildOption: if self.protocol == AI_Mazedonian.Protocol.EARLY_GAME: return True return False w.append((w9, 1)) def w10(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: if AI lacks population by twice the desired value -> double down""" if type(elem) is RecruitmentOption: if self.build_order.population / 2 > ai_stat.me.population: return True return False w.append((w10, 0.9)) def w11(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: if AI doesn't have a farm -> highest prio (if it cannot build one -> wait)""" if type(elem) is BuildOption: if elem.type == BuildingType.FARM: for b in ai_stat.map.building_list: if b.type == BuildingType.FARM: return False return True # returns true if AI does not have a farm and building one is an option return False w.append((w11, 10)) def w12(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: extension to w11 (if it cannot build one -> wait)""" if type(elem) is WaitOption: for b in ai_stat.map.building_list: if b.type == BuildingType.FARM: return False return True # returns true if AI does not have a farm return False w.append((w12, 5)) def w13(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: if pop >= pop_limit, make building barracks slightly more popular""" if ai_stat.me.population_limit <= ai_stat.me.population: if type(elem) is BuildOption: if elem.type == BuildingType.BARRACKS: if not has_building_under_construction( BuildingType.BARRACKS, ai_stat): return True if type(elem) is WaitOption: if not has_building_under_construction(BuildingType.BARRACKS, ai_stat): return True return False w.append((w13, 1.7)) def w14(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """during Crusade, build troops""" if type(elem) is RecruitmentOption: if self.state is AI_Mazedonian.AI_State.CRUSADE: return True return False w.append((w14, 5)) def w15(elem: AI_Mazedonian.Option, ai_stat: AI_GameStatus) -> bool: """Idea: Upgrade villa if possible""" "" if type(elem) is UpgradeOption: if self.protocol is AI_Mazedonian.Protocol.LATE_GAME: return True return False w.append((w15, 3)) hint(f"AI has found {len(w)} weight functions.") return w
def setup_movement_weights( self: AI_Mazedonian) -> List[Tuple[Callable, float]]: aw: List[Tuple[Callable, float]] = [] def aw1(elem: AI_Mazedonian.AttackTarget, ai_stat: AI_GameStatus) -> bool: if type(elem.target) == AI_Army: if essentials.is_obj_in_list(elem.target, self.claimed_tiles): return True return False aw.append((aw1, 2)) def aw2(elem: AI_Mazedonian.AttackTarget, ai_stat: AI_GameStatus) -> bool: if type(elem.target) == AI_Army: if self.previous_amount_of_buildings > len( ai_stat.map.building_list): return True return False aw.append((aw2, 1)) def aw3(elem: AI_Mazedonian.AttackTarget, ai_stat: AI_GameStatus) -> bool: """Idea: reduce aggressifness in opponant is stronger""" if type(elem.target) == AI_Army: if elem.target.owner in self.hostile_player: if self.opponent_strength[ elem.target.owner] == AI_Mazedonian.Strength.STRONGER: return True return False aw.append((aw3, -1)) def aw4(elem: AI_Mazedonian.AttackTarget, ai_stat: AI_GameStatus) -> bool: """Idea: Reduce will to attack in early game, but defend""" if type(elem.target) == AI_Army: if self.protocol == AI_Mazedonian.Protocol.EARLY_GAME: if len(self.hostile_player) == 0: return True return False aw.append((aw4, -2)) def aw5(elem: AI_Mazedonian.AttackTarget, ai_stat: AI_GameStatus) -> bool: """Idea: Move in for the kill if the opp is weaker""" if type(elem.target) == AI_Building: if elem.target.owner in self.hostile_player: if self.opponent_strength[ elem.target.owner] == AI_Mazedonian.Strength.WEAKER: return True return False aw.append((aw5, 2)) def aw6(elem: AI_Mazedonian.AttackTarget, ai_stat: AI_GameStatus) -> bool: """Idea: Defend if attacked by opponent""" if type(elem.target) == AI_Army: if elem.target.owner in self.hostile_player: for opp in ai_stat.opponents: if opp.id == elem.target.owner: if opp.has_attacked: return True return False aw.append((aw6, 1)) def aw7(elem: AI_Mazedonian.AttackTarget, ai_stat: AI_GameStatus) -> bool: """Idea: during crusade, attack target player""" if self.state is AI_Mazedonian.AI_State.CRUSADE: if elem.target.owner == self.crusade_target_id: return True return False aw.append((aw7, 5)) def aw8(elem: AI_Mazedonian.AttackTarget, ai_stat: AI_GameStatus) -> bool: """Keep the previous attack target for static targets""" if self.previous_attack_target is not None: if elem.target.offset_coordinates == self.previous_attack_target.target.offset_coordinates: return True return False aw.append((aw8, 2)) hint(f"AI has found {len(aw)} movement weight functions.") return aw