def is_valid(self): if not super(OrderColonize, self).is_valid(): return False universe = fo.getUniverse() planet = self.target.get_object() sys_partial_vis_turn = universe.getVisibilityTurnsMap(planet.systemID, fo.empireID()).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(planet.id, fo.empireID()).get(fo.visibility.partial, -9999) if not (planet_partial_vis_turn == sys_partial_vis_turn and planet.unowned or (planet.ownedBy(fo.empireID()) and not planet.currentMeterValue(fo.meterType.population))): self.executed = True self.order_issued = True return False else: return self.fleet.get_object().hasColonyShips
def is_valid(self): if not super(OrderOutpost, self).is_valid(): return False universe = fo.getUniverse() planet = self.target.get_object() sys_partial_vis_turn = universe.getVisibilityTurnsMap(planet.systemID, fo.empireID()).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(planet.id, fo.empireID()).get(fo.visibility.partial, -9999) if not (planet_partial_vis_turn == sys_partial_vis_turn and planet.unowned): self.executed = True self.order_issued = True return False else: return self.fleet.get_object().hasOutpostShips
def handle_diplomatic_message(self, message): """Handle a diplomatic message update from the server, such as if another player declares war, accepts peace, or cancels a proposed peace treaty. :param message: message.recipient and message.sender are respective empire IDs :return: """ debug("Received diplomatic %s message from %s to %s." % ( message.type, fo.getEmpire(message.sender), 'me' if message.recipient == fo.empireID() else fo.getEmpire(message.recipient))) # TODO: remove the following early return once proper support for third party diplomatic history is added if message.recipient != fo.empireID(): return aistate = get_aistate() if message.type == fo.diplomaticMessageType.peaceProposal: aistate.log_peace_request(message.sender, message.recipient) proposal_sender_player = fo.empirePlayerID(message.sender) attitude = aistate.character.attitude_to_empire(message.sender, aistate.diplomatic_logs) possible_acknowledgments = [] aggression = aistate.character.get_trait(Aggression) if aggression.key <= fo.aggression.typical: possible_acknowledgments = UserStringList("AI_PEACE_PROPOSAL_ACKNOWLEDGEMENTS_MILD_LIST") if attitude > 0: possible_replies = UserStringList("AI_PEACE_PROPOSAL_RESPONSES_YES_MILD_LIST") else: possible_replies = UserStringList("AI_PEACE_PROPOSAL_RESPONSES_NO_MILD_LIST") else: possible_acknowledgments = UserStringList("AI_PEACE_PROPOSAL_ACKNOWLEDGEMENTS_HARSH_LIST") if attitude > 0: possible_replies = UserStringList("AI_PEACE_PROPOSAL_RESPONSES_YES_HARSH_LIST") else: possible_replies = UserStringList("AI_PEACE_PROPOSAL_RESPONSES_NO_HARSH_LIST") acknowledgement = random.choice(possible_acknowledgments) reply_text = random.choice(possible_replies) debug("Acknowledging proposal with initial message (from %d choices): '%s'" % ( len(possible_acknowledgments), acknowledgement)) fo.sendChatMessage(proposal_sender_player, acknowledgement) if attitude > 0: diplo_reply = fo.diplomaticMessage(message.recipient, message.sender, fo.diplomaticMessageType.acceptPeaceProposal) debug("Sending diplomatic message to empire %s of type %s" % (message.sender, diplo_reply.type)) fo.sendDiplomaticMessage(diplo_reply) debug("sending chat to player %d of empire %d, message body: '%s'" % ( proposal_sender_player, message.sender, reply_text)) fo.sendChatMessage(proposal_sender_player, reply_text) elif message.type == fo.diplomaticMessageType.warDeclaration: # note: apparently this is currently (normally?) sent not as a warDeclaration, # but as a simple diplomatic_status_update to war aistate.log_war_declaration(message.sender, message.recipient)
def declare_war_on_all(): # pylint: disable=invalid-name """Used to declare war on all other empires (at start of game)""" my_emp_id = fo.empireID() for emp_id in fo.allEmpireIDs(): if emp_id != my_emp_id: msg = fo.diplomaticMessage(my_emp_id, emp_id, fo.diplomaticMessageType.warDeclaration) fo.sendDiplomaticMessage(msg)
def getAIFleetOrdersFromSystemAITargets(fleetAITarget, aiTargets): result = [] # TODO: use Graph Theory to construct move orders # TODO: add priority empireID = fo.empireID() # determine system where fleet will be or where is if is going nowhere lastSystemAITarget = fleetAITarget.getRequiredSystemAITargets()[0] # for every system which fleet wanted to visit, determine systems to visit and create move orders for aiTarget in aiTargets: # determine systems required to visit(with possible return to supplied system) systemAITargets = canTravelToSystemAndReturnToResupply( fleetAITarget.getTargetID(), lastSystemAITarget, aiTarget, empireID ) if len(systemAITargets) > 0: # for every system required to visit create move order for systemAITarget in systemAITargets: # remember last system which will be visited lastSystemAITarget = systemAITarget # create move order aiFleetOrder = AIFleetOrder.AIFleetOrder(AIFleetOrderType.ORDER_MOVE, fleetAITarget, systemAITarget) result.append(aiFleetOrder) else: print "fleetID: " + str(fleetAITarget.getTargetID()) + " can't travel to target:" + str(aiTarget) return result
def issue_order(self): if not super(OrderMilitary, self).issue_order(): return False target_sys_id = self.target.id fleet = self.target.get_object() system_status = get_aistate().systemStatus.get(target_sys_id, {}) total_threat = sum(system_status.get(threat, 0) for threat in ("fleetThreat", "planetThreat", "monsterThreat")) combat_trigger = system_status.get("fleetThreat", 0) or system_status.get("monsterThreat", 0) if not combat_trigger and system_status.get("planetThreat", 0): universe = fo.getUniverse() system = universe.getSystem(target_sys_id) for planet_id in system.planetIDs: planet = universe.getPlanet(planet_id) if planet.ownedBy(fo.empireID()): # TODO: also exclude at-peace planets continue if planet.unowned and not EspionageAI.colony_detectable_by_empire(planet_id, empire=fo.empireID()): continue if sum( [ planet.currentMeterValue(meter_type) for meter_type in [fo.meterType.defense, fo.meterType.shield, fo.meterType.construction] ] ): combat_trigger = True break if not all( ( fleet, fleet.systemID == target_sys_id, system_status.get("currently_visible", False), not (total_threat and combat_trigger), ) ): self.executed = False return True
def handle_diplomatic_status_update(self, status_update): """Handle an update about the diplomatic status between players, which may or may not include this player.""" print "Received diplomatic status update to %s about empire %s and empire %s" % ( status_update.status, status_update.empire1, status_update.empire2) if status_update.empire2 == fo.empireID() and status_update.status == fo.diplomaticStatus.war: foAI.foAIstate.log_war_declaration(status_update.empire1, status_update.empire2)
def attitude_to_empire(self, other_empire_id, diplomatic_logs): # TODO: In other traits consider proximity, competitive # needs, relations with other empires, past history with this # empire, etc. # in the meantime, somewhat random # TODO: Move the diplomatic log portion of this trait back # into diplomacy where it belongs. if self.aggression == fo.aggression.maniacal: return -9 if self.aggression == fo.aggression.beginner: return 9 log_index = (other_empire_id, fo.empireID()) num_alliance_requests = len( diplomatic_logs.get('alliance_requests', {}).get(log_index, [])) num_peace_requests = len( diplomatic_logs.get('peace_requests', {}).get(log_index, [])) num_war_declarations = len( diplomatic_logs.get('war_declarations', {}).get(log_index, [])) # Too many requests for peace irritate the AI, as do any war declarations irritation = ( self.aggression * (2.0 + num_alliance_requests / 5.0 + num_peace_requests / 10.0 + 2.0 * num_war_declarations) + 0.5) attitude = 10 * random.random() - irritation return min(10, max(-10, attitude))
def split_fleet(fleet_id: int) -> List[int]: """Split a fleet into its ships. :param fleet_id: fleet to be split. :return: New fleets. Empty if couldn't split. """ universe = fo.getUniverse() empire_id = fo.empireID() fleet = universe.getFleet(fleet_id) new_fleets = [] if fleet is None: return [] if not fleet.ownedBy(empire_id): return [] if len(list( fleet.shipIDs)) <= 1: # fleet with only one ship cannot be split return [] ship_ids = list(fleet.shipIDs) aistate = get_aistate() for ship_id in ship_ids[1:]: new_fleet_id = split_ship_from_fleet(fleet_id, ship_id) new_fleets.append(new_fleet_id) aistate.get_fleet_role(fleet_id, force_new=True) aistate.update_fleet_rating(fleet_id) if new_fleets: aistate.ensure_have_fleet_missions(new_fleets) return new_fleets
def get_current_and_max_structure(fleet: int) -> Tuple[float, float]: """Return a 2-tuple of the sums of structure and maxStructure meters of all ships in the fleet.""" universe = fo.getUniverse() destroyed_ids = universe.destroyedObjectIDs(fo.empireID()) fleet = universe.getFleet(fleet) if not fleet: return (0.0, 0.0) ships_cur_health = 0 ships_max_health = 0 for ship_id in fleet.shipIDs: # Check if we have see this ship get destroyed in a different fleet since the last time we saw the subject fleet # this may be redundant with the new fleet assignment check made below, but for its limited scope it may be more # reliable, in that it does not rely on any particular handling of post-destruction stats if ship_id in destroyed_ids: continue this_ship = universe.getShip(ship_id) # check that the ship has not been seen in a new fleet since this current fleet was last observed if not (this_ship and this_ship.fleetID == fleet.id): continue ships_cur_health += this_ship.initialMeterValue(fo.meterType.structure) ships_max_health += this_ship.initialMeterValue( fo.meterType.maxStructure) return ships_cur_health, ships_max_health
def __init__(self): self.have_gas_giant = False self.have_asteroids = False self.have_ruins = False self.have_nest = False self.have_computronium = False self.num_researchers = 0 # population with research focus self.num_industrialists = 0 # population with industry focus empire_id = fo.empireID() universe = fo.getUniverse() for planet_info in _get_planets_info().values(): planet = universe.getPlanet(planet_info.pid) if planet.ownedBy(empire_id): if AIDependencies.ANCIENT_RUINS_SPECIAL in planet.specials: self.have_ruins = True population = planet.currentMeterValue(fo.meterType.population) if population > 0 and AIDependencies.COMPUTRONIUM_SPECIAL in planet.specials: self.have_computronium = True # TODO: Check if species can set research focus if planet.focus == FocusType.FOCUS_INDUSTRY: self.num_industrialists += population elif planet.focus == FocusType.FOCUS_RESEARCH: self.num_researchers += population
def issue_order(self): if not super(OrderMilitary, self).issue_order(): return False target_sys_id = self.target.id fleet = self.target.get_object() system_status = foAI.foAIstate.systemStatus.get(target_sys_id, {}) total_threat = sum(system_status.get(threat, 0) for threat in ('fleetThreat', 'planetThreat', 'monsterThreat')) combat_trigger = system_status.get('fleetThreat', 0) or system_status.get('monsterThreat', 0) if not combat_trigger and system_status.get('planetThreat', 0): universe = fo.getUniverse() system = universe.getSystem(target_sys_id) for planet_id in system.planetIDs: planet = universe.getPlanet(planet_id) if planet.ownedBy(fo.empireID()): # TODO: also exclude at-peace planets continue if planet.unowned and not EspionageAI.colony_detectable_by_empire(planet_id, empire=fo.empireID()): continue if sum([planet.currentMeterValue(meter_type) for meter_type in [fo.meterType.defense, fo.meterType.shield, fo.meterType.construction]]): combat_trigger = True break if not all(( fleet, fleet.systemID == target_sys_id, system_status.get('currently_visible', False), not (total_threat and combat_trigger) )): self.executed = False return True
def getAIFleetOrdersFromSystemAITargets(fleetAITarget, aiTargets): result = [] # TODO: use Graph Theory to construct move orders # TODO: add priority empireID = fo.empireID() # determine system where fleet will be or where is if is going nowhere lastSystemAITarget = fleetAITarget.getRequiredSystemAITargets()[0] # for every system which fleet wanted to visit, determine systems to visit and create move orders for aiTarget in aiTargets: # determine systems required to visit(with possible return to supplied system) #print "checking system targets" systemAITargets = canTravelToSystem(fleetAITarget.getTargetID(), lastSystemAITarget, aiTarget, empireID) #print "making path with %d targets: "%len(systemAITargets) , PlanetUtilsAI.sysNameIDs( [sysTarg. getTargetID() for sysTarg in systemAITargets]) if len(systemAITargets) > 0: # for every system required to visit create move order for systemAITarget in systemAITargets: # remember last system which will be visited lastSystemAITarget = systemAITarget # create move order aiFleetOrder = AIFleetOrder.AIFleetOrder(AIFleetOrderType.ORDER_MOVE, fleetAITarget, systemAITarget) result.append(aiFleetOrder) else: startSysID = lastSystemAITarget.getTargetID() targetSysID = aiTarget.getTargetID() if startSysID != targetSysID: print "fleetID: " + str(fleetAITarget.getTargetID()) + " can't travel to target:" + str(aiTarget) return result
def __update_empire_standard_enemy(self): """Update the empire's standard enemy. The standard enemy is the enemy that is most often seen. """ # TODO: If no current information available, rate against own fighters universe = fo.getUniverse() empire_id = fo.empireID() # assess enemy fleets that may have been momentarily visible (start with dummy entries) dummy_stats = CombatRatingsAI.default_ship_stats().get_stats(hashable=True) cur_e_fighters = Counter() # actual visible enemies old_e_fighters = Counter({dummy_stats: 0}) # destroyed enemies TODO: consider seen but out of sight enemies for fleet_id in universe.fleetIDs: fleet = universe.getFleet(fleet_id) if (not fleet or fleet.empty or fleet.ownedBy(empire_id) or fleet.unowned or not (fleet.hasArmedShips or fleet.hasFighterShips)): continue # track old/dead enemy fighters for rating assessments in case not enough current info ship_stats = CombatRatingsAI.FleetCombatStats(fleet_id).get_ship_stats(hashable=True) dead_fleet = fleet_id in universe.destroyedObjectIDs(empire_id) e_f_dict = old_e_fighters if dead_fleet else cur_e_fighters for stats in ship_stats: # log only ships that are armed if stats[0]: e_f_dict[stats] += 1 e_f_dict = cur_e_fighters or old_e_fighters self.__empire_standard_enemy = sorted([(v, k) for k, v in e_f_dict.items()])[-1][1] self.empire_standard_enemy_rating = self.get_standard_enemy().get_rating()
def getAIFleetOrdersFromSystemAITargets(fleetAITarget, aiTargets): result = [] # TODO: use Graph Theory to construct move orders # TODO: add priority empireID = fo.empireID() # determine system where fleet will be or where is if is going nowhere lastSystemAITarget = fleetAITarget.get_required_system_ai_targets()[0] secure_targets = set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs) # for every system which fleet wanted to visit, determine systems to visit and create move orders for aiTarget in aiTargets: # determine systems required to visit(with possible return to supplied system) #print "checking system targets" ensure_return = aiTarget.target_id not in secure_targets systemAITargets = canTravelToSystem(fleetAITarget.target_id, lastSystemAITarget, aiTarget, empireID, ensure_return=ensure_return) #print "making path with %d targets: "%len(systemAITargets) , PlanetUtilsAI.sysNameIDs( [sysTarg.target_id for sysTarg in systemAITargets]) if len(systemAITargets) > 0: # for every system required to visit create move order for systemAITarget in systemAITargets: # remember last system which will be visited lastSystemAITarget = systemAITarget # create move order aiFleetOrder = AIFleetOrder.AIFleetOrder(AIFleetOrderType.ORDER_MOVE, fleetAITarget, systemAITarget) result.append(aiFleetOrder) else: startSysID = lastSystemAITarget.target_id targetSysID = aiTarget.target_id if startSysID != targetSysID: print "fleetID: " + str(fleetAITarget.target_id) + " can't travel to target:" + str(aiTarget) return result
def assign_invasion_values(planet_ids): """Creates a dictionary that takes planet_ids as key and their invasion score as value.""" empire_id = fo.empireID() planet_values = {} neighbor_values = {} neighbor_val_ratio = .95 universe = fo.getUniverse() secure_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types([MissionType.SECURE]) for pid in planet_ids: planet_values[pid] = neighbor_values.setdefault(pid, evaluate_invasion_planet(pid, secure_missions)) print "planet %d, values %s" % (pid, planet_values[pid]) planet = universe.getPlanet(pid) species_name = (planet and planet.speciesName) or "" species = fo.getSpecies(species_name) if species and species.canProduceShips: system = universe.getSystem(planet.systemID) if not system: continue planet_industries = {} for pid2 in system.planetIDs: planet2 = universe.getPlanet(pid2) species_name2 = (planet2 and planet2.speciesName) or "" species2 = fo.getSpecies(species_name2) if species2 and species2.canProduceShips: planet_industries[pid2] = planet2.currentMeterValue(fo.meterType.industry) + 0.1 # to prevent divide-by-zero industry_ratio = planet_industries[pid] / max(planet_industries.values()) for pid2 in system.planetIDs: if pid2 == pid: continue planet2 = universe.getPlanet(pid2) if planet2 and (planet2.owner != empire_id) and ((planet2.owner != -1) or (planet.currentMeterValue(fo.meterType.population) > 0)): # TODO check for allies planet_values[pid][0] += industry_ratio * neighbor_val_ratio * (neighbor_values.setdefault(pid2, evaluate_invasion_planet(pid2, secure_missions))[0]) return planet_values
def merge_fleet_a_into_b(fleet_a_id, fleet_b_id, leave_rating=0, need_rating=0, context=""): universe = fo.getUniverse() fleet_a = universe.getFleet(fleet_a_id) fleet_b = universe.getFleet(fleet_b_id) if not fleet_a or not fleet_b: return 0 system_id = fleet_a.systemID if fleet_b.systemID != system_id: return 0 remaining_rating = CombatRatingsAI.get_fleet_rating(fleet_a_id) transferred_rating = 0 for ship_id in fleet_a.shipIDs: this_ship = universe.getShip(ship_id) if not this_ship: continue this_rating = CombatRatingsAI.ShipCombatStats(ship_id).get_rating() remaining_rating = CombatRatingsAI.rating_needed(remaining_rating, this_rating) if remaining_rating < leave_rating: # merging this would leave old fleet under minimum rating, try other ships. continue transferred = fo.issueFleetTransferOrder(ship_id, fleet_b_id) if transferred: transferred_rating = CombatRatingsAI.combine_ratings(transferred_rating, this_rating) else: print " *** transfer of ship %4d, formerly of fleet %4d, into fleet %4d failed; %s" % ( ship_id, fleet_a_id, fleet_b_id, (" context is %s" % context) if context else "") if need_rating != 0 and need_rating <= transferred_rating: break fleet_a = universe.getFleet(fleet_a_id) if not fleet_a or fleet_a.empty or fleet_a_id in universe.destroyedObjectIDs(fo.empireID()): foAI.foAIstate.delete_fleet_info(fleet_a_id) foAI.foAIstate.update_fleet_rating(fleet_b_id)
def get_current_and_max_structure(fleet): """Return a 2-tuple of the sums of structure and maxStructure meters of all ships in the fleet :param fleet: :type fleet: int | universe_object.Fleet | fo.Fleet :return: tuple of sums of structure and maxStructure meters of all ships in the fleet :rtype: (float, float) """ universe = fo.getUniverse() destroyed_ids = universe.destroyedObjectIDs(fo.empireID()) if isinstance(fleet, int): fleet = universe.getFleet(fleet) elif isinstance(fleet, Fleet): fleet = fleet.get_object() if not fleet: return (0.0, 0.0) ships_cur_health = 0 ships_max_health = 0 for ship_id in fleet.shipIDs: # Check if we have see this ship get destroyed in a different fleet since the last time we saw the subject fleet # this may be redundant with the new fleet assignment check made below, but for its limited scope it may be more # reliable, in that it does not rely on any particular handling of post-destruction stats if ship_id in destroyed_ids: continue this_ship = universe.getShip(ship_id) # check that the ship has not been seen in a new fleet since this current fleet was last observed if not (this_ship and this_ship.fleetID == fleet.id): continue ships_cur_health += this_ship.initialMeterValue(fo.meterType.structure) ships_max_health += this_ship.initialMeterValue(fo.meterType.maxStructure) return ships_cur_health, ships_max_health
def __cleanExplorableSystems(self, startSystemID): "cleanup of all explorable systems" universe = fo.getUniverse() systemIDs = universe.systemIDs empireID = fo.empireID() empire = fo.getEmpire() for systemID in systemIDs: system = universe.getSystem(systemID) if not system: continue #print "system with id: " + str(systemID) if (empire.hasExploredSystem(systemID)): self.addExplorableSystem(AIExplorableSystemType.EXPLORABLE_SYSTEM_EXPLORED, systemID) self.removeExplorableSystem(AIExplorableSystemType.EXPLORABLE_SYSTEM_UNEXPLORED, systemID) #print " has been explored" continue if (startSystemID == -1 or not universe.systemsConnected(systemID, startSystemID, empireID)): for explorableSystemsType in EnumsAI.getAIExplorableSystemTypes(): self.removeExplorableSystem(explorableSystemsType, systemID) #print " is not connected to system with id: " + str(startSystemID) continue explorableSystemsType = self.getExplorableSystem(systemID) if (explorableSystemsType == AIExplorableSystemType.EXPLORABLE_SYSTEM_VISIBLE): #print " is already explored system target" continue #print " is now an unexplored system" self.addExplorableSystem(AIExplorableSystemType.EXPLORABLE_SYSTEM_UNEXPLORED, systemID)
def splitFleet(fleetID): "splits a fleet into its ships" universe = fo.getUniverse() empireID = fo.empireID() fleet = universe.getFleet(fleetID) newfleets = [] if fleet == None: return [] if not fleet.ownedBy(empireID): return [] if len(list(fleet.shipIDs)) <= 1: # fleet with only one ship cannot be split return [fleetID] shipIDs = list( fleet.shipIDs ) for shipID in shipIDs[1:]: newFleetID = fo.issueNewFleetOrder("Fleet %d"%(shipID), shipID) if newFleetID: newFleet=universe.getFleet(newFleetID) if not newFleet: print "Error: newly split fleet %d not available from universe"%newFleetID fo.issueRenameOrder(newFleetID, "Fleet %5d"%newFleetID) #to ease review of debugging logs fo.issueAggressionOrder(newFleetID, True) role = foAI.foAIstate.getFleetRole(newFleetID) #and mission? foAI.foAIstate.getRating(newFleetID) # newfleets.append(newFleetID) foAI.foAIstate.newlySplitFleets[newFleetID]=True else: print "Error - got no fleet ID back after trying to split a ship from fleet %d"%fleetID foAI.foAIstate.getFleetRole(fleetID, forceNew=True) # foAI.foAIstate.updateFleetRating(fleetID) # foAI.foAIstate.ensureHaveFleetMissions(newfleets) return newfleets
def calculateColonisationPriority(): """calculates the demand for colony ships by colonisable planets""" global allottedColonyTargets, colony_growth_barrier enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() total_pp = fo.getEmpire().productionPoints num_colonies = len(list(AIstate.popCtrIDs)) # significant growth barrier for low aggression, negligible for high aggression colony_growth_barrier = 2 + ( (0.5 + foAI.foAIstate.aggression)**2) * fo.currentTurn() / 50.0 colonyCost = AIDependencies.COLONY_POD_COST * ( 1 + AIDependencies.COLONY_POD_UPKEEP * num_colonies) turnsToBuild = 8 # TODO: check for susp anim pods, build time 10 mil_prio = foAI.foAIstate.get_priority( EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY) allottedPortion = ([[[0.6, 0.8], [0.3, 0.4]], [[0.8, 0.9], [ 0.3, 0.4 ]]][galaxy_is_sparse][any(enemies_sighted)][fo.empireID() % 2]) #if ( foAI.foAIstate.get_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_COLONISATION) # > 2 * foAI.foAIstate.get_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY)): # allottedPortion *= 1.5 if mil_prio < 100: allottedPortion *= 2 elif mil_prio < 200: allottedPortion *= 1.5 elif fo.currentTurn() > 100: allottedPortion *= 0.75**(num_colonies / 10.0) #allottedColonyTargets = 1+ int(fo.currentTurn()/50) allottedColonyTargets = 1 + int( total_pp * turnsToBuild * allottedPortion / colonyCost) outpost_prio = foAI.foAIstate.get_priority( EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_OUTPOST) # if have any outposts to build, don't build colony ships TODO: make more complex assessment if outpost_prio > 0 or num_colonies > colony_growth_barrier: return 0.0 if num_colonies > colony_growth_barrier: return 0.0 numColonisablePlanetIDs = len([ pid for (pid, (score, _)) in foAI.foAIstate.colonisablePlanetIDs.items() if score > 60 ][:allottedColonyTargets + 2]) if numColonisablePlanetIDs == 0: return 1 colonyshipIDs = FleetUtilsAI.get_empire_fleet_ids_by_role( EnumsAI.AIFleetMissionType.FLEET_MISSION_COLONISATION) numColonyships = len( FleetUtilsAI.extract_fleet_ids_without_mission_types(colonyshipIDs)) colonisationPriority = 60 * (1 + numColonisablePlanetIDs - numColonyships ) / (numColonisablePlanetIDs + 1) # print # print "Number of Colony Ships : " + str(numColonyships) # print "Number of Colonisable planets : " + str(numColonisablePlanetIDs) # print "Priority for colony ships : " + str(colonisationPriority) if colonisationPriority < 1: return 0 return colonisationPriority
def generateAIFleetOrders(self): "generates AIFleetOrders from fleets targets to accomplish" universe = fo.getUniverse() fleetID = self.target_id fleet = universe.getFleet(fleetID) if (not fleet) or fleet.empty or (fleetID in universe.destroyedObjectIDs(fo.empireID())): #fleet was probably merged into another or was destroyed foAI.foAIstate.deleteFleetInfo(fleetID) return # TODO: priority self.clearAIFleetOrders() systemID = fleet.systemID start_sys_id = [ fleet.nextSystemID, systemID ][ systemID >= 0 ] # if fleet doesn't have any mission, then repair if needed or resupply if is current location not in supplyable system empire = fo.getEmpire() fleetSupplyableSystemIDs = empire.fleetSupplyableSystemIDs #if (not self.hasAnyAIMissionTypes()): ntargets=0 for aiFleetMissionType in self.getAIMissionTypes(): ntargets += len( self.getAITargets(aiFleetMissionType) ) if (ntargets ==0) and (systemID not in set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs) ): if self.need_repair(): repairAIFleetOrder = MoveUtilsAI.getRepairAIFleetOrder(self.getAITarget(), start_sys_id) if repairAIFleetOrder.isValid(): self.appendAIFleetOrder(repairAIFleetOrder) if not(self.getLocationAITarget().target_id in fleetSupplyableSystemIDs): resupplyAIFleetOrder = MoveUtilsAI.getResupplyAIFleetOrder(self.getAITarget(), self.getLocationAITarget()) if resupplyAIFleetOrder.isValid(): self.appendAIFleetOrder(resupplyAIFleetOrder) return #no targets # for some targets fleet has to visit systems and therefore fleet visit them systemAITargets = self.__getRequiredToVisitSystemAITargets() aiFleetOrdersToVisitSystems = MoveUtilsAI.getAIFleetOrdersFromSystemAITargets(self.getAITarget(), systemAITargets) #TODO: if fleet doesn't have enough fuel to get to final target, consider resetting Mission #print "----------------------------------------" #print "*+*+ fleet %d : has fleet action system targets: %s"%(fleetID, [str(obj) for obj in systemAITargets]) #print "----------" #print "*+*+ fleet %d: has movement orders: %s"%(fleetID, [str(obj) for obj in aiFleetOrdersToVisitSystems]) for aiFleetOrder in aiFleetOrdersToVisitSystems: self.appendAIFleetOrder(aiFleetOrder) # if fleet is in some system = fleet.systemID >=0, then also generate system AIFleetOrders if systemID >= 0: # system in where fleet is systemAITarget = AITarget.AITarget(EnumsAI.AITargetType.TARGET_SYSTEM, systemID) # if mission aiTarget has required system where fleet is, then generate aiFleetOrder from this aiTarget aiMissionTypes = self.getAIMissionTypes() # for all targets in all mission types get required systems to visit for aiFleetMissionType in aiMissionTypes: aiTargets = self.getAITargets(aiFleetMissionType) for aiTarget in aiTargets: if systemAITarget in aiTarget.get_required_system_ai_targets(): # from target required to visit get fleet orders to accomplish target aiFleetOrder = self.__getAIFleetOrderFromAITarget(aiFleetMissionType, aiTarget) self.appendAIFleetOrder(aiFleetOrder)
def follow_vis_system_connections(start_system_id, home_system_id): universe = fo.getUniverse() empire_id = fo.empireID() exploration_list = [start_system_id] while exploration_list: cur_system_id = exploration_list.pop() if cur_system_id in graph_flags: continue graph_flags.add(cur_system_id) system = universe.getSystem(cur_system_id) if cur_system_id in foAI.foAIstate.visBorderSystemIDs: pre_vis = "a border system" elif cur_system_id in foAI.foAIstate.visInteriorSystemIDs: pre_vis = "an interior system" else: pre_vis = "an unknown system" system_header = "*** system %s;" % system if fo.currentTurn() < 50: visibility_turn_list = sorted(universe.getVisibilityTurnsMap( cur_system_id, empire_id).items(), key=lambda x: x[0].numerator) visibility_info = ', '.join('%s: %s' % (vis.name, turn) for vis, turn in visibility_turn_list) print "%s previously %s. Visibility per turn: %s " % ( system_header, pre_vis, visibility_info) status_info = [] else: status_info = [system_header] has_been_visible = universe.getVisibilityTurnsMap( cur_system_id, empire_id).get(fo.visibility.partial, 0) > 0 is_connected = universe.systemsConnected(cur_system_id, home_system_id, -1) # self.empire_id) status_info.append(" -- is%s partially visible" % ("" if has_been_visible else " not")) status_info.append(" -- is%s visibly connected to homesystem" % ("" if is_connected else " not")) if has_been_visible: sys_status = foAI.foAIstate.systemStatus.setdefault( cur_system_id, {}) foAI.foAIstate.visInteriorSystemIDs.add(cur_system_id) foAI.foAIstate.visBorderSystemIDs.discard(cur_system_id) neighbors = set( dict_from_map( universe.getSystemNeighborsMap(cur_system_id, empire_id)).keys()) sys_status.setdefault('neighbors', set()).update(neighbors) if neighbors: status_info.append(" -- has neighbors %s" % sorted(neighbors)) for sys_id in neighbors: if sys_id not in foAI.foAIstate.exploredSystemIDs: foAI.foAIstate.unexploredSystemIDs.add(sys_id) if (sys_id not in graph_flags) and ( sys_id not in foAI.foAIstate.visInteriorSystemIDs): foAI.foAIstate.visBorderSystemIDs.add(sys_id) exploration_list.append(sys_id) if fo.currentTurn() < 50: print '\n'.join(status_info) print "----------------------------------------------------------"
def get_research_index(): empire_id = fo.empireID() research_index = empire_id % 2 if foAI.foAIstate.aggression >= fo.aggression.aggressive: # maniacal research_index = 2 + (empire_id % 3) # so indices [2,3,4] elif foAI.foAIstate.aggression >= fo.aggression.typical: research_index += 1 return research_index
def get_partial_visibility_turn(obj_id: int) -> int: """Return the last turn an object had at least partial visibility. :return: Last turn an object had at least partial visibility, -9999 if never """ visibility_turns_map = fo.getUniverse().getVisibilityTurnsMap( obj_id, fo.empireID()) return visibility_turns_map.get(fo.visibility.partial, -9999)
def get_inhabited_planets(self): """ Return frozenset of empire planet ids with species. :rtype: frozenset[int] """ empire_id = fo.empireID() return frozenset(x.pid for x in self.__planet_info.itervalues() if x.owner == empire_id and x.species_name)
def get_inhabited_planets(self): """ Return frozenset of empire planet ids with species. :rtype: frozenset[int] """ empire_id = fo.empireID() return frozenset(x.pid for x in self.__planet_info.values() if x.owner == empire_id and x.species_name)
def _check_abort_mission(self, fleet_order): """ checks if current mission (targeting a planet) should be aborted""" planet_stealthed = False target_is_planet = fleet_order.target and isinstance( fleet_order.target, TargetPlanet) planet = None if target_is_planet: planet = fleet_order.target.get_object() # Check visibility prediction, but if somehow still have current visibility, don't # abort the mission yet if not EspionageAI.colony_detectable_by_empire( planet.id, empire=fo.empireID()): if get_partial_visibility_turn(planet.id) == fo.currentTurn(): debug( "EspionageAI predicts planet id %d to be stealthed" % planet.id + ", but somehow have current visibity anyway, so won't trigger mission abort" ) else: debug( "EspionageAI predicts we can no longer detect %s, will abort mission" % fleet_order.target) planet_stealthed = True if target_is_planet and not planet_stealthed: if isinstance(fleet_order, OrderColonize): if (planet.initialMeterValue(fo.meterType.population) == 0 and (planet.ownedBy(fo.empireID()) or planet.unowned)): return False elif isinstance(fleet_order, OrderOutpost): if planet.unowned: return False elif isinstance(fleet_order, OrderInvade): # TODO add substantive abort check return False else: return False # canceling fleet orders debug(" %s" % fleet_order) debug( "Fleet %d had a target planet that is no longer valid for this mission; aborting." % self.fleet.id) self.clear_fleet_orders() self.clear_target() FleetUtilsAI.split_fleet(self.fleet.id) return True
def colony_detectable_by_empire( planet_id: int, species_name: Optional[str] = None, empire: Union[int, List[int]] = ALL_EMPIRES, future_stealth_bonus: int = 0, default_result: bool = True, ) -> bool: """ Predicts if a planet/colony is/will-be detectable by an empire. The passed empire value can be a single empire ID or a list of empire IDs. If passed ALL_empires, will use the list of all empires. When using a list of empires (or when passed ALL_EMPIRES), the present empire is excluded. To check for the present empire the current empire ID must be passed as a simple int. Ignores current ownership of the planet unless the passed empire value is a simple int matching fo.empireID(), because in most cases we are concerned about (i) visibility to us of a planet we do not own, or (ii) visibility by enemies of a planet we own or expect to own at the time we are making the visibility projection for (even if they may own it right now). The only case where current ownership matters is when we are considering whether to abort a colonization mission, which might be for a planet we already own. :param planet_id: required, the planet of concern :param species_name: will override the existing planet species if provided :param empire: empire ID (or list of empire IDs) whose detection ability is of concern :param future_stealth_bonus: can specify a projected future stealth bonus, such as from a stealth tech :param default_result: generally for offensive assessments should be False, for defensive should be True :return: whether the planet is predicted to be detectable """ # The future_stealth_bonus can be used if the AI knows it has researched techs that would grant a stealth bonus to # the planet once it was colonized/captured planet = fo.getUniverse().getPlanet(planet_id) if not planet: error("Couldn't retrieve planet ID %d." % planet_id) return default_result if species_name is None: species_name = planet.speciesName # in case we are checking about aborting a colonization mission if empire == fo.empireID() and planet.ownedBy(empire): return True # could just check stealth meter, but this approach might allow us to plan ahead a bit even if the planet # is temporarily stealth boosted by temporary effects like ion storm planet_stealth = AIDependencies.BASE_PLANET_STEALTH if planet.specials: planet_stealth += max([AIDependencies.STEALTH_SPECIAL_STRENGTHS.get(_spec, 0) for _spec in planet.specials]) # TODO: check planet buildings for stealth bonuses # if the planet already has an existing stealth special, then the most common situation is that it would be # overlapping with or superseded by the future_stealth_bonus, not additive with it. planet_stealth = max(planet_stealth, AIDependencies.BASE_PLANET_STEALTH + future_stealth_bonus) species_stealth_mod = get_species_tag_value(species_name, Tags.STEALTH) total_stealth = planet_stealth + species_stealth_mod if isinstance(empire, int): empire_detection = get_empire_detection(empire) else: empire_detection = get_max_empire_detection(empire) return total_stealth < empire_detection
def check_mergers(self, context=""): """ Merge local fleets with same mission into this fleet. :param context: Context of the function call for logging purposes :type context: str """ debug("Considering to merge %s", self.__str__()) if self.type not in MERGEABLE_MISSION_TYPES: debug("Mission type does not allow merging.") return if not self.target: debug("Mission has no valid target - do not merge.") return universe = fo.getUniverse() empire_id = fo.empireID() fleet_id = self.fleet.id main_fleet = universe.getFleet(fleet_id) main_fleet_system_id = main_fleet.systemID if main_fleet_system_id == INVALID_ID: debug("Can't merge: fleet in middle of starlane.") return # only merge PROTECT_REGION if there is any threat near target if self.type == MissionType.PROTECT_REGION: neighbor_systems = universe.getImmediateNeighbors( self.target.id, empire_id) if not any( MilitaryAI.get_system_local_threat(sys_id) for sys_id in neighbor_systems): debug("Not merging PROTECT_REGION fleet - no threat nearby.") return destroyed_list = set(universe.destroyedObjectIDs(empire_id)) aistate = get_aistate() system_status = aistate.systemStatus[main_fleet_system_id] other_fleets_here = [ fid for fid in system_status.get('myFleetsAccessible', []) if fid != fleet_id and fid not in destroyed_list and universe.getFleet(fid).ownedBy(empire_id) ] if not other_fleets_here: debug("No other fleets here") return for fid in other_fleets_here: fleet_mission = aistate.get_fleet_mission(fid) if fleet_mission.type != self.type or fleet_mission.target != self.target: debug("Local candidate %s does not have same mission." % fleet_mission) continue FleetUtilsAI.merge_fleet_a_into_b( fid, fleet_id, context="Order %s of mission %s" % (context, self))
def is_valid(self): if not super(OrderColonize, self).is_valid(): return False universe = fo.getUniverse() planet = self.target.get_object() sys_partial_vis_turn = universe.getVisibilityTurnsMap( planet.systemID, fo.empireID()).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap( planet.id, fo.empireID()).get(fo.visibility.partial, -9999) if not (planet_partial_vis_turn == sys_partial_vis_turn and planet.unowned or (planet.ownedBy(fo.empireID()) and not planet.currentMeterValue(fo.meterType.population))): self.executed = True self.order_issued = True return False else: return self.fleet.get_object().hasColonyShips
def handleDiplomaticMessage(message): print "Received diplomatic " + str(message.type) + " message from empire " + str(message.sender) + " to empire " + str(message.recipient) print "my empire id: " + str(fo.empireID()) if (message.type == fo.diplomaticMessageType.peaceProposal and message.recipient == fo.empireID()): replySender = message.recipient replyRecipient = message.sender reply = fo.diplomaticMessage(replySender, replyRecipient, fo.diplomaticMessageType.acceptProposal) print "Sending diplomatic message to empire " + str(replyRecipient) + " of type " + str(reply.type) fo.sendDiplomaticMessage(reply)
def generate_fleet_orders(self): """generates AIFleetOrders from fleets targets to accomplish""" universe = fo.getUniverse() fleet_id = self.fleet.id fleet = universe.getFleet(fleet_id) if (not fleet) or fleet.empty or ( fleet_id in universe.destroyedObjectIDs(fo.empireID()) ): # fleet was probably merged into another or was destroyed foAI.foAIstate.delete_fleet_info(fleet_id) return # TODO: priority self.clear_fleet_orders() system_id = fleet.systemID start_sys_id = [fleet.nextSystemID, system_id][system_id >= 0] # if fleet doesn't have any mission, then repair if needed or resupply if is current location not in supplyable system empire = fo.getEmpire() fleet_supplyable_system_ids = empire.fleetSupplyableSystemIDs # if (not self.hasAnyAIMissionTypes()): if not self.target and (system_id not in set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs)): if self._need_repair(): repair_fleet_order = MoveUtilsAI.get_repair_fleet_order( self.fleet, start_sys_id) if repair_fleet_order.is_valid(): self.orders.append(repair_fleet_order) if fleet.fuel < fleet.maxFuel and self.get_location_target( ).id not in fleet_supplyable_system_ids: resupply_fleet_order = MoveUtilsAI.get_resupply_fleet_order( self.fleet, self.get_location_target()) if resupply_fleet_order.is_valid(): self.orders.append(resupply_fleet_order) return # no targets # for some targets fleet has to visit systems and therefore fleet visit them if self.target: system_targets_required_to_visit = [self.target.get_system()] orders_to_visit_systems = MoveUtilsAI.get_fleet_orders_from_system_targets( self.fleet, system_targets_required_to_visit) # TODO: if fleet doesn't have enough fuel to get to final target, consider resetting Mission for fleet_order in orders_to_visit_systems: self.orders.append(fleet_order) # if fleet is in some system = fleet.system_id >=0, then also generate system AIFleetOrders if system_id >= 0 and self.target: # system in where fleet is system_target = System(system_id) # if mission aiTarget has required system where fleet is, then generate fleet_order from this aiTarget # for all targets in all mission types get required systems to visit if system_target == self.target.get_system(): # from target required to visit get fleet orders to accomplish target fleet_order = self._get_fleet_order_from_target( self.type, self.target) self.orders.append(fleet_order)
def __cleanup_qualifiying_base_targets(self): """Cleanup invalid entries in qualifying base targets.""" universe = fo.getUniverse() empire_id = fo.empireID() for dct in [self.qualifyingTroopBaseTargets]: for pid in dct.keys(): planet = universe.getPlanet(pid) if planet and planet.ownedBy(empire_id): del dct[pid]
def get_partial_visibility_turn(obj_id): """Return the last turn an object had at least partial visibility. :type obj_id: int :return: Last turn an object had at least partial visibility, -9999 if never :rtype: int """ visibility_turns_map = fo.getUniverse().getVisibilityTurnsMap(obj_id, fo.empireID()) return visibility_turns_map.get(fo.visibility.partial, -9999)
def generate_fleet_orders(self): """generates AIFleetOrders from fleets targets to accomplish""" universe = fo.getUniverse() fleet_id = self.fleet.id fleet = universe.getFleet(fleet_id) if (not fleet) or fleet.empty or (fleet_id in universe.destroyedObjectIDs( fo.empireID())): # fleet was probably merged into another or was destroyed foAI.foAIstate.delete_fleet_info(fleet_id) return # TODO: priority self.clear_fleet_orders() system_id = fleet.systemID start_sys_id = [fleet.nextSystemID, system_id][system_id >= 0] # if fleet doesn't have any mission, # then repair if needed or resupply if is current location not in supplyable system empire = fo.getEmpire() fleet_supplyable_system_ids = empire.fleetSupplyableSystemIDs # if (not self.hasAnyAIMissionTypes()): if not self.target and (system_id not in set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs)): if self._need_repair(): repair_fleet_order = MoveUtilsAI.get_repair_fleet_order( self.fleet, start_sys_id) if repair_fleet_order and repair_fleet_order.is_valid(): self.orders.append(repair_fleet_order) cur_fighter_capacity, max_fighter_capacity = FleetUtilsAI.get_fighter_capacity_of_fleet( fleet_id) if (fleet.fuel < fleet.maxFuel or cur_fighter_capacity < max_fighter_capacity and self.get_location_target().id not in fleet_supplyable_system_ids): resupply_fleet_order = MoveUtilsAI.get_resupply_fleet_order( self.fleet, self.get_location_target()) if resupply_fleet_order.is_valid(): self.orders.append(resupply_fleet_order) return # no targets if self.target: # for some targets fleet has to visit systems and therefore fleet visit them system_to_visit = self.target.get_system() orders_to_visit_systems = MoveUtilsAI.create_move_orders_to_system( self.fleet, system_to_visit) # TODO: if fleet doesn't have enough fuel to get to final target, consider resetting Mission for fleet_order in orders_to_visit_systems: self.orders.append(fleet_order) # also generate appropriate final orders fleet_order = self._get_fleet_order_from_target( self.type, self.target) self.orders.append(fleet_order)
def merge_fleet_a_into_b(fleetA_ID, fleetB_ID, leaveRating=0, needRating=0, context=""): universe = fo.getUniverse() fleetA = universe.getFleet(fleetA_ID) sysID=fleetA.systemID fleetB = universe.getFleet(fleetB_ID) if not fleetA or not fleetB: return 0 success = True initRating = foAI.foAIstate.get_rating(fleetA_ID) remainingRating = initRating.copy() transferredRating = 0 transferredAttack=0 transferredHealth=0 BHasMonster=False for shipID in fleetB.shipIDs: thisShip=universe.getShip(shipID) if not thisShip: continue if thisShip.isMonster: BHasMonster = True break for shipID in fleetA.shipIDs: thisShip=universe.getShip(shipID) if (not thisShip) or ( thisShip.isMonster != BHasMonster ) : continue stats = foAI.foAIstate.get_design_id_stats(thisShip.designID) thisRating = stats['attack'] * ( stats['structure'] + stats['shields'] ) if (remainingRating['attack'] -stats['attack'])*(remainingRating['health'] -( stats['structure'] + stats['shields'] )) < leaveRating: continue #remainingRating -= thisRating remainingRating['attack'] -= stats['attack'] remainingRating['health'] -= ( stats['structure'] + stats['shields'] ) thisSuccess = ( fo.issueFleetTransferOrder(shipID, fleetB_ID) )#returns a zero if transfer failure if thisSuccess: transferredRating += thisRating transferredAttack += stats['attack'] transferredHealth += ( stats['structure'] + stats['shields'] ) else: print "\t\t\t\t *** attempted transfer of ship %4d, formerly of fleet %4d, into fleet %4d with result %d; %s"%(shipID, fleetA_ID, fleetB_ID, thisSuccess, [" context is %s"%context, ""][context==""]) success = success and thisSuccess if needRating !=0 and needRating <= transferredAttack*transferredHealth: #transferredRating: break fleetA = universe.getFleet(fleetA_ID) if (not fleetA) or fleetA.empty or fleetA_ID in universe.destroyedObjectIDs(fo.empireID()): #print "\t\t\t\t\tdeleting fleet info for old fleet %d after transfers into fleet %d"%(fleetA_ID, fleetB_ID) foAI.foAIstate.delete_fleet_info(fleetA_ID) else: newARating = foAI.foAIstate.update_fleet_rating(fleetA_ID) if success : #and ( newARating==remainingRating) : #print "\t\t\t\t\t\t\%d rating from fleet %d successfully transferred to fleet %d, leaving %d"%(transferredAttack*transferredHealth, fleetA_ID, fleetB_ID, newARating['overall']) pass else: #print "\t\t\t\t\t\t transfer of %d rating from fleet %d to fleet %d was attempted but appears to have had problems, leaving %d"%(transferredAttack*transferredHealth, fleetA_ID, fleetB_ID, newARating['overall']) pass foAI.foAIstate.update_fleet_rating(fleetB_ID) return transferredAttack*transferredHealth, transferredAttack, transferredHealth
def update_explored_systems(): universe = fo.getUniverse() empire = fo.getEmpire() obs_lanes = empire.obstructedStarlanes() obs_lanes_list = [el for el in obs_lanes ] # should result in list of tuples (sys_id1, sys_id2) if obs_lanes_list: print "Obstructed starlanes are: %s" % ', '.join( '%s-%s' % item for item in obs_lanes_list) else: print "No obstructed Starlanes" empire_id = fo.empireID() newly_explored = [] still_unexplored = [] for sys_id in list(foAI.foAIstate.unexploredSystemIDs): if empire.hasExploredSystem( sys_id ): # consider making determination according to visibility rather than actual visit, which I think is what empire.hasExploredSystem covers del foAI.foAIstate.unexploredSystemIDs[sys_id] foAI.foAIstate.exploredSystemIDs[sys_id] = 1 system = universe.getSystem(sys_id) print "Moved system %s from unexplored list to explored list" % system if sys_id in borderUnexploredSystemIDs: del borderUnexploredSystemIDs[sys_id] newly_explored.append(sys_id) else: still_unexplored.append(sys_id) neighbor_list = [] dummy = [] for id_list, next_list in [(newly_explored, neighbor_list), (neighbor_list, dummy)]: for sys_id in id_list: neighbors = list(universe.getImmediateNeighbors(sys_id, empire_id)) all_explored = True for neighbor_id in neighbors: if neighbor_id in foAI.foAIstate.unexploredSystemIDs: # when it matters, unexplored will be smaller than explored all_explored = False else: next_list.append(neighbor_id) if all_explored: interiorExploredSystemIDs[sys_id] = 1 if sys_id in borderExploredSystemIDs: del borderExploredSystemIDs[sys_id] else: borderExploredSystemIDs[sys_id] = 1 for sys_id in still_unexplored: neighbors = list(universe.getImmediateNeighbors(sys_id, empire_id)) any_explored = False for neighbor_id in neighbors: if neighbor_id in foAI.foAIstate.exploredSystemIDs: # consider changing to unexplored test -- when it matters, unexplored will be smaller than explored, but need to not get previously untreated neighbors any_explored = True if any_explored: borderUnexploredSystemIDs[sys_id] = 1 return newly_explored
def is_valid(self): if not super(OrderColonize, self).is_valid(): return False planet = self.target.get_object() if (planet.unowned or planet.ownedBy(fo.empireID())) and not planet.currentMeterValue(fo.meterType.population): return self.fleet.get_object().hasColonyShips # Otherwise, terminate early self.executed = True self.order_issued = True return False
def getRepairAIFleetOrder(fleetAITarget, current_sys_id): "returns repair AIFleetOrder to [nearest safe] drydock" # find nearest supplied system empireID = fo.empireID() drydock_sys_id = getNearestDrydockSystemID(current_sys_id) drydockSystemAITarget = AITarget.AITarget(AITargetType.TARGET_SYSTEM, drydock_sys_id) print "ordering fleet %d to %s for repair"%(fleetAITarget.target_id, PlanetUtilsAI.sysNameIDs([drydock_sys_id])) # create resupply AIFleetOrder aiFleetOrder = AIFleetOrder.AIFleetOrder(AIFleetOrderType.ORDER_REPAIR, fleetAITarget, drydockSystemAITarget) return aiFleetOrder
def calculateOutpostPriority(): """calculates the demand for outpost ships by colonisable planets""" global allotted_outpost_targets base_outpost_cost = AIDependencies.OUTPOST_POD_COST enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() total_pp = fo.getEmpire().productionPoints num_colonies = len(list(AIstate.popCtrIDs)) # significant growth barrier for low aggression, negligible for high aggression if num_colonies > colony_growth_barrier: return 0.0 mil_prio = foAI.foAIstate.get_priority( EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY) NOT_SPARCE, ENEMY_UNSEEN = 0, 0 IS_SPARCE, ENEMY_SEEN = 1, 1 allotted_portion = { (NOT_SPARCE, ENEMY_UNSEEN): (0.6, 0.8), (NOT_SPARCE, ENEMY_SEEN): (0.3, 0.4), (IS_SPARCE, ENEMY_UNSEEN): (0.8, 0.9), (IS_SPARCE, ENEMY_SEEN): (0.3, 0.4), }[(galaxy_is_sparse, any(enemies_sighted))][fo.empireID() % 2] if mil_prio < 100: allotted_portion *= 2 elif mil_prio < 200: allotted_portion *= 1.5 allotted_outpost_targets = 1 + int( total_pp * 3 * allotted_portion / base_outpost_cost) num_outpost_targets = len([ pid for (pid, (score, specName)) in foAI.foAIstate.colonisableOutpostIDs.items() if score > 1.0 * base_outpost_cost / 3.0 ][:allotted_outpost_targets]) if num_outpost_targets == 0 or not tech_is_complete( AIDependencies.OUTPOSTING_TECH): return 0 outpostShipIDs = FleetUtilsAI.get_empire_fleet_ids_by_role( EnumsAI.AIFleetMissionType.FLEET_MISSION_OUTPOST) num_outpost_ships = len( FleetUtilsAI.extract_fleet_ids_without_mission_types(outpostShipIDs)) outpost_priority = 50 * (num_outpost_targets - num_outpost_ships) / num_outpost_targets # print # print "Number of Outpost Ships : " + str(num_outpost_ships) # print "Number of Colonisable outposts: " + str(num_outpost_planet_ids) print "Priority for outpost ships : " + str(outpost_priority) if outpost_priority < 1: return 0 return outpost_priority
def merge_fleet_a_into_b(fleet_a_id, fleet_b_id, leave_rating=0, need_rating=0, context=""): universe = fo.getUniverse() fleet_a = universe.getFleet(fleet_a_id) fleet_b = universe.getFleet(fleet_b_id) if not fleet_a or not fleet_b: return 0 success = True init_rating = foAI.foAIstate.get_rating(fleet_a_id) remaining_rating = init_rating.copy() transferred_rating = 0 transferred_attack = 0 transferred_health = 0 b_has_monster = False for ship_id in fleet_b.shipIDs: this_ship = universe.getShip(ship_id) if not this_ship: continue if this_ship.isMonster: b_has_monster = True break for ship_id in fleet_a.shipIDs: this_ship = universe.getShip(ship_id) if not this_ship or this_ship.isMonster != b_has_monster: continue stats = foAI.foAIstate.get_design_id_stats(this_ship.designID) this_rating = stats['attack'] * (stats['structure'] + stats['shields']) if (remaining_rating['attack'] - stats['attack']) * ( remaining_rating['health'] - (stats['structure'] + stats['shields'])) < leave_rating: continue # remaining_rating -= this_rating remaining_rating['attack'] -= stats['attack'] remaining_rating['health'] -= stats['structure'] + stats['shields'] transferred = fo.issueFleetTransferOrder(ship_id, fleet_b_id) if transferred: transferred_rating += this_rating transferred_attack += stats['attack'] transferred_health += stats['structure'] + stats['shields'] else: print " *** transfer of ship %4d, formerly of fleet %4d, into fleet %4d failed; %s" % ( ship_id, fleet_a_id, fleet_b_id, [" context is %s" % context, ""][context == ""]) success = success and transferred if need_rating != 0 and need_rating <= transferred_attack * transferred_health: # transferred_rating: break fleet_a = universe.getFleet(fleet_a_id) if not fleet_a or fleet_a.empty or fleet_a_id in universe.destroyedObjectIDs( fo.empireID()): foAI.foAIstate.delete_fleet_info(fleet_a_id) foAI.foAIstate.update_fleet_rating(fleet_b_id) return transferred_attack * transferred_health, transferred_attack, transferred_health
def getResupplyAIFleetOrder(fleetAITarget, currentSystemAITarget): "returns resupply AIFleetOrder to nearest supplied system" # find nearest supplied system empireID = fo.empireID() suppliedSystemAITarget = getNearestSuppliedSystem(currentSystemAITarget.getTargetID(), empireID) # create resupply AIFleetOrder aiFleetOrder = AIFleetOrder.AIFleetOrder(AIFleetOrderType.ORDER_RESUPPLY, fleetAITarget, suppliedSystemAITarget) return aiFleetOrder
def get_safe_path_leg_to_dest(fleet_id, start_id, dest_id): start_targ = AITarget.AITarget(AITargetType.TARGET_SYSTEM, start_id) dest_targ = AITarget.AITarget(AITargetType.TARGET_SYSTEM, dest_id) #TODO actually get a safe path this_path = canTravelToSystem(fleet_id, start_targ, dest_targ, fo.empireID(), ensure_return=False) path_ids = [ targ.target_id for targ in this_path if targ.target_id != start_id] + [start_id] start_info = PlanetUtilsAI.sysNameIDs([start_id]) dest_info = PlanetUtilsAI.sysNameIDs([dest_id]) path_info = [ PlanetUtilsAI.sysNameIDs([sys_id]) for sys_id in path_ids] print "Fleet %d requested safe path leg from %s to %s, found path %s"%(fleet_id, start_info, dest_info , path_info) return path_ids[0]
def __update_buildings(self): universe = fo.getUniverse() empire_id = fo.empireID() drydocks = {} for building_id in universe.buildingIDs: building = universe.getBuilding(building_id) if not building: continue if building.buildingTypeName == AIDependencies.BLD_SHIPYARD_ORBITAL_DRYDOCK and building.ownedBy(empire_id): drydocks.setdefault(building.systemID, []).append(building.planetID) self.__drydock_locations = ReadOnlyDict({k: tuple(v) for k, v in drydocks.iteritems()})
def get_empire_planets_by_species(self): """ Return dict for empire from species to list of planet ids. :rtype: dict[str, list[int]] """ empire_id = fo.empireID() result = {} for x in (x for x in self.__planet_info.values() if x.owner == empire_id and x.species_name): result.setdefault(x.species_name, []).append(x.pid) return result
def __update_buildings(self): universe = fo.getUniverse() empire_id = fo.empireID() drydocks = {} for building_id in universe.buildingIDs: building = universe.getBuilding(building_id) if not building: continue if building.buildingTypeName == AIDependencies.BLD_SHIPYARD_ORBITAL_DRYDOCK and building.ownedBy(empire_id): drydocks.setdefault(building.systemID, []).append(building.planetID) self.__drydock_locations = ReadOnlyDict({k: tuple(v) for k, v in drydocks.items()})
def follow_vis_system_connections(start_system_id, home_system_id): universe = fo.getUniverse() empire_id = fo.empireID() exploration_list = [start_system_id] aistate = get_aistate() while exploration_list: cur_system_id = exploration_list.pop() if cur_system_id in graph_flags: continue graph_flags.add(cur_system_id) system = universe.getSystem(cur_system_id) if cur_system_id in aistate.visBorderSystemIDs: pre_vis = "a border system" elif cur_system_id in aistate.visInteriorSystemIDs: pre_vis = "an interior system" else: pre_vis = "an unknown system" system_header = "*** system %s;" % system if fo.currentTurn() < 50: visibility_turn_list = sorted(universe.getVisibilityTurnsMap( cur_system_id, empire_id).items(), key=lambda x: x[0].numerator) visibility_info = ", ".join("%s: %s" % (vis.name, turn) for vis, turn in visibility_turn_list) debug("%s previously %s. Visibility per turn: %s " % (system_header, pre_vis, visibility_info)) status_info = [] else: status_info = [system_header] has_been_visible = get_partial_visibility_turn(cur_system_id) > 0 is_connected = systems_connected(cur_system_id, home_system_id) status_info.append(" -- is%s partially visible" % ("" if has_been_visible else " not")) status_info.append(" -- is%s visibly connected to homesystem" % ("" if is_connected else " not")) if has_been_visible: sys_status = aistate.systemStatus.setdefault(cur_system_id, {}) aistate.visInteriorSystemIDs.add(cur_system_id) aistate.visBorderSystemIDs.discard(cur_system_id) neighbors = get_neighbors(cur_system_id) sys_status.setdefault("neighbors", set()).update(neighbors) if neighbors: status_info.append(" -- has neighbors %s" % sorted(neighbors)) for sys_id in neighbors: if sys_id not in aistate.exploredSystemIDs: aistate.unexploredSystemIDs.add(sys_id) if (sys_id not in graph_flags) and ( sys_id not in aistate.visInteriorSystemIDs): aistate.visBorderSystemIDs.add(sys_id) exploration_list.append(sys_id) if fo.currentTurn() < 50: debug("\n".join(status_info)) debug("----------------------------------------------------------")
def get_empire_planets_by_species(self): """ Return dict for empire from species to list of planet ids. :rtype: dict[str, list[int]] """ empire_id = fo.empireID() result = {} for x in (x for x in self.__planet_info.itervalues() if x.owner == empire_id and x.species_name): result.setdefault(x.species_name, []).append(x.pid) return result
def calculateColonisationPriority(): """calculates the demand for colony ships by colonisable planets""" global allottedColonyTargets, colony_growth_barrier enemies_sighted = foAI.foAIstate.misc.get("enemies_sighted", {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() total_pp = fo.getEmpire().productionPoints num_colonies = len(list(AIstate.popCtrIDs)) # significant growth barrier for low aggression, negligible for high aggression colony_growth_barrier = 2 + ((0.5 + foAI.foAIstate.aggression) ** 2) * fo.currentTurn() / 50.0 colonyCost = AIDependencies.COLONY_POD_COST * (1 + AIDependencies.COLONY_POD_UPKEEP * num_colonies) turnsToBuild = 8 # TODO: check for susp anim pods, build time 10 mil_prio = foAI.foAIstate.get_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY) allottedPortion = [[[0.6, 0.8], [0.3, 0.4]], [[0.8, 0.9], [0.3, 0.4]]][galaxy_is_sparse][any(enemies_sighted)][ fo.empireID() % 2 ] # if ( foAI.foAIstate.get_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_COLONISATION) # > 2 * foAI.foAIstate.get_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY)): # allottedPortion *= 1.5 if mil_prio < 100: allottedPortion *= 2 elif mil_prio < 200: allottedPortion *= 1.5 elif fo.currentTurn() > 100: allottedPortion *= 0.75 ** (num_colonies / 10.0) # allottedColonyTargets = 1+ int(fo.currentTurn()/50) allottedColonyTargets = 1 + int(total_pp * turnsToBuild * allottedPortion / colonyCost) outpost_prio = foAI.foAIstate.get_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_OUTPOST) # if have any outposts to build, don't build colony ships TODO: make more complex assessment if outpost_prio > 0 or num_colonies > colony_growth_barrier: return 0.0 if num_colonies > colony_growth_barrier: return 0.0 numColonisablePlanetIDs = len( [pid for (pid, (score, _)) in foAI.foAIstate.colonisablePlanetIDs.items() if score > 60][ : allottedColonyTargets + 2 ] ) if numColonisablePlanetIDs == 0: return 1 colonyshipIDs = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_COLONISATION) numColonyships = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(colonyshipIDs)) colonisationPriority = 60 * (1 + numColonisablePlanetIDs - numColonyships) / (numColonisablePlanetIDs + 1) # print # print "Number of Colony Ships : " + str(numColonyships) # print "Number of Colonisable planets : " + str(numColonisablePlanetIDs) # print "Priority for colony ships : " + str(colonisationPriority) if colonisationPriority < 1: return 0 return colonisationPriority
def generateAIFleetOrders(self): "generates AIFleetOrders from fleets targets to accomplish" universe = fo.getUniverse() fleetID = self.getAITargetID() fleet = universe.getFleet(fleetID) if (not fleet) or fleet.empty or (fleetID in universe.destroyedObjectIDs(fo.empireID())): #fleet was probably merged into another or was destroyed foAI.foAIstate.deleteFleetInfo(fleetID) return # TODO: priority self.clearAIFleetOrders() ntargets=0 for aiFleetMissionType in self.getAIMissionTypes(): ntargets += len( self.getAITargets(aiFleetMissionType) ) if ntargets ==0: return #no targets # for some targets fleet has to visit systems and therefore fleet visit them systemAITargets = self.__getRequiredToVisitSystemAITargets() aiFleetOrdersToVisitSystems = MoveUtilsAI.getAIFleetOrdersFromSystemAITargets(self.getAITarget(), systemAITargets) #print "----------------------------------------" #print "*+*+ fleet %d : has fleet action system targets: %s"%(fleetID, [str(obj) for obj in systemAITargets]) #print "----------" #print "*+*+ fleet %d: has movement orders: %s"%(fleetID, [str(obj) for obj in aiFleetOrdersToVisitSystems]) for aiFleetOrder in aiFleetOrdersToVisitSystems: self.appendAIFleetOrder(aiFleetOrder) # if fleet is in some system = fleet.systemID >=0, then also generate system AIFleetOrders systemID = fleet.systemID if systemID >= 0: # system in where fleet is systemAITarget = AITarget.AITarget(AITargetType.TARGET_SYSTEM, systemID) # if mission aiTarget has required system where fleet is, then generate aiFleetOrder from this aiTarget aiMissionTypes = self.getAIMissionTypes() # for all targets in all mission types get required systems to visit for aiFleetMissionType in aiMissionTypes: aiTargets = self.getAITargets(aiFleetMissionType) for aiTarget in aiTargets: if systemAITarget in aiTarget.getRequiredSystemAITargets(): # from target required to visit get fleet orders to accomplish target aiFleetOrder = self.__getAIFleetOrderFromAITarget(aiFleetMissionType, aiTarget) self.appendAIFleetOrder(aiFleetOrder) # if fleet don't have any mission, then resupply if is current location not in supplyable system empire = fo.getEmpire() fleetSupplyableSystemIDs = empire.fleetSupplyableSystemIDs if (not self.hasAnyAIMissionTypes()) and not(self.getLocationAITarget().getTargetID() in fleetSupplyableSystemIDs): resupplyAIFleetOrder = MoveUtilsAI.getResupplyAIFleetOrder(self.getAITarget(), self.getLocationAITarget()) if resupplyAIFleetOrder.isValid(): self.appendAIFleetOrder(resupplyAIFleetOrder)
def get_max_empire_detection(empire_list: Union[List[int], "fo.IntVec"]) -> float: """ Returns the max detection strength across all empires except for the current AI's empire. :param empire_list: list of empire IDs :return: max detection strength of provided empires, excluding the current self empire """ max_detection = 0 for this_empire_id in empire_list: if this_empire_id != fo.empireID(): max_detection = max(max_detection, get_empire_detection(this_empire_id)) return max_detection
def is_valid(self): if not super(OrderColonize, self).is_valid(): return False planet = self.target.get_object() sys_partial_vis_turn = get_partial_visibility_turn(planet.systemID) planet_partial_vis_turn = get_partial_visibility_turn(planet.id) if (planet_partial_vis_turn == sys_partial_vis_turn and planet.unowned or (planet.ownedBy(fo.empireID()) and not planet.currentMeterValue(fo.meterType.population))): return self.fleet.get_object().hasColonyShips self.executed = True self.order_issued = True return False
def get_empire_inhabited_planets_by_system(self): """ Return dict from system id to planet ids of empire with species. :rtype: dict[int, list[int]] """ # TODO: as currently used, is duplicative with combo of foAI.foAIstate.popCtrSystemIDs and foAI.foAIstate.colonizedSystems empire_id = fo.empireID() empire_planets_with_species = (x for x in self.__planet_info.itervalues() if x.owner == empire_id and x.species_name) result = {} for x in empire_planets_with_species: result.setdefault(x.system_id, []).append(x.pid) return result
def get_empire_fleet_ids(): """Returns all fleetIDs for current empire.""" empire_id = fo.empireID() universe = fo.getUniverse() empire_fleet_ids = [] destroyed_object_ids = universe.destroyedObjectIDs(empire_id) for fleet_id in set(list(universe.fleetIDs) + list(foAI.foAIstate.newlySplitFleets)): fleet = universe.getFleet(fleet_id) if fleet is None: continue if fleet.ownedBy(empire_id) and fleet_id not in destroyed_object_ids and not fleet.empty and fleet.shipIDs: empire_fleet_ids.append(fleet_id) return empire_fleet_ids