def calculateResearchPriority(): "calculates the AI empire's demand for research" universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID gotAlgo = empire.getTechStatus("LRN_ALGO_ELEGANCE") == fo.techStatus.complete totalPP = empire.productionPoints totalRP = empire.resourceProduction(fo.resourceType.research) # get current industry production & Target ownedPlanetIDs = PlanetUtilsAI.getOwnedPlanetsByEmpire(universe.planetIDs, empireID) planets = map(universe.getPlanet, ownedPlanetIDs) targetRP = sum( map( lambda x: x.currentMeterValue(fo.meterType.targetResearch), planets) ) if (fo.currentTurn() < 20) or not gotAlgo: researchPriority = 60 # mid industry , high research at beginning of game to get easy gro tech and to get research booster Algotrithmic Elegance elif fo.currentTurn() < 30: researchPriority = 30 # mid industry , mid research elif fo.currentTurn() < 40: researchPriority = 20 # high industry , low research else: researchPriority = 15 # high industry , low research print "" print "Research Production (current/target) : ( %.1f / %.1f )"%(totalRP, targetRP) print "Priority for Research: " + str(researchPriority) return researchPriority
def calculateIndustryPriority(): "calculates the demand for industry" universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID # get current industry production & Target industryProduction = empire.resourceProduction(fo.resourceType.industry) ownedPlanetIDs = PlanetUtilsAI.getOwnedPlanetsByEmpire(universe.planetIDs, empireID) planets = map(universe.getPlanet, ownedPlanetIDs) targetPP = sum( map( lambda x: x.currentMeterValue(fo.meterType.targetIndustry), planets) ) if fo.currentTurn() < 20: industryPriority = 20 # mid industry , high research at beginning of game to get easy gro tech elif fo.currentTurn() < 30: industryPriority = 25 # mid industry , mid research elif fo.currentTurn() < 40: industryPriority = 40 # high industry , mid research elif fo.currentTurn() < 50: industryPriority = 50 # high industry , mid research else: industryPriority = 60 # high industry , low-mid research # increase demand for industry industry production is low #industryPriority = 380 / (industryProduction + 0.001) print "" print "Industry Production (current/target) : ( %.1f / %.1f ) at turn %s"%(industryProduction, targetPP, fo.currentTurn()) print "Priority for Industry: " + str(industryPriority) return industryPriority
def __border_exploration_update(self): universe = fo.getUniverse() exploration_center = PlanetUtilsAI.get_capital_sys_id() # a bad state probably from an old savegame, or else empire has lost (or almost has) if exploration_center == INVALID_ID: exploration_center = self.__origin_home_system_id ExplorationAI.graph_flags.clear() if fo.currentTurn() < 50: print "-------------------------------------------------" print "Border Exploration Update (relative to %s)" % universe.getSystem(exploration_center) print "-------------------------------------------------" if self.visBorderSystemIDs == {INVALID_ID}: self.visBorderSystemIDs.clear() self.visBorderSystemIDs.add(exploration_center) for sys_id in list(self.visBorderSystemIDs): # This set is modified during iteration. if fo.currentTurn() < 50: print "Considering border system %s" % universe.getSystem(sys_id) ExplorationAI.follow_vis_system_connections(sys_id, exploration_center) newly_explored = ExplorationAI.update_explored_systems() nametags = [] for sys_id in newly_explored: newsys = universe.getSystem(sys_id) # an explored system *should* always be able to be gotten nametags.append("ID:%4d -- %-20s" % (sys_id, (newsys and newsys.name) or "name unknown")) if newly_explored: print "-------------------------------------------------" print "Newly explored systems:\n%s" % "\n".join(nametags) print "-------------------------------------------------"
def calculateMilitaryPriority(): "calculates the demand for military ships by military targeted systems" global unmetThreat universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID capitalID = PlanetUtilsAI.getCapital() if capitalID is not None and capitalID != -1: homeworld = universe.getPlanet(capitalID) else: return 0# no capitol (not even a capitol-in-the-making), means can't produce any ships have_mod_weaps = ( empire.getTechStatus("SHP_WEAPON_1_4") == fo.techStatus.complete or empire.getTechStatus("SHP_WEAPON_2_1") == fo.techStatus.complete or empire.getTechStatus("SHP_WEAPON_4_1") == fo.techStatus.complete ) allottedInvasionTargets = 1+ int(fo.currentTurn()/25) targetPlanetIDs = [pid for pid, pscore, trp in AIstate.invasionTargets[:allottedInvasionTargets] ] + [pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs[:allottedColonyTargets] ] + [pid for pid, pscore in foAI.foAIstate.colonisableOutpostIDs[:allottedColonyTargets] ] mySystems = set( AIstate.popCtrSystemIDs ).union( AIstate.outpostSystemIDs ) targetSystems = set( PlanetUtilsAI.getSystems(targetPlanetIDs) ) curShipRating = ProductionAI.curBestMilShipRating() cSRR = curShipRating**0.5 unmetThreat = 0.0 currentTurn=fo.currentTurn() shipsNeeded=0 defenses_needed = 0 for sysID in mySystems.union(targetSystems) : status=foAI.foAIstate.systemStatus.get( sysID, {} ) myRating = status.get('myFleetRating', 0) my_defenses = status.get('mydefenses', {}).get('overall', 0) baseMonsterThreat = status.get('monsterThreat', 0) #scale monster threat so that in early - mid game big monsters don't over-drive military production if currentTurn>200: monsterThreat = baseMonsterThreat elif currentTurn>100: if baseMonsterThreat <2000: monsterThreat = baseMonsterThreat else: monsterThreat = 2000 + (currentTurn/100.0 - 1) *(baseMonsterThreat-2000) else: monsterThreat = 0 if sysID in mySystems: threatRoot = status.get('fleetThreat', 0)**0.5 + 0.8*status.get('max_neighbor_threat', 0)**0.5 + 0.2*status.get('neighborThreat', 0)**0.5 + monsterThreat**0.5 + status.get('planetThreat', 0)**0.5 else: threatRoot = status.get('fleetThreat', 0)**0.5 + monsterThreat**0.5 + status.get('planetThreat', 0)**0.5 shipsNeeded += math.ceil(( max(0, (threatRoot - (myRating**0.5 + my_defenses**0.5)))**2)/curShipRating) #militaryPriority = int( 40 + max(0, 75*unmetThreat / curShipRating) ) militaryPriority = int( 40 + max(0, 75*shipsNeeded) ) if not have_mod_weaps: militaryPriority /= 2 #print "Calculating Military Priority: 40 + 75 * unmetThreat/curShipRating \n\t Priority: %d \t unmetThreat %.0f curShipRating: %.0f"%(militaryPriority, unmetThreat, curShipRating) print "Calculating Military Priority: 40 + 75 * shipsNeeded \n\t Priority: %d \t shipsNeeded %d \t unmetThreat %.0f curShipRating: %.0f"%(militaryPriority, shipsNeeded, unmetThreat, curShipRating) return max( militaryPriority, 0)
def log_war_declaration(self, initiating_empire_id, recipient_empire_id): """Keep a record of war declarations made or received by this empire.""" # if war declaration is made on turn 1, don't hold it against them if fo.currentTurn() == 1: return war_declarations = self.diplomatic_logs.setdefault('war_declarations', {}) log_index = (initiating_empire_id, recipient_empire_id) war_declarations.setdefault(log_index, []).append(fo.currentTurn())
def calculateMilitaryPriority(): "calculates the demand for military ships by military targeted systems" universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID capitalID = PlanetUtilsAI.getCapital() if capitalID: homeworld = universe.getPlanet(capitalID) else: return 0# no capitol (not even a capitol-in-the-making), means can't produce any ships allottedInvasionTargets = 1+ int(fo.currentTurn()/25) targetPlanetIDs = [pid for pid, pscore, trp in AIstate.invasionTargets[:allottedInvasionTargets] ] + [pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs[:allottedColonyTargets] ] + [pid for pid, pscore in foAI.foAIstate.colonisableOutpostIDs[:allottedColonyTargets] ] mySystems = set( AIstate.popCtrSystemIDs ).union( AIstate.outpostSystemIDs ) targetSystems = set( PlanetUtilsAI.getSystems(targetPlanetIDs) ) curShipRating = ProductionAI.curBestMilShipRating() cSRR = curShipRating**0.5 unmetThreat = 0.0 currentTurn=fo.currentTurn() shipsNeeded=0 for sysID in mySystems.union(targetSystems) : status=foAI.foAIstate.systemStatus.get( sysID, {} ) myAttack, myHealth =0, 0 for fid in status.get('myfleets', []) : rating= foAI.foAIstate.fleetStatus.get(fid, {}).get('rating', {}) myAttack += rating.get('attack', 0) myHealth += rating.get('health', 0) myRating = myAttack*myHealth baseMonsterThreat = status.get('monsterThreat', 0) if currentTurn>200: monsterThreat = baseMonsterThreat elif currentTurn>100: if baseMonsterThreat <2000: monsterThreat = baseMonsterThreat else: monsterThreat = 2000 + (currentTurn/100.0 - 1) *(baseMonsterThreat-2000) else: monsterThreat = 0 threatRoot = status.get('fleetThreat', 0)**0.5 + status.get('planetThreat', 0)**0.5 + monsterThreat**0.5 if sysID in mySystems: threatRoot += (0.3* status.get('neighborThreat', 0))**0.5 else: threatRoot += (0.1* status.get('neighborThreat', 0))**0.5 threat = threatRoot**2 unmetThreat += max( 0, threat - myRating ) shipsNeeded += math.ceil( max(0, (threatRoot/cSRR)- (myRating/curShipRating)**0.5 ) ) #militaryPriority = int( 40 + max(0, 75*unmetThreat / curShipRating) ) militaryPriority = int( 40 + max(0, 75*shipsNeeded) ) #print "Calculating Military Priority: 40 + 75 * unmetThreat/curShipRating \n\t Priority: %d \t unmetThreat %.0f curShipRating: %.0f"%(militaryPriority, unmetThreat, curShipRating) print "Calculating Military Priority: 40 + 75 * shipsNeeded \n\t Priority: %d \t shipsNeeded %d \t unmetThreat %.0f curShipRating: %.0f"%(militaryPriority, shipsNeeded, unmetThreat, curShipRating) return max( militaryPriority, 0)
def calculateResearchPriority(): "calculates the AI empire's demand for research" universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID industryPriority = foAI.foAIstate.getPriority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_PRODUCTION) gotAlgo = empire.getTechStatus("LRN_ALGO_ELEGANCE") == fo.techStatus.complete researchQueueList = ResearchAI.getResearchQueueTechs() orbGenTech = "PRO_ORBITAL_GEN" totalPP = empire.productionPoints totalRP = empire.resourceProduction(fo.resourceType.research) industrySurge= (foAI.foAIstate.aggression > fo.aggression.cautious) and ( totalPP <(30*(foAI.foAIstate.aggression)) ) and (orbGenTech in researchQueueList[:3] or empire.getTechStatus(orbGenTech) == fo.techStatus.complete) # get current industry production & Target ownedPlanetIDs = PlanetUtilsAI.getOwnedPlanetsByEmpire(universe.planetIDs, empireID) planets = map(universe.getPlanet, ownedPlanetIDs) targetRP = sum( map( lambda x: x.currentMeterValue(fo.meterType.targetResearch), planets) ) styleIndex = empireID%2 cutoffSets = [ [25, 45, 70 ], [35, 50, 70 ] ] cutoffs = cutoffSets[styleIndex ] settings = [ [2, .6, .4, .35 ], [1.4, .7, .4, .35 ] ][styleIndex ] if industrySurge and True: researchPriority = 0.2 * industryPriority else: if (fo.currentTurn() < cutoffs[0]) or not gotAlgo: researchPriority = settings[0] * industryPriority # high research at beginning of game to get easy gro tech and to get research booster Algotrithmic Elegance elif fo.currentTurn() < cutoffs[1]: researchPriority = settings[1] * industryPriority# med-high research elif fo.currentTurn() < cutoffs[2]: researchPriority = settings[2] * industryPriority # med-high industry else: researchQueue = list(empire.researchQueue) researchPriority = settings[3] * industryPriority # high industry , low research if len(researchQueue) == 0 : researchPriority = 0 # done with research elif len(researchQueue) <5 and researchQueue[-1].allocation > 0 : researchPriority = len(researchQueue) # barely not done with research elif len(researchQueue) <10 and researchQueue[-1].allocation > 0 : researchPriority = 4+ len(researchQueue) # almost done with research elif len(researchQueue) <20 and researchQueue[int(len(researchQueue)/2)].allocation > 0 : researchPriority = 0.5 * researchPriority # closing in on end of research elif len(researchQueue) <20: researchPriority = 0.7*researchPriority # high industry , low research print "" print "Research Production (current/target) : ( %.1f / %.1f )"%(totalRP, targetRP) print "Priority for Research: " + str(researchPriority) return researchPriority
def calculateMilitaryPriority(): "calculates the demand for military ships by military targeted systems" universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID capitalID = PlanetUtilsAI.getCapital() homeworld = universe.getPlanet(capitalID) if homeworld: homeSystemID = homeworld.systemID else: homeSystemID=-1 totalThreat = 0 for sysStatus in foAI.foAIstate.systemStatus.values(): totalThreat += max(0, (sysStatus.get('fleetThreat', 0) + sysStatus.get('planetThreat', 0) - 0.7*sysStatus.get('monsterThreat', 0) + sysStatus.get('neighborThreat', 0) )) #being safe; should never be neg since fleetThreat should include monsterThreat totalFleetRating = 0 for fleetStatus in foAI.foAIstate.fleetStatus.values(): totalFleetRating += fleetStatus.get('rating', {}).get('overall', 0) #numMilitaryTargetedSystemIDs = len(AIstate.militaryTargetedSystemIDs) #militaryShipIDs = FleetUtilsAI.getEmpireFleetIDsByRole(AIFleetMissionType.FLEET_MISSION_MILITARY) #numMilitaryShips = len(FleetUtilsAI.extractFleetIDsWithoutMissionTypes(militaryShipIDs)) curShipRating = curBestMilShipRating() if fo.currentTurn() < 20: threatBias = 0 elif fo.currentTurn() < 40: threatBias = 10 elif fo.currentTurn() < 60: threatBias = 80 elif fo.currentTurn() < 80: threatBias = 100 else: threatBias = 200 if threatBias > 0: threatBias = max(threatBias, curShipRating) visibleSystemIDs = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate. visBorderSystemIDs.keys() accessibleSystemIDs = [sysID for sysID in visibleSystemIDs if universe.systemsConnected(sysID, homeSystemID, empireID) ] totalBias = len(accessibleSystemIDs) * threatBias # build one more military ship than military targeted systems #militaryPriority = 100 * ((numMilitaryTargetedSystemIDs +2) - numMilitaryShips) / (numMilitaryTargetedSystemIDs + 1) militaryPriority = int( 40 + max(0, 15*((1.25*totalThreat +threatBias - totalFleetRating ) / curShipRating)) ) print "Military Priority Calc: int( 40 + max(0, 10*((1.25*totalThreat(%d) - totalFleetRating(%d) ) / curShipRating(%d) )) ) = %d"%(totalThreat, totalFleetRating, curShipRating, militaryPriority) # print "" # print "Number of Military Ships Without Missions: " + str(numMilitaryShips) # print "Number of Military Targeted Systems: " + str(numMilitaryTargetedSystemIDs) # print "Priority for Military Ships: " + str(militaryPriority) return max( militaryPriority, 0)
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 calculateResearchPriority(): "calculates the AI empire's demand for research" universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID gotAlgo = empire.getTechStatus("LRN_ALGO_ELEGANCE") == fo.techStatus.complete researchQueueList = getResearchQueueTechs() orbGenTech = "PRO_ORBITAL_GEN" totalPP = empire.productionPoints totalRP = empire.resourceProduction(fo.resourceType.research) industrySurge= (foAI.foAIstate.aggression > fo.aggression.cautious) and ( totalPP <(20*(foAI.foAIstate.aggression)) ) and (orbGenTech in researchQueueList[:2] or empire.getTechStatus(orbGenTech) == fo.techStatus.complete) # get current industry production & Target ownedPlanetIDs = PlanetUtilsAI.getOwnedPlanetsByEmpire(universe.planetIDs, empireID) planets = map(universe.getPlanet, ownedPlanetIDs) targetRP = sum( map( lambda x: x.currentMeterValue(fo.meterType.targetResearch), planets) ) styleIndex = empireID%2 styleAdjustmentMap = {0:0, 1:0}#TODO: decide if I want to do anything with this styleAdjustment = styleAdjustmentMap.get( styleIndex, 0 ) cutoffs = [ [30, 40, 60 ], [35, 50, 70 ] ][styleIndex ] #1 doing better settings = [ [50, 40, 35, 30 ], [50, 40, 30, 25 ] ][styleIndex ] #cutoffs = [ [40, 65, 90 ], [40, 90, 150 ] ][styleIndex ] #1 doing better if industrySurge and False: researchPriority = 10+styleAdjustment else: if (fo.currentTurn() < cutoffs[0]) or not gotAlgo: researchPriority = settings[0] # mid industry , high research at beginning of game to get easy gro tech and to get research booster Algotrithmic Elegance elif fo.currentTurn() < cutoffs[1]: researchPriority = settings[1] +styleAdjustment# mid industry , mid research elif fo.currentTurn() < cutoffs[2]: researchPriority = settings[2]+styleAdjustment # high industry , low research else: researchQueue = list(empire.researchQueue) researchPriority = settings[3]+styleAdjustment # high industry , low research if len(researchQueue) == 0 : researchPriority = 0 # done with research elif len(researchQueue) <5 and researchQueue[-1].allocation > 0 : researchPriority = len(researchQueue) # barely not done with research elif len(researchQueue) <10 and researchQueue[-1].allocation > 0 : researchPriority = 2 # almost done with research elif len(researchQueue) <20 and researchQueue[int(len(researchQueue)/2)].allocation > 0 : researchPriority = 5 # closing in on end of research elif len(researchQueue) <20: researchPriority = 10 # high industry , low research print "" print "Research Production (current/target) : ( %.1f / %.1f )"%(totalRP, targetRP) print "Priority for Research: " + str(researchPriority) return researchPriority
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 = 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 = aistate.systemStatus.setdefault(cur_system_id, {}) aistate.visInteriorSystemIDs.add(cur_system_id) aistate.visBorderSystemIDs.discard(cur_system_id) neighbors = set(universe.getImmediateNeighbors(cur_system_id, empire_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 refresh(self): """Turn start AIstate cleanup/refresh.""" universe = fo.getUniverse() # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats fleetsLostBySystem.clear() invasionTargets[:] = [] exploration_center = PlanetUtilsAI.get_capital_sys_id() if exploration_center == -1: # a bad state probably from an old savegame, or else empire has lost (or almost has) exploration_center = self.__origin_home_system_id # check if planets in cache is still present. Remove destroyed. for system_id, info in sorted(self.systemStatus.items()): planet_dict = info.get('planets', {}) cache_planet_set = set(planet_dict) system_planet_set = set(universe.getSystem(system_id).planetIDs) diff = cache_planet_set - system_planet_set if diff: print "Removing destroyed planets from systemStatus for system %s: planets to be removed: %s" % (system_id, sorted(diff)) for key in diff: del planet_dict[key] ExplorationAI.graphFlags.clear() if fo.currentTurn() < 50: print "-------------------------------------------------" print "Border Exploration Update (relative to %s)" % (PlanetUtilsAI.sys_name_ids([exploration_center, -1])[0]) print "-------------------------------------------------" if self.visBorderSystemIDs.keys() == [-1]: self.visBorderSystemIDs.clear() self.visBorderSystemIDs[exploration_center] = 1 for sys_id in self.visBorderSystemIDs.keys(): # This dict modified during iteration. if fo.currentTurn() < 50: print "Considering border system %s" % (PlanetUtilsAI.sys_name_ids([sys_id, -1])[0]) ExplorationAI.follow_vis_system_connections(sys_id, exploration_center) newly_explored = ExplorationAI.update_explored_systems() nametags = [] for sys_id in newly_explored: newsys = universe.getSystem(sys_id) nametags.append("ID:%4d -- %-20s" % (sys_id, (newsys and newsys.name) or "name unknown")) # an explored system *should* always be able to be gotten if newly_explored: print "-------------------------------------------------" print "Newly explored systems:\n%s" % "\n".join(nametags) print "-------------------------------------------------" # cleanup fleet roles # self.update_fleet_locs() self.__clean_fleet_roles() self.__clean_fleet_missions(FleetUtilsAI.get_empire_fleet_ids()) print "Fleets lost by system: %s" % fleetsLostBySystem self.update_system_status()
def clean(self): "turn start AIstate cleanup" fleetsLostBySystem.clear() fleetsLostByID.clear() invasionTargets[:]=[] ExplorationAI.graphFlags.clear() if fo.currentTurn() < 50: print "-------------------------------------------------" print "Border Exploration Update" print "-------------------------------------------------" for sysID in list(self.visBorderSystemIDs): ExplorationAI.followVisSystemConnections(sysID, self.origHomeSystemID) newlyExplored = ExplorationAI.updateExploredSystems() nametags=[] universe = fo.getUniverse() for sysID in newlyExplored: newsys = universe.getSystem(sysID) nametags.append( "ID:%4d -- %20s"%(sysID, (newsys and newsys.name) or"name unknown" ) )# an explored system *should* always be able to be gotten if newlyExplored: print "-------------------------------------------------" print "newly explored systems: \n"+"\n".join(nametags) print "-------------------------------------------------" # cleanup fleet roles #self.updateFleetLocs() self.__cleanFleetRoles() self.__cleanAIFleetMissions(FleetUtilsAI.getEmpireFleetIDs()) print "Fleets lost by system: %s"%fleetsLostBySystem self.updateSystemStatus() ExplorationAI.updateScoutFleets() #should do this after clearing dead fleets, currently should be already done here
def end(self): """ Stop timer, output result, clear checks. If dumping to file, if headers are not match to prev, new header line will be added. """ turn = fo.currentTurn() self.stop() if not self.timers: return max_header = max(len(x[0]) for x in self.timers) line_max_size = max_header + 14 print print ('Timing for %s:' % self.timer_name) print '=' * line_max_size for name, val in self.timers: print "%-*s %8d msec" % (max_header, name, val) print '-' * line_max_size print ("Total: %8d msec" % sum(x[1] for x in self.timers)).rjust(line_max_size) if self.write_log and DUMP_TO_FILE: headers = make_header('Turn', *[x[0] for x in self.timers]) if self.headers != headers: self._write(''.join(headers) + '\n' + ''.join(['-' * (len(x) - 2) + ' ' for x in headers])) self.headers = headers row = [] for header, val in zip(self.headers, [turn] + [x[1] for x in self.timers]): row.append('%*s ' % (len(header) - 2, int(val))) self._write(''.join(row)) self.timers = [] # clear times
def calculateExplorationPriority(): """calculates the demand for scouts by unexplored systems""" global scoutsNeeded universe = fo.getUniverse() empire = fo.getEmpire() numUnexploredSystems = len( ExplorationAI.borderUnexploredSystemIDs ) #len(foAI.foAIstate.get_explorable_systems(AIExplorableSystemType.EXPLORABLE_SYSTEM_UNEXPLORED)) numScouts = sum( [ foAI.foAIstate.fleetStatus.get(fid, {}).get('nships', 0) for fid in FleetUtilsAI.get_empire_fleet_ids_by_role( EnumsAI.AIFleetMissionType.FLEET_MISSION_EXPLORATION)] ) # FleetUtilsAI.get_empire_fleet_ids_by_role(AIFleetMissionType.FLEET_MISSION_EXPLORATION) productionQueue = empire.productionQueue queuedScoutShips=0 for queue_index in range(0, len(productionQueue)): element=productionQueue[queue_index] if element.buildType == EnumsAI.AIEmpireProductionTypes.BT_SHIP: if foAI.foAIstate.get_ship_role(element.designID) == EnumsAI.AIShipRoleType.SHIP_ROLE_CIVILIAN_EXPLORATION : queuedScoutShips += element.remaining * element.blocksize milShips = MilitaryAI.num_milships # intent of the following calc is essentially # new_scouts_needed = min(need_cap_A, need_cap_B, base_need) - already_got_or_queued # where need_cap_A is to help prevent scouting needs from swamping military needs, and # need_cap_B is to help regulate investment into scouting while the empire is small. # These caps could perhaps instead be tied more directly to military priority and # total empire production. scoutsNeeded = max(0, min( 4+int(milShips/5), 4+int(fo.currentTurn()/50) , 2+ numUnexploredSystems**0.5 ) - numScouts - queuedScoutShips ) explorationPriority = int(40*scoutsNeeded) print print "Number of Scouts : " + str(numScouts) print "Number of Unexplored systems: " + str(numUnexploredSystems) print "military size: ", milShips print "Priority for scouts : " + str(explorationPriority) return explorationPriority
def cur_best_mil_ship_rating(include_designs=False): """Find the best military ship we have available in this turn and return its rating. :param include_designs: toggles if available designs are considered or only existing ships :return: float: rating of the best ship """ current_turn = fo.currentTurn() if current_turn in best_ship_rating_cache: best_rating = best_ship_rating_cache[current_turn] if include_designs: best_design_rating = ProductionAI.cur_best_military_design_rating() best_rating = max(best_rating, best_design_rating) return best_rating best_rating = 0.001 universe = fo.getUniverse() for fleet_id in FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.MILITARY): fleet = universe.getFleet(fleet_id) for ship_id in fleet.shipIDs: ship = universe.getShip(ship_id) if ship: ship_info = [(ship.id, ship.designID, ship.speciesName)] ship_rating = foAI.foAIstate.rate_psuedo_fleet(ship_info=ship_info)['overall'] best_rating = max(best_rating, ship_rating) best_ship_rating_cache[current_turn] = best_rating if include_designs: best_design_rating = ProductionAI.cur_best_military_design_rating() best_rating = max(best_rating, best_design_rating) return max(best_rating, 0.001)
def calculateExplorationPriority(): """calculates the demand for scouts by unexplored systems""" global scoutsNeeded universe = fo.getUniverse() empire = fo.getEmpire() numUnexploredSystems = len( ExplorationAI.borderUnexploredSystemIDs ) #len(foAI.foAIstate.get_explorable_systems(AIExplorableSystemType.EXPLORABLE_SYSTEM_UNEXPLORED)) numScouts = sum( [ foAI.foAIstate.fleetStatus.get(fid, {}).get('nships', 0) for fid in ExplorationAI.currentScoutFleetIDs] ) # FleetUtilsAI.get_empire_fleet_ids_by_role(AIFleetMissionType.FLEET_MISSION_EXPLORATION) productionQueue = empire.productionQueue queuedScoutShips=0 for queue_index in range(0, len(productionQueue)): element=productionQueue[queue_index] if element.buildType == EnumsAI.AIEmpireProductionTypes.BT_SHIP: if foAI.foAIstate.get_ship_role(element.designID) == EnumsAI.AIShipRoleType.SHIP_ROLE_CIVILIAN_EXPLORATION : queuedScoutShips += element.remaining * element.blocksize milShips = MilitaryAI.num_milships scoutsNeeded = max(0, min( 4+int(milShips/5), 4+int(fo.currentTurn()/50) , 2+ numUnexploredSystems**0.5 ) - numScouts - queuedScoutShips ) explorationPriority = int(40*scoutsNeeded) print print "Number of Scouts : " + str(numScouts) print "Number of Unexplored systems: " + str(numUnexploredSystems) print "military size: ", milShips print "Priority for scouts : " + str(explorationPriority) return explorationPriority
def generateOrders(): empire = fo.getEmpire() print "Empire: " + empire.name + " TURN: " + str(fo.currentTurn()) print "Capital: " + str(empire.capitalID) # turn cleanup splitFleet() identifyShipDesigns() identifyFleetsRoles() foAIstate.clean(ExplorationAI.getHomeSystemID(), FleetUtilsAI.getEmpireFleetIDs()) # ...missions # ...demands/priorities # call AI modules PriorityAI.calculatePriorities() ExplorationAI.assignScoutsToExploreSystems() ColonisationAI.assignColonyFleetsToColonise() InvasionAI.assignInvasionFleetsToInvade() FleetUtilsAI.generateAIFleetOrdersForAIFleetMissions() FleetUtilsAI.issueAIFleetOrdersForAIFleetMissions() ResearchAI.generateResearchOrders() ProductionAI.generateProductionOrders() ResourcesAI.generateResourcesOrders() foAIstate.afterTurnCleanup() fo.doneTurn()
def calculateLearningPriority(): """calculates the demand for techs learning category""" currentturn = fo.currentTurn() if currentturn == 1: return 100 elif currentturn > 1: return 0
def wrapper(): if foAI.foAIstate is None: return function() else: cache = foAI.foAIstate.misc.setdefault('caches', {}).setdefault(function.__name__, {}) this_turn = fo.currentTurn() return cache[this_turn] if this_turn in cache else cache.setdefault(this_turn, function())
def generateResourcesOrders(): #+ "generate resources focus orders" timer= [ time() ] ## calculate top resource priority ##topResourcePriority() timer.append( time() ) ## set resource foci of planets ##setCapitalIDResourceFocus() timer.append( time() ) #------------------------------ ##setGeneralPlanetResourceFocus() setPlanetResourceFoci() timer.append( time() ) #------------------------------- ##setAsteroidsResourceFocus() timer.append( time() ) ##setGasGiantsResourceFocus() timer.append( time() ) printResourcesPriority() timer.append( time() ) if doResourceTiming and timer_entries==__timerEntries1: times = [timer[i] - timer[i-1] for i in range(1, len(timer) ) ] timeFmt = "%30s: %8d msec " print "ResourcesAI Time Requirements:" for mod, modTime in zip(timer_entries, times): print timeFmt%((30*' '+mod)[-30:], int(1000*modTime)) if resourceTimerFile: resourceTimerFile.write( __timerFileFmt%tuple( [ fo.currentTurn() ]+map(lambda x: int(1000*x), times )) +'\n') resourceTimerFile.flush()
def print_resource_ai_footer(): empire = fo.getEmpire() pp, rp = empire.productionPoints, empire.resourceProduction(fo.resourceType.research) # Next string used in charts. Don't modify it! print "Current Output (turn %4d) RP/PP : %.2f ( %.1f / %.1f )" % (fo.currentTurn(), rp / (pp + 0.0001), rp, pp) print "------------------------" print "ResourcesAI Time Requirements:"
def _calculate_exploration_priority(): """Calculates the demand for scouts by unexplored systems.""" empire = fo.getEmpire() num_unexplored_systems = len(ExplorationAI.borderUnexploredSystemIDs) num_scouts = sum([foAI.foAIstate.fleetStatus.get(fid, {}).get('nships', 0) for fid in FleetUtilsAI.get_empire_fleet_ids_by_role( MissionType.EXPLORATION)]) production_queue = empire.productionQueue queued_scout_ships = 0 for queue_index in range(0, len(production_queue)): element = production_queue[queue_index] if element.buildType == EmpireProductionTypes.BT_SHIP: if foAI.foAIstate.get_ship_role(element.designID) == ShipRoleType.CIVILIAN_EXPLORATION: queued_scout_ships += element.remaining * element.blocksize mil_ships = MilitaryAI.get_num_military_ships() # intent of the following calc is essentially # new_scouts_needed = min(need_cap_A, need_cap_B, base_need) - already_got_or_queued # where need_cap_A is to help prevent scouting needs from swamping military needs, and # need_cap_B is to help regulate investment into scouting while the empire is small. # These caps could perhaps instead be tied more directly to military priority and # total empire production. desired_number_of_scouts = int(min(4 + mil_ships/5, 4 + fo.currentTurn()/50.0, 2 + num_unexplored_systems**0.5)) scouts_needed = max(0, desired_number_of_scouts - (num_scouts + queued_scout_ships)) exploration_priority = int(40 * scouts_needed) print print "Number of Scouts: %s" % num_scouts print "Number of Unexplored systems: %s" % num_unexplored_systems print "Military size: %s" % mil_ships print "Priority for scouts: %s" % exploration_priority return exploration_priority
def cur_best_mil_ship_rating(include_designs=False): """Find the best military ship we have available in this turn and return its rating. :param include_designs: toggles if available designs are considered or only existing ships :return: float: rating of the best ship """ current_turn = fo.currentTurn() if current_turn in _best_ship_rating_cache: best_rating = _best_ship_rating_cache[current_turn] if include_designs: best_design_rating = ProductionAI.cur_best_military_design_rating() best_rating = max(best_rating, best_design_rating) return best_rating best_rating = 0.001 universe = fo.getUniverse() for fleet_id in FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.MILITARY): fleet = universe.getFleet(fleet_id) for ship_id in fleet.shipIDs: ship_rating = CombatRatingsAI.ShipCombatStats(ship_id).get_rating(enemy_stats=foAI.foAIstate.get_standard_enemy()) best_rating = max(best_rating, ship_rating) _best_ship_rating_cache[current_turn] = best_rating if include_designs: best_design_rating = ProductionAI.cur_best_military_design_rating() best_rating = max(best_rating, best_design_rating) return max(best_rating, 0.001)
def _calculate_learning_priority(): """Calculates the demand for techs learning category.""" turn = fo.currentTurn() if turn == 1: return 100 elif turn > 1: return 0
def get_prev_turn_uid(self): """ Return uid of previous turn. If called during the first turn after loading a saved game that had an AI version not yet using uids will return default value. """ return self.turn_uids.get(fo.currentTurn() - 1, '0')
def __report_system_threats(self): """Print a table with system threats to the logfile.""" current_turn = fo.currentTurn() if current_turn >= 100: return threat_table = Table([ Text('System'), Text('Vis.'), Float('Total'), Float('by Monsters'), Float('by Fleets'), Float('by Planets'), Float('1 jump away'), Float('2 jumps'), Float('3 jumps')], table_name="System Threat Turn %d" % current_turn ) universe = fo.getUniverse() for sys_id in universe.systemIDs: sys_status = self.systemStatus.get(sys_id, {}) system = universe.getSystem(sys_id) threat_table.add_row([ system, "Yes" if sys_status.get('currently_visible', False) else "No", sys_status.get('totalThreat', 0), sys_status.get('monsterThreat', 0), sys_status.get('fleetThreat', 0), sys_status.get('planetThreat', 0), sys_status.get('neighborThreat', 0.0), sys_status.get('jump2_threat', 0.0), sys_status.get('jump3_threat', 0.0), ]) info(threat_table)
def wrapper(): if get_aistate() is None: return func() else: cache = get_aistate().misc.setdefault('caches', {}).setdefault(func.__name__, {}) this_turn = fo.currentTurn() return cache[this_turn] if this_turn in cache else cache.setdefault(this_turn, func())
def calculateMilitaryPriority(): "calculates the demand for military ships by military targeted systems" universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID capitalID = PlanetUtilsAI.getCapital() if capitalID: homeworld = universe.getPlanet(capitalID) else: return 0# no capitol (not even a capitol-in-the-making), means can't produce any ships allottedInvasionTargets = 1+ int(fo.currentTurn()/25) targetPlanetIDs = [pid for pid, pscore, trp in AIstate.invasionTargets[:allottedInvasionTargets] ] + [pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs[:allottedColonyTargets] ] + [pid for pid, pscore in foAI.foAIstate.colonisableOutpostIDs[:allottedColonyTargets] ] mySystems = set( AIstate.popCtrSystemIDs ).union( AIstate.outpostSystemIDs ) targetSystems = set( PlanetUtilsAI.getSystems(targetPlanetIDs) ) curShipRating = curBestMilShipRating() unmetThreat = 0.0 currentTurn=fo.currentTurn() for sysID in mySystems.union(targetSystems) : status=foAI.foAIstate.systemStatus.get( sysID, {} ) myAttack, myHealth =0, 0 for fid in status.get('myfleets', []) : rating= foAI.foAIstate.fleetStatus.get(fid, {}).get('rating', {}) myAttack += rating.get('attack', 0) myHealth += rating.get('health', 0) myRating = myAttack*myHealth baseMonsterThreat = status.get('monsterThreat', 0) if currentTurn>200: monsterThreat = baseMonsterThreat elif currentTurn>100: if baseMonsterThreat <2000: monsterThreat = baseMonsterThreat else: monsterThreat = 2000 + (currentTurn/100.0 - 1) *(baseMonsterThreat-2000) else: monsterThreat = 0 if sysID in mySystems: threat = status.get('fleetThreat', 0) + status.get('planetThreat', 0) + 0.3* status.get('neighborThreat', 0) else: threat = status.get('fleetThreat', 0) + status.get('planetThreat', 0) + 0.1* status.get('neighborThreat', 0) unmetThreat += max( 0, threat + monsterThreat - myRating ) militaryPriority = int( 40 + max(0, 75*unmetThreat / curShipRating) ) return max( militaryPriority, 0)
def dump_universe(): """Dump the universe but not more than once per turn.""" cur_turn = fo.currentTurn() if (not hasattr(dump_universe, "last_dump") or dump_universe.last_dump < cur_turn): dump_universe.last_dump = cur_turn fo.getUniverse().dump() # goes to debug logger
def get_current_turn_uid(self): """ Return uid of current turn. """ return self.turn_uids.setdefault(fo.currentTurn(), self.generate_uid())
def _calculate_research_priority(): """Calculates the AI empire's demand for research.""" universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID current_turn = fo.currentTurn() enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) recent_enemies = [x for x in enemies_sighted if x > current_turn - 8] industry_priority = foAI.foAIstate.get_priority( PriorityType.RESOURCE_PRODUCTION) got_algo = tech_is_complete(AIDependencies.LRN_ALGO_ELEGANCE) got_quant = tech_is_complete(AIDependencies.LRN_QUANT_NET) research_queue_list = ResearchAI.get_research_queue_techs() orb_gen_tech = AIDependencies.PRO_ORBITAL_GEN got_orb_gen = tech_is_complete(orb_gen_tech) mgrav_prod_tech = AIDependencies.PRO_MICROGRAV_MAN got_mgrav_prod = tech_is_complete(mgrav_prod_tech) # got_solar_gen = tech_is_complete(AIDependencies.PRO_SOL_ORB_GEN) milestone_techs = [ "PRO_SENTIENT_AUTOMATION", "LRN_DISTRIB_THOUGHT", "LRN_QUANT_NET", "SHP_WEAPON_2_4", "SHP_WEAPON_3_2", "SHP_WEAPON_4_2" ] milestones_done = [ mstone for mstone in milestone_techs if tech_is_complete(mstone) ] print "Research Milestones accomplished at turn %d: %s" % (current_turn, milestones_done) total_pp = empire.productionPoints total_rp = empire.resourceProduction(fo.resourceType.research) industry_surge = ( foAI.foAIstate.character.may_surge_industry(total_pp, total_rp) and (((orb_gen_tech in research_queue_list[:2] or got_orb_gen) and state.have_gas_giant) or ((mgrav_prod_tech in research_queue_list[:2] or got_mgrav_prod) and state.have_asteroids)) and (not (len(AIstate.popCtrIDs) >= 12))) # get current industry production & Target owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire( universe.planetIDs) planets = map(universe.getPlanet, owned_planet_ids) target_rp = sum( map(lambda x: x.currentMeterValue(fo.meterType.targetResearch), planets)) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) style_index = foAI.foAIstate.character.preferred_research_cutoff([0, 1]) if foAI.foAIstate.character.may_maximize_research(): style_index += 1 cutoff_sets = [[25, 45, 70, 110], [30, 45, 70, 150], [25, 40, 80, 160]] cutoffs = cutoff_sets[style_index] settings = [[1.3, .7, .5, .4, .35], [1.4, 0.8, 0.6, 0.5, 0.35], [1.4, 0.8, 0.6, 0.5, 0.4]][style_index] if (current_turn < cutoffs[0]) or (not got_algo) or ( (style_index == 0) and not got_orb_gen and (current_turn < cutoffs[1])): research_priority = settings[ 0] * industry_priority # high research at beginning of game to get easy gro tech and to get research booster Algotrithmic Elegance elif (not got_orb_gen) or (current_turn < cutoffs[1]): research_priority = settings[1] * industry_priority # med-high research elif current_turn < cutoffs[2]: research_priority = settings[2] * industry_priority # med-high industry elif current_turn < cutoffs[3]: research_priority = settings[3] * industry_priority # med-high industry else: research_queue = list(empire.researchQueue) research_priority = settings[ 4] * industry_priority # high industry , low research if len(research_queue) == 0: research_priority = 0 # done with research elif len(research_queue) < 5 and research_queue[-1].allocation > 0: research_priority = len( research_queue ) * 0.01 * industry_priority # barely not done with research elif len(research_queue) < 10 and research_queue[-1].allocation > 0: research_priority = ( 4 + 2 * len(research_queue) ) * 0.01 * industry_priority # almost done with research elif len(research_queue) < 20 and research_queue[int( len(research_queue) / 2)].allocation > 0: research_priority *= 0.7 # closing in on end of research if industry_surge: if galaxy_is_sparse and not any(enemies_sighted): research_priority *= 0.5 else: research_priority *= 0.8 if ((tech_is_complete("SHP_WEAPON_2_4") or tech_is_complete("SHP_WEAPON_4_1")) and tech_is_complete(AIDependencies.PROD_AUTO_NAME)): # industry_factor = [ [0.25, 0.2], [0.3, 0.25], [0.3, 0.25] ][style_index ] # researchPriority = min(researchPriority, industry_factor[got_solar_gen]*industryPriority) research_priority *= 0.9 if got_quant: research_priority = min(research_priority + 0.1 * industry_priority, research_priority * 1.3) research_priority = int(research_priority) print "Research Production (current/target) : ( %.1f / %.1f )" % ( total_rp, target_rp) print "Priority for Research: %d (new target ~ %d RP)" % ( research_priority, total_pp * research_priority / industry_priority) if len(enemies_sighted) < ( 2 + current_turn / 20.0): # TODO: adjust for colonisation priority research_priority *= 1.2 if (current_turn > 20) and (len(recent_enemies) > 3): research_priority *= 0.8 return research_priority
def issue_fleet_orders(self): """issues AIFleetOrders which can be issued in system and moves to next one if is possible""" # TODO: priority order_completed = True debug( "\nChecking orders for fleet %s (on turn %d), with mission type %s and target %s", self.fleet.get_object(), fo.currentTurn(), self.type or "No mission", self.target or "No Target", ) if MissionType.INVASION == self.type: self._check_retarget_invasion() just_issued_move_order = False last_move_target_id = INVALID_ID # Note: the following abort check somewhat assumes only one major mission type for fleet_order in self.orders: if isinstance( fleet_order, (OrderColonize, OrderOutpost, OrderInvade)) and self._check_abort_mission(fleet_order): return aistate = get_aistate() for fleet_order in self.orders: if just_issued_move_order and self.fleet.get_object( ).systemID != last_move_target_id: # having just issued a move order, we will normally stop issuing orders this turn, except that if there # are consecutive move orders we will consider moving through the first destination rather than stopping # Without the below noinspection directive, PyCharm is concerned about the 2nd part of the test # noinspection PyTypeChecker if not isinstance(fleet_order, OrderMove) or self.need_to_pause_movement( last_move_target_id, fleet_order): break debug("Checking order: %s" % fleet_order) self.check_mergers(context=str(fleet_order)) if fleet_order.can_issue_order(verbose=False): # only move if all other orders completed if isinstance(fleet_order, OrderMove) and order_completed: debug("Issuing fleet order %s" % fleet_order) fleet_order.issue_order() just_issued_move_order = True last_move_target_id = fleet_order.target.id elif not isinstance(fleet_order, OrderMove): debug("Issuing fleet order %s" % fleet_order) fleet_order.issue_order() else: debug( "NOT issuing (even though can_issue) fleet order %s" % fleet_order) status_words = tuple( ["not", ""][_s] for _s in [fleet_order.order_issued, fleet_order.executed]) debug("Order %s issued and %s fully executed." % status_words) if not fleet_order.executed: order_completed = False else: # check that we're not held up by a Big Monster if fleet_order.order_issued: # A previously issued order that wasn't instantly executed must have had cirumstances change so that # the order can't currently be reissued (or perhaps simply a savegame has been reloaded on the same # turn the order was issued). if not fleet_order.executed: order_completed = False # Go on to the next order. continue debug("CAN'T issue fleet order %s because:" % fleet_order) fleet_order.can_issue_order(verbose=True) if isinstance(fleet_order, OrderMove): this_system_id = fleet_order.target.id this_status = aistate.systemStatus.setdefault( this_system_id, {}) threat_threshold = fo.currentTurn( ) * MilitaryAI.cur_best_mil_ship_rating() / 4.0 if this_status.get("monsterThreat", 0) > threat_threshold: # if this move order is not this mil fleet's final destination, and blocked by Big Monster, # release and hope for more effective reassignment if (self.type not in (MissionType.MILITARY, MissionType.SECURE) or fleet_order != self.orders[-1]): debug( "Aborting mission due to being blocked by Big Monster at system %d, threat %d" % (this_system_id, aistate.systemStatus[this_system_id] ["monsterThreat"])) debug("Full set of orders were:") for this_order in self.orders: debug(" - %s" % this_order) self.clear_fleet_orders() self.clear_target() return break # do not order the next order until this one is finished. else: # went through entire order list if order_completed: debug("Final order is completed") orders = self.orders last_order = orders[-1] if orders else None universe = fo.getUniverse() if last_order and isinstance(last_order, OrderColonize): planet = universe.getPlanet(last_order.target.id) 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 not planet.initialMeterValue( fo.meterType.population): warning( "Fleet %s has tentatively completed its " "colonize mission but will wait to confirm population.", self.fleet, ) debug(" Order details are %s" % last_order) debug( " Order is valid: %s; issued: %s; executed: %s" % (last_order.is_valid(), last_order.order_issued, last_order.executed)) if not last_order.is_valid(): source_target = last_order.fleet target_target = last_order.target debug( " source target validity: %s; target target validity: %s " % (bool(source_target), bool(target_target))) return # colonize order must not have completed yet clear_all = True last_sys_target = INVALID_ID if last_order and isinstance(last_order, OrderMilitary): last_sys_target = last_order.target.id # not doing this until decide a way to release from a SECURE mission # if (MissionType.SECURE == self.type) or secure_targets = set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs) if last_sys_target in secure_targets: # consider a secure mission if last_sys_target in AIstate.colonyTargetedSystemIDs: secure_type = "Colony" elif last_sys_target in AIstate.outpostTargetedSystemIDs: secure_type = "Outpost" elif last_sys_target in AIstate.invasionTargetedSystemIDs: secure_type = "Invasion" else: secure_type = "Unidentified" debug( "Fleet %d has completed initial stage of its mission " "to secure system %d (targeted for %s), " "may release a portion of ships" % (self.fleet.id, last_sys_target, secure_type)) clear_all = False # for PROTECT_REGION missions, only release fleet if no more threat if self.type == MissionType.PROTECT_REGION: # use military logic code below to determine if can release # any or even all of the ships. clear_all = False last_sys_target = self.target.id debug( "Check if PROTECT_REGION mission with target %d is finished.", last_sys_target) fleet_id = self.fleet.id if clear_all: if orders: debug( "Fleet %d has completed its mission; clearing all orders and targets." % self.fleet.id) debug("Full set of orders were:") for this_order in orders: debug("\t\t %s" % this_order) self.clear_fleet_orders() self.clear_target() if aistate.get_fleet_role(fleet_id) in ( MissionType.MILITARY, MissionType.SECURE): allocations = MilitaryAI.get_military_fleets( mil_fleets_ids=[fleet_id], try_reset=False, thisround="Fleet %d Reassignment" % fleet_id) if allocations: MilitaryAI.assign_military_fleets_to_systems( use_fleet_id_list=[fleet_id], allocations=allocations) else: # no orders debug("No Current Orders") else: potential_threat = combine_ratings( MilitaryAI.get_system_local_threat(last_sys_target), MilitaryAI.get_system_neighbor_threat(last_sys_target), ) threat_present = potential_threat > 0 debug("Fleet threat present? %s", threat_present) target_system = universe.getSystem(last_sys_target) if not threat_present and target_system: for pid in target_system.planetIDs: planet = universe.getPlanet(pid) if (planet and planet.owner != fo.empireID() and planet.currentMeterValue( fo.meterType.maxDefense) > 0): debug("Found local planetary threat: %s", planet) threat_present = True break if not threat_present: debug( "No current threat in target system; releasing a portion of ships." ) # at least first stage of current task is done; # release extra ships for potential other deployments new_fleets = FleetUtilsAI.split_fleet(self.fleet.id) if self.type == MissionType.PROTECT_REGION: self.clear_fleet_orders() self.clear_target() new_fleets.append(self.fleet.id) else: debug( "Threat remains in target system; Considering to release some ships." ) new_fleets = [] fleet_portion_to_remain = self._portion_of_fleet_needed_here( ) if fleet_portion_to_remain >= 1: debug( "Can not release fleet yet due to large threat." ) elif fleet_portion_to_remain > 0: debug( "Not all ships are needed here - considering releasing a few" ) # TODO: Rate against specific enemy threat cause fleet_remaining_rating = CombatRatingsAI.get_fleet_rating( fleet_id) fleet_min_rating = fleet_portion_to_remain * fleet_remaining_rating debug("Starting rating: %.1f, Target rating: %.1f", fleet_remaining_rating, fleet_min_rating) allowance = CombatRatingsAI.rating_needed( fleet_remaining_rating, fleet_min_rating) debug( "May release ships with total rating of %.1f", allowance) ship_ids = list(self.fleet.get_object().shipIDs) for ship_id in ship_ids: ship_rating = CombatRatingsAI.get_ship_rating( ship_id) debug( "Considering to release ship %d with rating %.1f", ship_id, ship_rating) if ship_rating > allowance: debug( "Remaining rating insufficient. Not released." ) continue debug("Splitting from fleet.") new_fleet_id = FleetUtilsAI.split_ship_from_fleet( fleet_id, ship_id) if assertion_fails( new_fleet_id and new_fleet_id != INVALID_ID): break new_fleets.append(new_fleet_id) fleet_remaining_rating = CombatRatingsAI.rating_difference( fleet_remaining_rating, ship_rating) allowance = CombatRatingsAI.rating_difference( fleet_remaining_rating, fleet_min_rating) debug( "Remaining fleet rating: %.1f - Allowance: %.1f", fleet_remaining_rating, allowance) if new_fleets: aistate.get_fleet_role(fleet_id, force_new=True) aistate.update_fleet_rating(fleet_id) aistate.ensure_have_fleet_missions(new_fleets) else: debug( "Planetary defenses are deemed sufficient. Release fleet." ) new_fleets = FleetUtilsAI.split_fleet( self.fleet.id) new_military_fleets = [] for fleet_id in new_fleets: if aistate.get_fleet_role( fleet_id) in COMBAT_MISSION_TYPES: new_military_fleets.append(fleet_id) allocations = [] if new_military_fleets: allocations = MilitaryAI.get_military_fleets( mil_fleets_ids=new_military_fleets, try_reset=False, thisround="Fleet Reassignment %s" % new_military_fleets, ) if allocations: MilitaryAI.assign_military_fleets_to_systems( use_fleet_id_list=new_military_fleets, allocations=allocations)
def log_peace_request(self, initiating_empire_id, recipient_empire_id): """Keep a record of peace requests made or received by this empire.""" peace_requests = self.diplomatic_logs.setdefault('peace_requests', {}) log_index = (initiating_empire_id, recipient_empire_id) peace_requests.setdefault(log_index, []).append(fo.currentTurn())
def _calculate_military_priority(): """Calculates the demand for military ships by military targeted systems.""" global unmetThreat universe = fo.getUniverse() capital_id = PlanetUtilsAI.get_capital() if capital_id is None or capital_id == INVALID_ID: return 0 # no capitol (not even a capitol-in-the-making), means can't produce any ships have_l1_weaps = (tech_is_complete("SHP_WEAPON_1_4") or (tech_is_complete("SHP_WEAPON_1_3") and tech_is_complete("SHP_MIL_ROBO_CONT")) or tech_is_complete("SHP_WEAPON_2_1") or tech_is_complete("SHP_WEAPON_4_1")) have_l2_weaps = tech_is_complete("SHP_WEAPON_2_3") or tech_is_complete( "SHP_WEAPON_4_1") aistate = get_aistate() enemies_sighted = aistate.misc.get("enemies_sighted", {}) target_planet_ids = ([ pid for pid, pscore, trp in AIstate.invasionTargets[:allotted_invasion_targets()] ] + [ pid for pid, pscore in list(aistate.colonisablePlanetIDs.items()) [:allottedColonyTargets] ] + [ pid for pid, pscore in list(aistate.colonisableOutpostIDs.items()) [:allottedColonyTargets] ]) my_systems = set(get_owned_planets()) target_systems = set(PlanetUtilsAI.get_systems(target_planet_ids)) cur_ship_rating = cur_best_military_design_rating() current_turn = fo.currentTurn() ships_needed = 0 defense_ships_needed = 0 ships_needed_allocation = [] for sys_id in my_systems.union(target_systems): status = aistate.systemStatus.get(sys_id, {}) my_rating = status.get("myFleetRating", 0) my_defenses = status.get("mydefenses", {}).get("overall", 0) base_monster_threat = status.get("monsterThreat", 0) # scale monster threat so that in early - mid game big monsters don't over-drive military production monster_threat = base_monster_threat if current_turn > 200: pass elif current_turn > 100: if base_monster_threat >= 2000: monster_threat = 2000 + (current_turn / 100.0 - 1) * (base_monster_threat - 2000) elif current_turn > 30: if base_monster_threat >= 2000: monster_threat = 0 else: if base_monster_threat > 200: monster_threat = 0 if sys_id in my_systems: threat_root = (status.get("fleetThreat", 0)**0.5 + 0.8 * status.get("max_neighbor_threat", 0)**0.5 + 0.2 * status.get("neighborThreat", 0)**0.5 + monster_threat**0.5 + status.get("planetThreat", 0)**0.5) else: threat_root = (status.get("fleetThreat", 0)**0.5 + monster_threat**0.5 + status.get("planetThreat", 0)**0.5) ships_needed_here = math.ceil( (max(0, (threat_root - (my_rating**0.5 + my_defenses**0.5)))**2) / cur_ship_rating) ships_needed += ships_needed_here ships_needed_allocation.append( (universe.getSystem(sys_id), ships_needed_here)) if sys_id in my_systems: defense_ships_needed += ships_needed_here part1 = min(1 * fo.currentTurn(), 40) part2 = max(0, int(75 * ships_needed)) military_priority = part1 + part2 if not have_l1_weaps: military_priority /= 2.0 elif not (have_l2_weaps and enemies_sighted): military_priority /= 1.5 fmt_string = "Calculating Military Priority: min(t,40) + max(0,75 * ships_needed) \n\t Priority: %d \t ships_needed: %d \t defense_ships_needed: %d \t curShipRating: %.0f \t l1_weaps: %s \t enemies_sighted: %s" debug(fmt_string % (military_priority, ships_needed, defense_ships_needed, cur_ship_rating, have_l1_weaps, enemies_sighted)) debug("Source of milship demand: %s" % ships_needed_allocation) military_priority *= aistate.character.military_priority_scaling() return max(military_priority, 0)
def _calculate_planet_colonization_rating( planet_id: PlanetId, mission_type: MissionType, species_name: SpeciesName, detail: list, empire_research_list: Sequence, ) -> float: empire = fo.getEmpire() retval = 0 character = get_aistate().character discount_multiplier = character.preferred_discount_multiplier([30.0, 40.0]) species = fo.getSpecies(species_name) species_foci = [] and species and list(species.foci) tag_list = list(species.tags) if species else [] pilot_val = pilot_rating = 0 if species and species.canProduceShips: pilot_val = pilot_rating = rate_piloting_tag(species_name) if pilot_val > best_pilot_rating(): pilot_val *= 2 if pilot_val > 2: retval += discount_multiplier * 5 * pilot_val detail.append("Pilot Val %.1f" % (discount_multiplier * 5 * pilot_val)) if empire.productionPoints < 100: backup_factor = 0.0 else: backup_factor = min(1.0, (empire.productionPoints / 200.0)**2) universe = fo.getUniverse() capital_id = PlanetUtilsAI.get_capital() homeworld = universe.getPlanet(capital_id) planet = universe.getPlanet(planet_id) prospective_invasion_targets = [ pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allotted_invasion_targets()] if pscore > InvasionAI.MIN_INVASION_SCORE ] if species_name != planet.speciesName and planet.speciesName and mission_type != MissionType.INVASION: return 0 this_sysid = planet.systemID if homeworld: home_system_id = homeworld.systemID eval_system_id = this_sysid if home_system_id != INVALID_ID and eval_system_id != INVALID_ID: least_jumps = universe.jumpDistance(home_system_id, eval_system_id) if least_jumps == -1: # indicates no known path return 0.0 if planet is None: vis_map = universe.getVisibilityTurnsMap(planet_id, empire.empireID) debug("Planet %d object not available; visMap: %s" % (planet_id, vis_map)) return 0 # only count existing presence if not target planet # TODO: consider neighboring sytems for smaller contribution, and bigger contributions for # local colonies versus local outposts locally_owned_planets = [ lpid for lpid in get_owned_planets_in_system(this_sysid) if lpid != planet_id ] planets_with_species = get_inhabited_planets() locally_owned_pop_ctrs = [ lpid for lpid in locally_owned_planets if lpid in planets_with_species ] # triple count pop_ctrs existing_presence = len( locally_owned_planets) + 2 * len(locally_owned_pop_ctrs) system = universe.getSystem(this_sysid) sys_supply = get_system_supply(this_sysid) planet_supply = AIDependencies.supply_by_size.get(planet.size, 0) bld_types = set( universe.getBuilding(bldg).buildingTypeName for bldg in planet.buildingIDs).intersection( AIDependencies.building_supply) planet_supply += sum( AIDependencies.building_supply[bld_type].get(int(psize), 0) for psize in [-1, planet.size] for bld_type in bld_types) supply_specials = set(planet.specials).union(system.specials).intersection( AIDependencies.SUPPLY_MOD_SPECIALS) planet_supply += sum( AIDependencies.SUPPLY_MOD_SPECIALS[_special].get(int(psize), 0) for _special in supply_specials for psize in [-1, planet.size]) ind_tag_mod = get_species_tag_value(species_name, Tags.INDUSTRY) res_tag_mod = get_species_tag_value(species_name, Tags.RESEARCH) if species: supply_tag_mod = get_species_tag_value(species_name, Tags.SUPPLY) else: supply_tag_mod = 0 # determine the potential supply provided by owning this planet, and if the planet is currently populated by # the evaluated species, then save this supply value in a cache. # The system supply value can be negative (indicates the respective number of starlane jumps to the closest supplied # system). So if this total planet supply value is non-negative, then the planet would be supply connected (to at # least a portion of the existing empire) if the planet becomes owned by the AI. planet_supply += supply_tag_mod planet_supply = max(planet_supply, 0) # planets can't have negative supply if planet.speciesName == species_name: update_planet_supply(planet_id, planet_supply + sys_supply) threat_factor = _determine_colony_threat_factor(planet_id, species_name, existing_presence) sys_partial_vis_turn = get_partial_visibility_turn(this_sysid) planet_partial_vis_turn = get_partial_visibility_turn(planet_id) if planet_partial_vis_turn < sys_partial_vis_turn: # last time we had partial vis of the system, the planet was stealthed to us # print "Colonization AI couldn't get current info on planet id %d (was stealthed at last sighting)" % planet_id return 0 # TODO: track detection strength, order new scouting when it goes up star_bonus = 0 colony_star_bonus = 0 research_bonus = 0 growth_val = 0 fixed_ind = 0 fixed_res = 0 if system: already_got_this_one = this_sysid in get_owned_planets() # TODO: Should probably consider pilot rating also for Phototropic species if "PHOTOTROPHIC" not in tag_list and pilot_rating >= best_pilot_rating( ): if system.starType == fo.starType.red and tech_is_complete( "LRN_STELLAR_TOMOGRAPHY"): star_bonus += 40 * discount_multiplier # can be used for artif'l black hole and solar hull detail.append( "Red Star for Art Black Hole for solar hull %.1f" % (40 * discount_multiplier)) elif system.starType == fo.starType.blackHole and tech_is_complete( "SHP_FRC_ENRG_COMP"): star_bonus += 100 * discount_multiplier # can be used for solar hull detail.append("Black Hole for solar hull %.1f" % (100 * discount_multiplier)) if tech_is_complete("PRO_SOL_ORB_GEN" ) or "PRO_SOL_ORB_GEN" in empire_research_list[:5]: if system.starType in [fo.starType.blue, fo.starType.white]: if not has_claimed_star(fo.starType.blue, fo.starType.white): star_bonus += 20 * discount_multiplier detail.append("PRO_SOL_ORB_GEN BW %.1f" % (20 * discount_multiplier)) elif not already_got_this_one: # still has extra value as an alternate location for solar generators star_bonus += 10 * discount_multiplier * backup_factor detail.append("PRO_SOL_ORB_GEN BW Backup Location %.1f" % (10 * discount_multiplier * backup_factor)) elif fo.currentTurn() > 100: # lock up this whole system pass # starBonus += 5 # TODO: how much? # detail.append("PRO_SOL_ORB_GEN BW LockingDownSystem %.1f"%5) if system.starType in [fo.starType.yellow, fo.starType.orange]: if not has_claimed_star(fo.starType.blue, fo.starType.white, fo.starType.yellow, fo.starType.orange): star_bonus += 10 * discount_multiplier detail.append("PRO_SOL_ORB_GEN YO %.1f" % (10 * discount_multiplier)) else: pass # starBonus +=2 # still has extra value as an alternate location for solar generators # detail.append("PRO_SOL_ORB_GEN YO Backup %.1f" % 2) if system.starType in [fo.starType.blackHole ] and fo.currentTurn() > 100: if not already_got_this_one: # whether have tech yet or not, assign some base value star_bonus += 10 * discount_multiplier * backup_factor detail.append("Black Hole %.1f" % (10 * discount_multiplier * backup_factor)) else: star_bonus += 5 * discount_multiplier * backup_factor detail.append("Black Hole Backup %.1f" % (5 * discount_multiplier * backup_factor)) if tech_is_complete(AIDependencies.PRO_SOL_ORB_GEN ): # start valuing as soon as PRO_SOL_ORB_GEN done if system.starType == fo.starType.blackHole: # pretty rare planets, good for generator this_val = 0.5 * max(population_with_industry_focus(), 20) * discount_multiplier if not has_claimed_star(fo.starType.blackHole): star_bonus += this_val detail.append("PRO_SINGULAR_GEN %.1f" % this_val) elif not is_system_star_claimed(system): # still has extra value as an alternate location for generators & for blocking enemies generators star_bonus += this_val * backup_factor detail.append("PRO_SINGULAR_GEN Backup %.1f" % (this_val * backup_factor)) elif system.starType == fo.starType.red and not has_claimed_star( fo.starType.blackHole): rfactor = (1.0 + count_claimed_stars(fo.starType.red))**-2 star_bonus += 40 * discount_multiplier * backup_factor * rfactor # can be used for artif'l black hole detail.append( "Red Star for Art Black Hole %.1f" % (40 * discount_multiplier * backup_factor * rfactor)) if tech_is_complete( "PRO_NEUTRONIUM_EXTRACTION" ) or "PRO_NEUTRONIUM_EXTRACTION" in empire_research_list[:8]: if system.starType in [fo.starType.neutron]: if not has_claimed_star(fo.starType.neutron): star_bonus += 80 * discount_multiplier # pretty rare planets, good for armor detail.append("PRO_NEUTRONIUM_EXTRACTION %.1f" % (80 * discount_multiplier)) else: # still has extra value as an alternate location for generators & for bnlocking enemies generators star_bonus += 20 * discount_multiplier * backup_factor detail.append("PRO_NEUTRONIUM_EXTRACTION Backup %.1f" % (20 * discount_multiplier * backup_factor)) if tech_is_complete( "SHP_ENRG_BOUND_MAN" ) or "SHP_ENRG_BOUND_MAN" in empire_research_list[:6]: # TODO: base this on pilot val, and also consider red stars if system.starType in [fo.starType.blackHole, fo.starType.blue]: init_val = 100 * discount_multiplier * (pilot_val or 1) if not has_claimed_star(fo.starType.blackHole, fo.starType.blue): colony_star_bonus += init_val # pretty rare planets, good for energy shipyards detail.append("SHP_ENRG_BOUND_MAN %.1f" % init_val) elif not is_system_star_claimed(system): # still has extra value as an alternate location for energy shipyard colony_star_bonus += 0.5 * init_val * backup_factor detail.append("SHP_ENRG_BOUND_MAN Backup %.1f" % (0.5 * init_val * backup_factor)) retval += star_bonus detail.append("star_bonus=%.1f" % star_bonus) planet_specials = list(planet.specials) if "ECCENTRIC_ORBIT_SPECIAL" in planet.specials: fixed_res += discount_multiplier * 6 detail.append("ECCENTRIC_ORBIT_SPECIAL %.1f" % (discount_multiplier * 6)) if mission_type == MissionType.OUTPOST or ( mission_type == MissionType.INVASION and not species_name): if "ANCIENT_RUINS_SPECIAL" in planet.specials: # TODO: add value for depleted ancient ruins retval += discount_multiplier * 30 detail.append("Undepleted Ruins %.1f" % (discount_multiplier * 30)) for special in planet_specials: if "_NEST_" in special: nest_val = get_nest_rating( special, 5.0 ) * discount_multiplier # get an outpost on the nest quick retval += nest_val detail.append("%s %.1f" % (special, nest_val)) elif special == "FORTRESS_SPECIAL": fort_val = 10 * discount_multiplier retval += fort_val detail.append("%s %.1f" % (special, fort_val)) elif special == "HONEYCOMB_SPECIAL": honey_val = 0.3 * (AIDependencies.HONEYCOMB_IND_MULTIPLIER * AIDependencies.INDUSTRY_PER_POP * population_with_industry_focus() * discount_multiplier) retval += honey_val detail.append("%s %.1f" % (special, honey_val)) if planet.size == fo.planetSize.asteroids: ast_val = 0 if system: for pid in system.planetIDs: other_planet = universe.getPlanet(pid) if other_planet.size == fo.planetSize.asteroids: if pid == planet_id: continue elif pid < planet_id and planet.unowned: ast_val = 0 break elif other_planet.speciesName: if other_planet.owner == empire.empireID: ownership_factor = 1.0 elif pid in prospective_invasion_targets: ownership_factor = character.secondary_valuation_factor_for_invasion_targets( ) else: ownership_factor = 0.0 ast_val += ownership_factor * _base_asteroid_mining_val( ) * discount_multiplier retval += ast_val if ast_val > 0: detail.append("AsteroidMining %.1f" % ast_val) ast_val = 0 if tech_is_complete("SHP_ASTEROID_HULLS"): per_ast = 20 elif tech_is_complete("CON_ORBITAL_CON"): per_ast = 5 else: per_ast = 0.1 if system: for pid in system.planetIDs: other_planet = universe.getPlanet(pid) if other_planet.size == fo.planetSize.asteroids: if pid == planet_id: continue elif pid < planet_id and planet.unowned: ast_val = 0 break elif other_planet.speciesName: other_species = fo.getSpecies(other_planet.speciesName) if other_species and other_species.canProduceShips: if other_planet.owner == empire.empireID: ownership_factor = 1.0 elif pid in prospective_invasion_targets: ownership_factor = character.secondary_valuation_factor_for_invasion_targets( ) else: ownership_factor = 0.0 ast_val += ownership_factor * per_ast * discount_multiplier retval += ast_val if ast_val > 0: detail.append("AsteroidShipBuilding %.1f" % ast_val) # We will assume that if any GG in the system is populated, they all will wind up populated; cannot then hope # to build a GGG on a non-populated GG populated_gg_factor = 1.0 per_gg = 0 if planet.size == fo.planetSize.gasGiant: # TODO: Given current industry calc approach, consider bringing this max val down to actual max val of 10 if tech_is_complete("PRO_ORBITAL_GEN"): per_gg = 20 elif tech_is_complete("CON_ORBITAL_CON"): per_gg = 10 if species_name: populated_gg_factor = 0.5 else: per_gg = 5 if system: gg_list = [] orb_gen_val = 0 gg_detail = [] for pid in system.planetIDs: other_planet = universe.getPlanet(pid) if other_planet.size == fo.planetSize.gasGiant: gg_list.append(pid) if other_planet.speciesName: populated_gg_factor = 0.5 if (pid != planet_id and other_planet.owner == empire.empireID and FocusType.FOCUS_INDUSTRY in list(other_planet.availableFoci) + [other_planet.focus]): orb_gen_val += per_gg * discount_multiplier # Note, this reported value may not take into account a later adjustment from a populated gg gg_detail.append("GGG for %s %.1f" % (other_planet.name, discount_multiplier * per_gg * populated_gg_factor)) if planet_id in sorted(gg_list)[:1]: retval += orb_gen_val * populated_gg_factor detail.extend(gg_detail) else: detail.append("Won't GGG") if existing_presence: retval = (retval + existing_presence * _get_defense_value(species_name)) * 1.5 detail.append("preexisting system colony => %.1f" % retval) # Fixme - sys_supply is always <= 0 leading to incorrect supply bonus score supply_val = 0 if sys_supply < 0: if sys_supply + planet_supply >= 0: supply_val += 30 * (planet_supply - max(-3, sys_supply)) else: retval += 30 * (planet_supply + sys_supply) # a penalty elif planet_supply > sys_supply and ( sys_supply < 2): # TODO: check min neighbor supply supply_val += 25 * (planet_supply - sys_supply) detail.append("sys_supply: %d, planet_supply: %d, supply_val: %.0f" % (sys_supply, planet_supply, supply_val)) retval += supply_val if threat_factor < 1.0: threat_factor = _revise_threat_factor(threat_factor, retval, this_sysid, MINIMUM_COLONY_SCORE) retval *= threat_factor detail.append("threat reducing value by %3d %%" % (100 * (1 - threat_factor))) return int(retval) else: # colonization mission if not species: return 0 supply_val = 0 if "ANCIENT_RUINS_SPECIAL" in planet.specials: retval += discount_multiplier * 50 detail.append("Undepleted Ruins %.1f" % (discount_multiplier * 50)) if "HONEYCOMB_SPECIAL" in planet.specials: honey_val = (AIDependencies.HONEYCOMB_IND_MULTIPLIER * AIDependencies.INDUSTRY_PER_POP * population_with_industry_focus() * discount_multiplier) if FocusType.FOCUS_INDUSTRY not in species_foci: honey_val *= -0.3 # discourage settlement by colonizers not able to use Industry Focus retval += honey_val detail.append("%s %.1f" % ("HONEYCOMB_SPECIAL", honey_val)) # Fixme - sys_supply is always <= 0 leading to incorrect supply bonus score if sys_supply <= 0: if sys_supply + planet_supply >= 0: supply_val = 40 * (planet_supply - max(-3, sys_supply)) else: supply_val = 200 * (planet_supply + sys_supply) # a penalty if species_name == "SP_SLY": # Sly are essentially stuck with lousy supply, so don't penalize for that supply_val = 0 elif planet_supply > sys_supply == 1: # TODO: check min neighbor supply supply_val = 20 * (planet_supply - sys_supply) # temp hack, AI builds too many colonies on tiny planets, reduce supply val supply_val *= 0.5 detail.append("sys_supply: %d, planet_supply: %d, supply_val: %.0f" % (sys_supply, planet_supply, supply_val)) # if AITags != "": # print "Species %s has AITags %s"%(specName, AITags) retval += fixed_res retval += colony_star_bonus asteroid_bonus = 0 gas_giant_bonus = 0 flat_industry = 0 mining_bonus = 0 per_ggg = 10 asteroid_factor = 0.0 gg_factor = 0.0 ast_shipyard_name = "" if system and FocusType.FOCUS_INDUSTRY in species.foci: for pid in system.planetIDs: if pid == planet_id: continue p2 = universe.getPlanet(pid) if p2: if p2.size == fo.planetSize.asteroids: this_factor = 0.0 if p2.owner == empire.empireID: this_factor = 1.0 elif p2.unowned: this_factor = 0.5 elif pid in prospective_invasion_targets: this_factor = character.secondary_valuation_factor_for_invasion_targets( ) if this_factor > asteroid_factor: asteroid_factor = this_factor ast_shipyard_name = p2.name if p2.size == fo.planetSize.gasGiant: if p2.owner == empire.empireID: gg_factor = max(gg_factor, 1.0) elif p2.unowned: gg_factor = max(gg_factor, 0.5) elif pid in prospective_invasion_targets: gg_factor = max( gg_factor, character. secondary_valuation_factor_for_invasion_targets( )) if asteroid_factor > 0.0: if tech_is_complete( "PRO_MICROGRAV_MAN" ) or "PRO_MICROGRAV_MAN" in empire_research_list[:10]: flat_industry += 2 * asteroid_factor # will go into detailed industry projection detail.append("Asteroid mining ~ %.1f" % (5 * asteroid_factor * discount_multiplier)) if tech_is_complete( "SHP_ASTEROID_HULLS" ) or "SHP_ASTEROID_HULLS" in empire_research_list[:11]: if species and species.canProduceShips: asteroid_bonus = 30 * discount_multiplier * pilot_val detail.append("Asteroid ShipBuilding from %s %.1f" % (ast_shipyard_name, discount_multiplier * 30 * pilot_val)) if gg_factor > 0.0: if tech_is_complete( "PRO_ORBITAL_GEN" ) or "PRO_ORBITAL_GEN" in empire_research_list[:5]: flat_industry += per_ggg * gg_factor # will go into detailed industry projection detail.append("GGG ~ %.1f" % (per_ggg * gg_factor * discount_multiplier)) # calculate the maximum population of the species on that planet. if planet.speciesName not in AIDependencies.SPECIES_FIXED_POPULATION: max_pop_size = calc_max_pop(planet, species, detail) else: max_pop_size = AIDependencies.SPECIES_FIXED_POPULATION[ planet.speciesName] detail.append("Fixed max population of %.2f" % max_pop_size) if max_pop_size <= 0: detail.append( "Non-positive population projection for species '%s', so no colonization value" % (species and species.name)) return 0 for special in [ "MINERALS_SPECIAL", "CRYSTALS_SPECIAL", "ELERIUM_SPECIAL" ]: if special in planet_specials: mining_bonus += 1 has_blackhole = has_claimed_star(fo.starType.blackHole) ind_tech_map_flat = AIDependencies.INDUSTRY_EFFECTS_FLAT_NOT_MODIFIED_BY_SPECIES ind_tech_map_before_species_mod = AIDependencies.INDUSTRY_EFFECTS_PER_POP_MODIFIED_BY_SPECIES ind_tech_map_after_species_mod = AIDependencies.INDUSTRY_EFFECTS_PER_POP_NOT_MODIFIED_BY_SPECIES ind_mult = 1 for tech in ind_tech_map_before_species_mod: if tech_is_complete(tech) and ( tech != AIDependencies.PRO_SINGULAR_GEN or has_blackhole): ind_mult += ind_tech_map_before_species_mod[tech] ind_mult = ind_mult * max( ind_tag_mod, 0.5 * (ind_tag_mod + res_tag_mod)) # TODO: report an actual calc for research value for tech in ind_tech_map_after_species_mod: if tech_is_complete(tech) and ( tech != AIDependencies.PRO_SINGULAR_GEN or has_blackhole): ind_mult += ind_tech_map_after_species_mod[tech] max_ind_factor = 0 for tech in ind_tech_map_flat: if tech_is_complete(tech): fixed_ind += discount_multiplier * ind_tech_map_flat[tech] if FocusType.FOCUS_INDUSTRY in species.foci: if "TIDAL_LOCK_SPECIAL" in planet.specials: ind_mult += 1 max_ind_factor += AIDependencies.INDUSTRY_PER_POP * mining_bonus max_ind_factor += AIDependencies.INDUSTRY_PER_POP * ind_mult cur_pop = 1.0 # assume an initial colonization value if planet.speciesName != "": cur_pop = planet.currentMeterValue(fo.meterType.population) elif tech_is_complete("GRO_LIFECYCLE_MAN"): cur_pop = 3.0 cur_industry = planet.currentMeterValue(fo.meterType.industry) ind_val = _project_ind_val(cur_pop, max_pop_size, cur_industry, max_ind_factor, flat_industry, discount_multiplier) detail.append("ind_val %.1f" % ind_val) # used to give preference to closest worlds for special in [ spec for spec in planet_specials if spec in AIDependencies.metabolismBoosts ]: # TODO: also consider potential future benefit re currently unpopulated planets gbonus = (discount_multiplier * AIDependencies.INDUSTRY_PER_POP * ind_mult * empire_metabolisms.get( AIDependencies.metabolismBoosts[special], 0) ) # due to growth applicability to other planets growth_val += gbonus detail.append("Bonus for %s: %.1f" % (special, gbonus)) if FocusType.FOCUS_RESEARCH in species.foci: research_bonus += discount_multiplier * 2 * AIDependencies.RESEARCH_PER_POP * max_pop_size if "ANCIENT_RUINS_SPECIAL" in planet.specials or "ANCIENT_RUINS_DEPLETED_SPECIAL" in planet.specials: research_bonus += discount_multiplier * 2 * AIDependencies.RESEARCH_PER_POP * max_pop_size * 5 detail.append("Ruins Research") if "TEMPORAL_ANOMALY_SPECIAL" in planet.specials: research_bonus += discount_multiplier * 2 * AIDependencies.RESEARCH_PER_POP * max_pop_size * 25 detail.append("Temporal Anomaly Research") if AIDependencies.COMPUTRONIUM_SPECIAL in planet.specials: comp_bonus = (0.5 * AIDependencies.TECH_COST_MULTIPLIER * AIDependencies.RESEARCH_PER_POP * AIDependencies.COMPUTRONIUM_RES_MULTIPLIER * population_with_research_focus() * discount_multiplier) if have_computronium(): comp_bonus *= backup_factor research_bonus += comp_bonus detail.append(AIDependencies.COMPUTRONIUM_SPECIAL) infl_val = rate_influence(planet, species, max_pop_size) detail.append("infl_val %.1f" % infl_val) retval += (max(ind_val + asteroid_bonus + gas_giant_bonus, research_bonus, growth_val, infl_val) + fixed_ind + fixed_res + supply_val) detail.append( f" max({ind_val:.1f}+{asteroid_bonus:.1f}+{gas_giant_bonus:.1f}," f" {research_bonus:.1f}, {growth_val:.1f}, {infl_val:.1f})" f" + {fixed_ind:.1f} + {fixed_res:.1f} + {supply_val:.1f}") if existing_presence: retval = (retval + existing_presence * _get_defense_value(species_name)) * 2 detail.append("preexisting system colony => %.1f" % retval) if threat_factor < 1.0: threat_factor = _revise_threat_factor(threat_factor, retval, this_sysid, MINIMUM_COLONY_SCORE) retval *= threat_factor detail.append("threat reducing value by %3d %%" % (100 * (1 - threat_factor))) return retval
def assign_scouts_to_explore_systems(): # TODO: use Graph Theory to explore closest systems universe = fo.getUniverse() capital_sys_id = PlanetUtilsAI.get_capital_sys_id() # order fleets to explore if not border_unexplored_system_ids or (capital_sys_id == INVALID_ID): return exp_systems_by_dist = sorted( (universe.linearDistance(capital_sys_id, x), x) for x in border_unexplored_system_ids) print "Exploration system considering following system-distance pairs:\n %s" % ( "\n ".join("%3d: %5.1f" % (sys_id, dist) for (dist, sys_id) in exp_systems_by_dist)) explore_list = [sys_id for dist, sys_id in exp_systems_by_dist] already_covered, available_scouts = get_current_exploration_info() print "Explorable system IDs: %s" % explore_list print "Already targeted: %s" % already_covered needs_vis = foAI.foAIstate.misc.setdefault('needs_vis', []) check_list = foAI.foAIstate.needsEmergencyExploration + needs_vis + explore_list if INVALID_ID in check_list: # shouldn't normally happen, unless due to bug elsewhere for sys_list, name in [(foAI.foAIstate.needsEmergencyExploration, "foAI.foAIstate.needsEmergencyExploration"), (needs_vis, "needs_vis"), (explore_list, "explore_list")]: if INVALID_ID in sys_list: error("INVALID_ID found in " + name, exc_info=True) # emergency coverage can be due to invasion detection trouble, etc. needs_coverage = [ sys_id for sys_id in check_list if sys_id not in already_covered and sys_id != INVALID_ID ] print "Needs coverage: %s" % needs_coverage print "Available scouts & AIstate locs: %s" % [ (x, foAI.foAIstate.fleetStatus.get(x, {}).get('sysID', INVALID_ID)) for x in available_scouts ] print "Available scouts & universe locs: %s" % [ (x, universe.getFleet(x).systemID) for x in available_scouts ] if not needs_coverage or not available_scouts: return available_scouts = set(available_scouts) sent_list = [] while available_scouts and needs_coverage: this_sys_id = needs_coverage.pop(0) sys_status = foAI.foAIstate.systemStatus.setdefault(this_sys_id, {}) if this_sys_id not in explore_list: # doesn't necessarily need direct visit if universe.getVisibility(this_sys_id, fo.empireID()) >= fo.visibility.partial: # already got visibility; remove from visit lists and skip if this_sys_id in needs_vis: del needs_vis[needs_vis.index(this_sys_id)] if this_sys_id in foAI.foAIstate.needsEmergencyExploration: del foAI.foAIstate.needsEmergencyExploration[ foAI.foAIstate.needsEmergencyExploration.index( this_sys_id)] print "system id %d already currently visible; skipping exploration" % this_sys_id continue # TODO: if blocked byu monster, try to find nearby system from which to see this system if (not foAI.foAIstate.character.may_explore_system( sys_status.setdefault('monsterThreat', 0)) or (fo.currentTurn() < 20 and foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat'] > 200)): print "Skipping exploration of system %d due to Big Monster, threat %d" % ( this_sys_id, foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat']) continue this_fleet_list = FleetUtilsAI.get_fleets_for_mission( target_stats={}, min_stats={}, cur_stats={}, starting_system=this_sys_id, fleet_pool_set=available_scouts, fleet_list=[]) if not this_fleet_list: print "Seem to have run out of scouts while trying to cover sys_id %d" % this_sys_id break # must have ran out of scouts fleet_id = this_fleet_list[0] fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id) target = universe_object.System(this_sys_id) if MoveUtilsAI.can_travel_to_system_and_return_to_resupply( fleet_id, fleet_mission.get_location_target(), target): fleet_mission.set_target(MissionType.EXPLORATION, target) sent_list.append(this_sys_id) else: # system too far out, skip it, but can add scout back to available pool print "sys_id %d too far out for fleet ( ID %d ) to reach" % ( this_sys_id, fleet_id) available_scouts.update(this_fleet_list) print "Sent scouting fleets to sysIDs : %s" % sent_list return # pylint: disable=pointless-string-statement """
def set_planet_resource_foci(): """set resource focus of planets """ newFoci = {} print "\n============================" print "Collecting info to assess Planet Focus Changes\n" universe = fo.getUniverse() empire = fo.getEmpire() currentTurn = fo.currentTurn() # set the random seed (based on galaxy seed, empire ID and current turn) # for game-reload consistency freq = min(3, (max(5, currentTurn - 80)) / 4.0)**(1.0 / 3) if not (limitAssessments and (abs(currentTurn - lastFociCheck[0]) < 1.5 * freq) and (random.random() < 1.0 / freq)): lastFociCheck[0] = currentTurn resource_timer.start("getPlanets") empirePlanetIDs = list( PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)) resource_timer.start("Filter") resource_timer.start("Priority") # TODO: take into acct splintering of resource groups # fleetSupplyableSystemIDs = empire.fleetSupplyableSystemIDs # fleetSupplyablePlanetIDs = PlanetUtilsAI.get_planets_in__systems_ids(fleetSupplyableSystemIDs) ppPrio = foAI.foAIstate.get_priority( AIPriorityType.PRIORITY_RESOURCE_PRODUCTION) rpPrio = foAI.foAIstate.get_priority( AIPriorityType.PRIORITY_RESOURCE_RESEARCH) priorityRatio = float(rpPrio) / (ppPrio + 0.0001) resource_timer.start("Shuffle") # not supporting Growth for general planets until also adding code to make sure would actually benefit # shuffle(generalPlanetIDs) resource_timer.start("Targets") planets = map(universe.getPlanet, empirePlanetIDs) planetMap.clear() planetMap.update(zip(empirePlanetIDs, planets)) if useGrowth: for metab, metabIncPop in ColonisationAI.empire_metabolisms.items( ): for special in [ aspec for aspec in AIDependencies.metabolismBoostMap.get( metab, []) if aspec in ColonisationAI.available_growth_specials ]: rankedPlanets = [] for pid in ColonisationAI.available_growth_specials[ special]: planet = planetMap[pid] cur_focus = planet.focus pop = planet.currentMeterValue(fo.meterType.population) if (pop > metabIncPop - 2 * planet.size) or ( GFocus not in planet.availableFoci ): # not enough benefit to lose local production, or can't put growth focus here continue for special2 in ["COMPUTRONIUM_SPECIAL"]: if special2 in planet.specials: break else: # didn't have any specials that would override interest in growth special print "Considering Growth Focus for %s (%d) with special %s; planet has pop %.1f and %s metabolism incremental pop is %.1f" % ( planet.name, pid, special, pop, metab, metabIncPop) if cur_focus == GFocus: pop -= 4 # discourage changing current focus to minimize focus-changing penalties rankedPlanets.append((pop, pid, cur_focus)) if not rankedPlanets: continue rankedPlanets.sort() print "Considering Growth Focus choice for special %s; possible planet pop, id pairs are %s" % ( metab, rankedPlanets) for spSize, spPID, cur_focus in rankedPlanets: # index 0 should be able to set focus, but just in case... result = 1 if cur_focus != GFocus: result = fo.issueChangeFocusOrder(spPID, GFocus) if result == 1: newFoci[spPID] = GFocus if spPID in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index( spPID)] print "%s focus of planet %s (%d) at Growth Focus" % ( ["set", "left"][cur_focus == GFocus], planetMap[spPID].name, spPID) break else: print "failed setting focus of planet %s (%d) at Growth Focus; focus left at %s" % ( planetMap[spPID].name, spPID, planetMap[spPID].focus) already_have_comp_moon = False for pid in empirePlanetIDs: planet = planetMap[pid] if "COMPUTRONIUM_SPECIAL" in planet.specials and RFocus in planet.availableFoci and not already_have_comp_moon: curFocus = planet.focus newFoci[pid] = RFocus result = 0 if curFocus != RFocus: result = fo.issueChangeFocusOrder(pid, RFocus) if result == 1: universe.updateMeterEstimates(empirePlanetIDs) if curFocus == RFocus or result == 1: already_have_comp_moon = True print "%s focus of planet %s (%d) (with Computronium Moon) at Research Focus" % ( ["set", "left" ][curFocus == RFocus], planetMap[pid].name, pid) if pid in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index(pid)] continue if ((([ bld.buildingTypeName for bld in map(universe.getObject, planet.buildingIDs) if bld.buildingTypeName in ["BLD_CONC_CAMP", "BLD_CONC_CAMP_REMNANT"] ] != []) or ([ ccspec for ccspec in planet.specials if ccspec in ["CONC_CAMP_MASTER_SPECIAL", "CONC_CAMP_SLAVE_SPECIAL"] ] != [])) and IFocus in planet.availableFoci): curFocus = planet.focus newFoci[pid] = IFocus result = 0 if curFocus != IFocus: result = fo.issueChangeFocusOrder(pid, IFocus) if result == 1: print( "Tried setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, got result %d and focus %s" % (newFoci[pid], planet.name, pid, planet.speciesName, curFocus, result, planetMap[pid].focus)) universe.updateMeterEstimates(empirePlanetIDs) if (result != 1) or planetMap[pid].focus != IFocus: newplanet = universe.getPlanet(pid) print( "Error: Failed setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, but new planet copy shows %s" % (newFoci[pid], planetMap[pid].name, pid, planetMap[pid].speciesName, planetMap[pid].focus, newplanet.focus)) if curFocus == IFocus or result == 1: print "%s focus of planet %s (%d) (with Concentration Camps/Remnants) at Industry Focus" % ( ["set", "left" ][curFocus == IFocus], planetMap[pid].name, pid) if pid in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index(pid)] continue # TODO: Was getting a key error mid-game without the below check for "pid in currentOutput" -- needs further investigation if pid in currentOutput and PFocus in planet.availableFoci and assess_protection_focus( pid): curFocus = planet.focus newFoci[pid] = PFocus result = 0 if curFocus != PFocus: result = fo.issueChangeFocusOrder(pid, PFocus) if result == 1: print( "Tried setting %s for planet %s (%d) with species %s and current focus %s, got result %d and focus %s" % (newFoci[pid], planet.name, pid, planet.speciesName, curFocus, result, planetMap[pid].focus)) universe.updateMeterEstimates(empirePlanetIDs) if (result != 1) or planetMap[pid].focus != PFocus: newplanet = universe.getPlanet(pid) print( "Error: Failed setting %s for planet %s (%d) with species %s and current focus %s, but new planet copy shows %s" % (newFoci[pid], planetMap[pid].name, pid, planetMap[pid].speciesName, planetMap[pid].focus, newplanet.focus)) if curFocus == PFocus or result == 1: print "%s focus of planet %s (%d) at Protection(Defense) Focus" % ( ["set", "left" ][curFocus == PFocus], planetMap[pid].name, pid) if pid in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index(pid)] continue # pp, rp = get_resource_target_totals(empirePlanetIDs, planetMap) pp, rp = get_resource_target_totals(planetMap.keys()) print "\n-----------------------------------------" print "Making Planet Focus Change Determinations\n" ratios = [] # for each planet, calculate RP:PP value ratio at which industry/Mining focus and research focus would have the same total value, & sort by that # include a bias to slightly discourage changing foci curTargetPP = 0.001 curTargetRP = 0.001 resource_timer.start("Loop") # loop has_force = tech_is_complete("CON_FRC_ENRG_STRC") preset_ids = set(planetMap.keys()) - set(empirePlanetIDs) ctPP0, ctRP0 = 0, 0 for pid in preset_ids: nPP, nRP = newTargets.get(pid, {}).get(planetMap[pid].focus, [0, 0]) curTargetPP += nPP curTargetRP += nRP iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0]) ctPP0 += iPP ctRP0 += iRP id_set = set(empirePlanetIDs) for adj_round in [1, 2, 3, 4]: maxi_ratio = ctRP0 / max( 0.01, ctPP0) # should only change between rounds 1 and 2 for pid in list(id_set): if adj_round == 1: # tally max Industry iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0]) ctPP0 += iPP ctRP0 += iRP continue II, IR = newTargets[pid][IFocus] RI, RR = newTargets[pid][RFocus] CI, CR = currentOutput[pid][IFocus], currentOutput[pid][RFocus] research_penalty = (currentFocus[pid] != RFocus) # calculate factor F at which II + F * IR == RI + F * RR =====> F = ( II-RI ) / (RR-IR) thisFactor = (II - RI) / max( 0.01, RR - IR ) # don't let denominator be zero for planets where focus doesn't change RP planet = planetMap[pid] if adj_round == 2: # take research at planets with very cheap research if (maxi_ratio < priorityRatio) and ( curTargetRP < priorityRatio * ctPP0) and (thisFactor <= 1.0): curTargetPP += RI curTargetRP += RR newFoci[pid] = RFocus id_set.discard(pid) continue if adj_round == 3: # take research at planets where can do reasonable balance if has_force or (foAI.foAIstate.aggression < fo.aggression.aggressive) or ( curTargetRP >= priorityRatio * ctPP0): continue pop = planet.currentMeterValue(fo.meterType.population) t_pop = planet.currentMeterValue( fo.meterType.targetPopulation) # if AI is aggressive+, and this planet in range where temporary Research focus can get an additional RP at cost of 1 PP, and still need some RP, then do it if pop < t_pop - 5: continue if (CI > II + 8) or (((RR > II) or ( (RR - CR) >= 1 + 2 * research_penalty)) and ((RR - IR) >= 3) and ((CR - IR) >= 0.7 * ((II - CI) * (1 + 0.1 * research_penalty)))): curTargetPP += CI - 1 - research_penalty curTargetRP += CR + 1 newFoci[pid] = RFocus id_set.discard(pid) continue # adj_round == 4 assume default IFocus curTargetPP += II # icurTargets initially calculated by Industry focus, which will be our default focus curTargetRP += IR newFoci[pid] = IFocus ratios.append((thisFactor, pid)) ratios.sort() printedHeader = False fociMap = { IFocus: "Industry", RFocus: "Research", MFocus: "Mining", GFocus: "Growth", PFocus: "Defense" } gotAlgo = tech_is_complete("LRN_ALGO_ELEGANCE") for ratio, pid in ratios: do_research = False # (newFoci[pid]==RFocus) if (priorityRatio < (curTargetRP / (curTargetPP + 0.0001)) ) and not do_research: # we have enough RP if ratio < 1.1 and foAI.foAIstate.aggression > fo.aggression.cautious: # but wait, RP is still super cheap relative to PP, maybe will take more RP if priorityRatio < 1.5 * ( curTargetRP / (curTargetPP + 0.0001) ): # yeah, really a glut of RP, stop taking RP break else: # RP not super cheap & we have enough, stop taking it break II, IR = newTargets[pid][IFocus] RI, RR = newTargets[pid][RFocus] # if currentFocus[pid] == MFocus: # II = max( II, newTargets[pid][MFocus][0] ) if do_research or ( gotAlgo and ((ratio > 2.0 and curTargetPP < 15) or (ratio > 2.5 and curTargetPP < 25 and II > 5) or (ratio > 3.0 and curTargetPP < 40 and II > 5) or (ratio > 4.0 and curTargetPP < 100 and II > 10) or ((curTargetRP + RR - IR) / max(0.001, curTargetPP - II + RI) > 2 * priorityRatio)) ): # we already have algo elegance and more RP would be too expensive, or overkill if not printedHeader: printedHeader = True print "Rejecting further Research Focus choices as too expensive:" print "%34s|%20s|%15s |%15s|%15s |%15s |%15s" % ( " Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " rejectedFocus ", " rejected target RP/PP ", "rejected RP-PP EQF") oldFocus = currentFocus[pid] cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][ RFocus] otPP, otRP = newTargets[pid].get(oldFocus, (0, 0)) ntPP, ntRP = newTargets[pid].get(RFocus, (0, 0)) print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f | cF: %8s | nF: %8s | cT: %5.1f / %5.1f | %.2f" % ( pid, planetMap[pid].name, cRP, cPP, otRP, otPP, fociMap.get(oldFocus, 'unknown'), fociMap[RFocus], ntRP, ntPP, ratio) continue # RP is getting too expensive, but might be willing to still allocate from a planet with less PP to lose # if planetMap[pid].currentMeterValue(fo.meterType.targetPopulation) >0: #only set to research if pop won't die out newFoci[pid] = RFocus curTargetRP += (RR - IR) curTargetPP -= (II - RI) print "============================" print "Planet Focus Assignments to achieve target RP/PP ratio of %.2f from current ratio of %.2f ( %.1f / %.1f )" % ( priorityRatio, rp / (pp + 0.0001), rp, pp) print "Max Industry assignments would result in target RP/PP ratio of %.2f ( %.1f / %.1f )" % ( ctRP0 / (ctPP0 + 0.0001), ctRP0, ctPP0) print "-------------------------------------" print "%34s|%20s|%15s |%15s|%15s |%15s " % ( " Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " newFocus ", " new target RP/PP ") totalChanged = 0 for id_set in empirePlanetIDs, preset_ids: for pid in id_set: canFocus = planetMap[pid].currentMeterValue( fo.meterType.targetPopulation) > 0 oldFocus = currentFocus[pid] newFocus = newFoci.get(pid, IFocus) cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][ RFocus] otPP, otRP = newTargets[pid].get(oldFocus, (0, 0)) ntPP, ntRP = otPP, otRP if newFocus != oldFocus and newFocus in planetMap[ pid].availableFoci: # planetMap[pid].focus totalChanged += 1 if newFocus != planetMap[pid].focus: result = fo.issueChangeFocusOrder(pid, newFocus) if result == 1: ntPP, ntRP = newTargets[pid].get(newFocus, (0, 0)) else: print "Trouble changing focus of planet %s (%d) to %s" % ( planetMap[pid].name, pid, newFocus) print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f | cF: %8s | nF: %8s | cT: %5.1f / %5.1f " % ( pid, planetMap[pid].name, cRP, cPP, otRP, otPP, fociMap.get(oldFocus, 'unknown'), fociMap[newFocus], ntRP, ntPP) print "-------------------------------------" print "-------------------------------------" print "Final Ratio Target (turn %4d) RP/PP : %.2f ( %.1f / %.1f ) after %d Focus changes" % ( fo.currentTurn(), curTargetRP / (curTargetPP + 0.0001), curTargetRP, curTargetPP, totalChanged) resource_timer.end() aPP, aRP = empire.productionPoints, empire.resourceProduction( fo.resourceType.research) # Next string used in charts. Don't modify it! print "Current Output (turn %4d) RP/PP : %.2f ( %.1f / %.1f )" % ( fo.currentTurn(), aRP / (aPP + 0.0001), aRP, aPP) print "------------------------" print "ResourcesAI Time Requirements:"
def assign_scouts_to_explore_systems(): # TODO: use Graph Theory to explore closest systems universe = fo.getUniverse() capital_sys_id = PlanetUtilsAI.get_capital_sys_id() # order fleets to explore if not border_unexplored_system_ids or (capital_sys_id == INVALID_ID): return exp_systems_by_dist = sorted( (universe.linearDistance(capital_sys_id, x), x) for x in border_unexplored_system_ids) debug( "Exploration system considering following system-distance pairs:\n %s" % ("\n ".join("%3d: %5.1f" % (sys_id, dist) for (dist, sys_id) in exp_systems_by_dist))) explore_list = [sys_id for dist, sys_id in exp_systems_by_dist] already_covered, available_scouts = get_current_exploration_info() debug("Explorable system IDs: %s" % explore_list) debug("Already targeted: %s" % already_covered) aistate = get_aistate() needs_vis = aistate.misc.setdefault("needs_vis", []) check_list = aistate.needsEmergencyExploration + needs_vis + explore_list if INVALID_ID in check_list: # shouldn't normally happen, unless due to bug elsewhere for sys_list, name in [ (aistate.needsEmergencyExploration, "aistate.needsEmergencyExploration"), (needs_vis, "needs_vis"), (explore_list, "explore_list"), ]: if INVALID_ID in sys_list: error("INVALID_ID found in " + name, exc_info=True) # emergency coverage can be due to invasion detection trouble, etc. debug("Check list: %s" % check_list) needs_coverage = [ sys_id for sys_id in check_list if sys_id not in already_covered and sys_id != INVALID_ID ] debug("Needs coverage: %s" % needs_coverage) debug("Available scouts & AIstate locs: %s" % [(x, aistate.fleetStatus.get(x, {}).get("sysID", INVALID_ID)) for x in available_scouts]) debug("Available scouts & universe locs: %s" % [(x, universe.getFleet(x).systemID) for x in available_scouts]) if not needs_coverage or not available_scouts: return # clean up targets which can not or don't need to be scouted for sys_id in list(needs_coverage): if sys_id not in explore_list: # doesn't necessarily need direct visit if universe.getVisibility(sys_id, fo.empireID()) >= fo.visibility.partial: # already got visibility; remove from visit lists and skip if sys_id in needs_vis: del needs_vis[needs_vis.index(sys_id)] if sys_id in aistate.needsEmergencyExploration: del aistate.needsEmergencyExploration[ aistate.needsEmergencyExploration.index(sys_id)] debug( "system id %d already currently visible; skipping exploration" % sys_id) needs_coverage.remove(sys_id) continue # skip systems threatened by monsters sys_status = aistate.systemStatus.setdefault(sys_id, {}) if not aistate.character.may_explore_system( sys_status.setdefault("monsterThreat", 0)) or ( fo.currentTurn() < 20 and aistate.systemStatus[sys_id]["monsterThreat"] > 0): debug( "Skipping exploration of system %d due to Big Monster, threat %d" % (sys_id, aistate.systemStatus[sys_id]["monsterThreat"])) needs_coverage.remove(sys_id) continue # find the jump distance for all possible scout-system pairings options = [] available_scouts = set(available_scouts) for fleet_id in available_scouts: start = TargetSystem(get_fleet_position(fleet_id)) for sys_id in needs_coverage: target = TargetSystem(sys_id) path = MoveUtilsAI.can_travel_to_system(fleet_id, start, target, ensure_return=True) if not path: continue num_jumps = len( path) - 1 # -1 as path contains the original system options.append((num_jumps, fleet_id, sys_id)) # Apply a simple, greedy heuristic to match scouts to nearby systems: # Always choose the shortest possible path from the remaining scout-system pairing. # This is clearly not optimal in the general case but it works well enough for now. # TODO: Consider using a more sophisticated assignment algorithm options.sort() while options: debug("Remaining options: %s" % options) _, fleet_id, sys_id = options[0] fleet_mission = aistate.get_fleet_mission(fleet_id) target = TargetSystem(sys_id) info("Sending fleet %d to explore %s" % (fleet_id, target)) fleet_mission.set_target(MissionType.EXPLORATION, target) options = [ option for option in options if option[1] != fleet_id and option[2] != sys_id ] available_scouts.remove(fleet_id) needs_coverage.remove(sys_id) debug("Exploration assignment finished.") debug("Unassigned scouts: %s" % available_scouts) debug("Unassigned exploration targets: %s" % needs_coverage)
def generateOrders(): # pylint: disable=invalid-name """Called once per turn to tell the Python AI to generate and issue orders to control its empire. at end of this function, fo.doneTurn() should be called to indicate to the client that orders are finished and can be sent to the server for processing.""" empire = fo.getEmpire() if empire is None: print "This client has no empire. Doing nothing to generate orders." try: # early abort if no empire. no need to do meter calculations # on last-seen gamestate if nothing can be ordered anyway... # # note that doneTurn() is issued on behalf of the client network # id, not the empire id, so not having a correct empire id does # not invalidate doneTurn() fo.doneTurn() except Exception as e: print_error(e) return if empire.eliminated: print "This empire has been eliminated. Aborting order generation" try: # early abort if already eliminated. no need to do meter calculations # on last-seen gamestate if nothing can be ordered anyway... fo.doneTurn() except Exception as e: print_error(e) return # This code block is required for correct AI work. print "Meter / Resource Pool updating..." fo.initMeterEstimatesDiscrepancies() fo.updateMeterEstimates(False) fo.updateResourcePools() turn = fo.currentTurn() turn_uid = foAIstate.set_turn_uid() print "\n\n\n", "=" * 20, print "Starting turn %s (%s) of game: %s" % (turn, turn_uid, foAIstate.uid), print "=" * 20, "\n" turn_timer.start("AI planning") # set the random seed (based on galaxy seed, empire name and current turn) # for game-reload consistency. random_seed = str( fo.getGalaxySetupData().seed) + "%05d%s" % (turn, fo.getEmpire().name) random.seed(random_seed) universe = fo.getUniverse() empire = fo.getEmpire() planet_id = PlanetUtilsAI.get_capital() planet = None if planet_id is not None: planet = universe.getPlanet(planet_id) aggression_name = get_trait_name_aggression(foAIstate.character) print "***************************************************************************" print "******* Log info for AI progress chart script. Do not modify. **********" print("Generating Orders") print( "EmpireID: {empire.empireID}" " Name: {empire.name}_{empire.empireID}_pid:{p_id}_{p_name}RIdx_{res_idx}_{aggression}" " Turn: {turn}").format(empire=empire, p_id=fo.playerID(), p_name=fo.playerName(), res_idx=ResearchAI.get_research_index(), turn=turn, aggression=aggression_name.capitalize()) print "EmpireColors: {0.colour.r} {0.colour.g} {0.colour.b} {0.colour.a}".format( empire) if planet: print "CapitalID: " + str( planet_id ) + " Name: " + planet.name + " Species: " + planet.speciesName else: print "CapitalID: None Currently Name: None Species: None " print "***************************************************************************" print "***************************************************************************" if turn == 1: declare_war_on_all() human_player = fo.empirePlayerID(1) greet = diplomatic_corp.get_first_turn_greet_message() fo.sendChatMessage( human_player, '%s (%s): [[%s]]' % (empire.name, get_trait_name_aggression( foAIstate.character), greet)) # turn cleanup !!! this was formerly done at start of every turn -- not sure why foAIstate.split_new_fleets() foAIstate.refresh( ) # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats foAIstate.report_system_threats() print("Calling AI Modules") # call AI modules action_list = [ ColonisationAI.survey_universe, ProductionAI.find_best_designs_this_turn, PriorityAI.calculate_priorities, ExplorationAI.assign_scouts_to_explore_systems, ColonisationAI.assign_colony_fleets_to_colonise, InvasionAI.assign_invasion_fleets_to_invade, MilitaryAI.assign_military_fleets_to_systems, FleetUtilsAI.generate_fleet_orders_for_fleet_missions, FleetUtilsAI.issue_fleet_orders_for_fleet_missions, ResearchAI.generate_research_orders, ProductionAI.generate_production_orders, ResourcesAI.generate_resources_orders, ] for action in action_list: try: main_timer.start(action.__name__) action() main_timer.stop() except Exception as e: print_error(e, location=action.__name__) main_timer.stop_print_and_clear() turn_timer.stop_print_and_clear() turn_timer.start("Server_Processing") try: fo.doneTurn() except Exception as e: print_error(e) # TODO move it to cycle above if using_statprof: try: statprof.stop() statprof.display() statprof.start() except: pass
def send_colony_ships(colony_fleet_ids, evaluated_planets, mission_type): """sends a list of colony ships to a list of planet_value_pairs""" fleet_pool = colony_fleet_ids[:] try_all = False if mission_type == MissionType.OUTPOST: cost = 20 + outpod_pod_cost() else: try_all = True cost = 20 + colony_pod_cost_turns()[1] if fo.currentTurn() < 50: cost *= 0.4 # will be making fast tech progress so value is underestimated elif fo.currentTurn() < 80: cost *= 0.8 # will be making fast-ish tech progress so value is underestimated potential_targets = [ (pid, (score, specName)) for (pid, (score, specName)) in evaluated_planets if score > (0.8 * cost) and score > MINIMUM_COLONY_SCORE ] debug("Colony/outpost ship matching: fleets %s to planets %s" % (fleet_pool, evaluated_planets)) if try_all: debug("Trying best matches to current colony ships") best_scores = dict(evaluated_planets) potential_targets = [] for pid, ratings in _all_colony_opportunities.items(): for rating in ratings: if rating[0] >= 0.75 * best_scores.get(pid, [9999])[0]: potential_targets.append((pid, rating)) potential_targets.sort(key=itemgetter(1), reverse=True) # added a lot of checking because have been getting mysterious exception, after too many recursions to get info fleet_pool = set(fleet_pool) universe = fo.getUniverse() for fid in fleet_pool: fleet = universe.getFleet(fid) if not fleet or fleet.empty: warning("Bad fleet ( ID %d ) given to colonization routine; will be skipped" % fid) fleet_pool.remove(fid) continue report_str = "Fleet ID (%d): %d ships; species: " % (fid, fleet.numShips) for sid in fleet.shipIDs: ship = universe.getShip(sid) if not ship: report_str += "NoShip, " else: report_str += "%s, " % ship.speciesName debug(report_str) debug("") already_targeted = [] # for planetID_value_pair in evaluatedPlanets: aistate = get_aistate() while fleet_pool and potential_targets: target = potential_targets.pop(0) if target in already_targeted: continue planet_id = target[0] if planet_id in already_targeted: continue planet = universe.getPlanet(planet_id) sys_id = planet.systemID if ( aistate.systemStatus.setdefault(sys_id, {}).setdefault("monsterThreat", 0) > 2000 or fo.currentTurn() < 20 and aistate.systemStatus[sys_id]["monsterThreat"] > 200 ): debug( "Skipping colonization of system %s due to Big Monster, threat %d" % (universe.getSystem(sys_id), aistate.systemStatus[sys_id]["monsterThreat"]) ) already_targeted.append(planet_id) continue # make sure not to run into stationary guards if ExplorationAI.system_could_have_unknown_stationary_guard(sys_id): ExplorationAI.request_emergency_exploration(sys_id) continue this_spec = target[1][1] found_fleets = [] try: this_fleet_list = FleetUtilsAI.get_fleets_for_mission( target_stats={}, min_stats={}, cur_stats={}, starting_system=sys_id, species=this_spec, fleet_pool_set=fleet_pool, fleet_list=found_fleets, ) except Exception as e: error(e, exc_info=True) continue if not this_fleet_list: fleet_pool.update(found_fleets) # just to be safe continue # must have no compatible colony/outpost ships fleet_id = this_fleet_list[0] already_targeted.append(planet_id) ai_target = TargetPlanet(planet_id) aistate.get_fleet_mission(fleet_id).set_target(mission_type, ai_target)
def _calculate_industry_priority(): # currently only used to print status """calculates the demand for industry""" universe = fo.getUniverse() empire = fo.getEmpire() # get current industry production & Target industry_production = empire.resourceProduction(fo.resourceType.industry) owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) planets = map(universe.getPlanet, owned_planet_ids) target_pp = sum(map(lambda x: x.currentMeterValue(fo.meterType.targetIndustry), planets)) # currently, previously set to 50 in calculatePriorities(), this is just for reporting industry_priority = foAI.foAIstate.get_priority(PriorityType.RESOURCE_PRODUCTION) print print "Industry Production (current/target) : ( %.1f / %.1f ) at turn %s" % (industry_production, target_pp, fo.currentTurn()) print "Priority for Industry: %s" % industry_priority return industry_priority
def _calculate_military_priority(): """Calculates the demand for military ships by military targeted systems.""" global unmetThreat universe = fo.getUniverse() capital_id = PlanetUtilsAI.get_capital() if capital_id is None or capital_id == -1: return 0 # no capitol (not even a capitol-in-the-making), means can't produce any ships have_l1_weaps = (tech_is_complete("SHP_WEAPON_1_4") or (tech_is_complete("SHP_WEAPON_1_3") and tech_is_complete("SHP_MIL_ROBO_CONT")) or tech_is_complete("SHP_WEAPON_2_1") or tech_is_complete("SHP_WEAPON_4_1")) have_l2_weaps = (tech_is_complete("SHP_WEAPON_2_3") or tech_is_complete("SHP_WEAPON_4_1")) enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) allotted_invasion_targets = 1 + int(fo.currentTurn()/25) target_planet_ids = [pid for pid, pscore, trp in AIstate.invasionTargets[:allotted_invasion_targets]] + [pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs.items()[:allottedColonyTargets]] + [pid for pid, pscore in foAI.foAIstate.colonisableOutpostIDs.items()[:allottedColonyTargets]] my_systems = set(AIstate.popCtrSystemIDs).union(AIstate.outpostSystemIDs) target_systems = set(PlanetUtilsAI.get_systems(target_planet_ids)) cur_ship_rating = ProductionAI.cur_best_military_design_rating() current_turn = fo.currentTurn() ships_needed = 0 defense_ships_needed = 0 ships_needed_allocation = [] for sys_id in my_systems.union(target_systems): status = foAI.foAIstate.systemStatus.get(sys_id, {}) my_rating = status.get('myFleetRating', 0) my_defenses = status.get('mydefenses', {}).get('overall', 0) base_monster_threat = status.get('monsterThreat', 0) # scale monster threat so that in early - mid game big monsters don't over-drive military production monster_threat = base_monster_threat if current_turn > 200: pass elif current_turn > 100: if base_monster_threat >= 2000: monster_threat = 2000 + (current_turn / 100.0 - 1) * (base_monster_threat - 2000) elif current_turn > 30: if base_monster_threat >= 2000: monster_threat = 0 else: if base_monster_threat > 200: monster_threat = 0 if sys_id in my_systems: threat_root = status.get('fleetThreat', 0)**0.5 + 0.8*status.get('max_neighbor_threat', 0)**0.5 + 0.2*status.get('neighborThreat', 0)**0.5 + monster_threat**0.5 + status.get('planetThreat', 0)**0.5 else: threat_root = status.get('fleetThreat', 0)**0.5 + monster_threat**0.5 + status.get('planetThreat', 0)**0.5 ships_needed_here = math.ceil((max(0, (threat_root - (my_rating ** 0.5 + my_defenses ** 0.5))) ** 2) / cur_ship_rating) ships_needed += ships_needed_here ships_needed_allocation.append((universe.getSystem(sys_id), ships_needed_here)) if sys_id in my_systems: defense_ships_needed += ships_needed_here part1 = min(1 * fo.currentTurn(), 40) part2 = max(0, int(75 * ships_needed)) military_priority = part1 + part2 if not have_l1_weaps: military_priority /= 2.0 elif not (have_l2_weaps and enemies_sighted): military_priority /= 1.5 fmt_string = "Calculating Military Priority: min(t,40) + max(0,75 * ships_needed) \n\t Priority: %d \t ships_needed: %d \t defense_ships_needed: %d \t curShipRating: %.0f \t l1_weaps: %s \t enemies_sighted: %s" print fmt_string % (military_priority, ships_needed, defense_ships_needed, cur_ship_rating, have_l1_weaps, enemies_sighted) print "Source of milship demand: ", ships_needed_allocation if foAI.foAIstate.aggression < fo.aggression.typical: military_priority *= (1.0 + foAI.foAIstate.aggression) / (1.0 + fo.aggression.typical) return max(military_priority, 0)
def _calculate_invasion_priority(): """Calculates the demand for troop ships by opponent planets.""" global allottedInvasionTargets if foAI.foAIstate.aggression <= fo.aggression.turtle: return 0 empire = fo.getEmpire() enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) multiplier = 1 num_colonies = len(list(AIstate.popCtrIDs)) if num_colonies > colony_growth_barrier: return 0.0 if len(foAI.foAIstate.colonisablePlanetIDs) > 0: best_colony_score = max(2, foAI.foAIstate.colonisablePlanetIDs.items()[0][1][0]) else: best_colony_score = 2 if foAI.foAIstate.aggression == fo.aggression.beginner and fo.currentTurn() < 150: return 0 allottedInvasionTargets = 1 + int(fo.currentTurn() / 25) total_val = 0 troops_needed = 0 for pid, pscore, trp in AIstate.invasionTargets[:allottedInvasionTargets]: if pscore > best_colony_score: multiplier += 1 total_val += 2 * pscore else: total_val += pscore troops_needed += trp + 4 # ToDo: This seems like it could be improved by some dynamic calculation of buffer if total_val == 0: return 0 production_queue = empire.productionQueue queued_troop_capacity = 0 for queue_index in range(0, len(production_queue)): element = production_queue[queue_index] if element.buildType == EmpireProductionTypes.BT_SHIP: if foAI.foAIstate.get_ship_role(element.designID) in [ShipRoleType.MILITARY_INVASION, ShipRoleType.BASE_INVASION]: design = fo.getShipDesign(element.designID) queued_troop_capacity += element.remaining * element.blocksize * design.troopCapacity _, best_design, _ = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_INVASION) if best_design: troops_per_best_ship = best_design.troopCapacity else: return 1e-6 # if we can not build troop ships, we don't want to build (non-existing) invasion ships # don't count troop bases here as these cannot be redeployed after misplaning # troopFleetIDs = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.INVASION)\ # + FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.ORBITAL_INVASION) troop_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.INVASION) total_troop_capacity = sum([FleetUtilsAI.count_troops_in_fleet(fid) for fid in troop_fleet_ids]) troop_ships_needed = \ math.ceil((troops_needed - (total_troop_capacity + queued_troop_capacity)) / troops_per_best_ship) # invasion_priority = max( 10+ 200*max(0, troop_ships_needed ) , int(0.1* total_val) ) invasion_priority = multiplier * (30 + 150*max(0, troop_ships_needed)) if not ColonisationAI.colony_status.get('colonies_under_attack', []): if not ColonisationAI.colony_status.get('colonies_under_threat', []): invasion_priority *= 2.0 else: invasion_priority *= 1.5 if not enemies_sighted: invasion_priority *= 1.5 if invasion_priority < 0: return 0 if foAI.foAIstate.aggression == fo.aggression.beginner: return 0.5 * invasion_priority else: return invasion_priority
def generateOrders(): # pylint: disable=invalid-name """ Called once per turn to tell the Python AI to generate and issue orders, i.e. to control its empire. After leaving this function, the AI's turn will be finished and its orders will be sent to the server. """ try: rules = fo.getGameRules() debug("Defined game rules:") for rule_name, rule_value in rules.getRulesAsStrings().items(): debug("%s: %s", rule_name, rule_value) debug("Rule RULE_NUM_COMBAT_ROUNDS value: " + str(rules.getInt("RULE_NUM_COMBAT_ROUNDS"))) except Exception as e: error("Exception %s when trying to get game rules" % e, exc_info=True) # If nothing can be ordered anyway, exit early. # Note that there is no need to update meters etc. in this case. empire = fo.getEmpire() if empire is None: fatal("This client has no empire. Aborting order generation.") return if empire.eliminated: info("This empire has been eliminated. Aborting order generation.") return # This code block is required for correct AI work. info("Meter / Resource Pool updating...") fo.initMeterEstimatesDiscrepancies() fo.updateMeterEstimates(False) fo.updateResourcePools() turn = fo.currentTurn() aistate = get_aistate() turn_uid = aistate.set_turn_uid() debug("\n\n\n" + "=" * 20) debug("Starting turn %s (%s) of game: %s" % (turn, turn_uid, aistate.uid)) debug("=" * 20 + "\n") turn_timer.start("AI planning") # set the random seed (based on galaxy seed, empire name and current turn) # for game-reload consistency. random_seed = str( fo.getGalaxySetupData().seed) + "%05d%s" % (turn, fo.getEmpire().name) random.seed(random_seed) universe = fo.getUniverse() empire = fo.getEmpire() planet_id = PlanetUtilsAI.get_capital() planet = None if planet_id is not None: planet = universe.getPlanet(planet_id) aggression_name = get_trait_name_aggression(aistate.character) debug( "***************************************************************************" ) debug( "******* Log info for AI progress chart script. Do not modify. **********" ) debug("Generating Orders") debug( "EmpireID: {empire.empireID}" " Name: {empire.name}_{empire.empireID}_pid:{p_id}_{p_name}RIdx_{res_idx}_{aggression}" " Turn: {turn}".format(empire=empire, p_id=fo.playerID(), p_name=fo.playerName(), res_idx=ResearchAI.get_research_index(), turn=turn, aggression=aggression_name.capitalize())) debug( "EmpireColors: {0.colour[0]} {0.colour[1]} {0.colour[2]} {0.colour[3]}" .format(empire)) if planet: debug("CapitalID: " + str(planet_id) + " Name: " + planet.name + " Species: " + planet.speciesName) else: debug("CapitalID: None Currently Name: None Species: None ") debug( "***************************************************************************" ) debug( "***************************************************************************" ) # When loading a savegame, the AI will already have issued orders for this turn. # To avoid duplicate orders, generally try not to replay turns. However, for debugging # purposes it is often useful to replay the turn and observe varying results after # code changes. Set the replay_after_load flag in the AI config to let the AI issue # new orders after a game load. Note that the orders from the original savegame are # still being issued and the AIstate was saved after those orders were issued. # TODO: Consider adding an option to clear AI orders after load (must save AIstate at turn start then) if fo.currentTurn() == aistate.last_turn_played: info("The AIstate indicates that this turn was already played.") if not check_bool(get_option_dict().get('replay_turn_after_load', 'False')): info( "Aborting new order generation. Orders from savegame will still be issued." ) return info("Issuing new orders anyway.") if turn == 1: human_player = fo.empirePlayerID(1) greet = diplomatic_corp.get_first_turn_greet_message() fo.sendChatMessage( human_player, '%s (%s): [[%s]]' % (empire.name, get_trait_name_aggression(aistate.character), greet)) aistate.prepare_for_new_turn() debug("Calling AI Modules") # call AI modules action_list = [ ColonisationAI.survey_universe, ProductionAI.find_best_designs_this_turn, PriorityAI.calculate_priorities, ExplorationAI.assign_scouts_to_explore_systems, ColonisationAI.assign_colony_fleets_to_colonise, InvasionAI.assign_invasion_fleets_to_invade, MilitaryAI.assign_military_fleets_to_systems, FleetUtilsAI.generate_fleet_orders_for_fleet_missions, FleetUtilsAI.issue_fleet_orders_for_fleet_missions, ResearchAI.generate_research_orders, ProductionAI.generate_production_orders, ResourcesAI.generate_resources_orders, ] for action in action_list: try: main_timer.start(action.__name__) action() main_timer.stop() except Exception as e: error("Exception %s while trying to %s" % (e, action.__name__), exc_info=True) main_timer.stop_print_and_clear() turn_timer.stop_print_and_clear() turn_timer.start("Server_Processing") aistate.last_turn_played = fo.currentTurn() if using_statprof: try: statprof.stop() statprof.display() statprof.start() except: # noqa: E722 pass
def __clean_fleet_roles(self, just_resumed=False): """Removes fleetRoles if a fleet has been lost, and update fleet Ratings.""" for sys_id in self.systemStatus: self.systemStatus[sys_id]['myFleetRating'] = 0 self.systemStatus[sys_id]['myFleetRatingVsPlanets'] = 0 universe = fo.getUniverse() ok_fleets = FleetUtilsAI.get_empire_fleet_ids() fleet_list = sorted(list(self.__fleetRoleByID)) ship_count = 0 destroyed_object_ids = universe.destroyedObjectIDs(fo.empireID()) fleet_table = Table([ Text('Fleet'), Float('Rating'), Float('Troops'), Text('Location'), Text('Destination') ], table_name="Fleet Summary Turn %d" % fo.currentTurn()) for fleet_id in fleet_list: status = self.fleetStatus.setdefault(fleet_id, {}) rating = CombatRatingsAI.get_fleet_rating( fleet_id, self.get_standard_enemy()) troops = FleetUtilsAI.count_troops_in_fleet(fleet_id) old_sys_id = status.get('sysID', -2) fleet = universe.getFleet(fleet_id) if fleet: sys_id = fleet.systemID if old_sys_id in [-2, -1]: old_sys_id = sys_id status['nships'] = len(fleet.shipIDs) ship_count += status['nships'] else: sys_id = old_sys_id # can still retrieve a fleet object even if fleet was just destroyed, so shouldn't get here # however,this has been observed happening, and is the reason a fleet check was added a few lines below. # Not at all sure how this came about, but was throwing off threat assessments if fleet_id not in ok_fleets: # or fleet.empty: if (fleet and self.__fleetRoleByID.get(fleet_id, -1) != -1 and fleet_id not in destroyed_object_ids and [ ship_id for ship_id in fleet.shipIDs if ship_id not in destroyed_object_ids ]): if not just_resumed: fleetsLostBySystem.setdefault(old_sys_id, []).append( max(rating, MilitaryAI.MinThreat)) if fleet_id in self.__fleetRoleByID: del self.__fleetRoleByID[fleet_id] if fleet_id in self.__aiMissionsByFleetID: del self.__aiMissionsByFleetID[fleet_id] if fleet_id in self.fleetStatus: del self.fleetStatus[fleet_id] continue else: # fleet in ok fleets this_sys = universe.getSystem(sys_id) next_sys = universe.getSystem(fleet.nextSystemID) fleet_table.add_row([ fleet, rating, troops, this_sys or 'starlane', next_sys or '-', ]) status['rating'] = rating if next_sys: status['sysID'] = next_sys.id elif this_sys: status['sysID'] = this_sys.id else: main_mission = self.get_fleet_mission(fleet_id) main_mission_type = (main_mission.getAIMissionTypes() + [-1])[0] if main_mission_type != -1: targets = main_mission.getAITargets(main_mission_type) if targets: m_mt0 = targets[0] if isinstance(m_mt0.target_type, System): status[ 'sysID'] = m_mt0.target.id # hmm, but might still be a fair ways from here fleet_table.print_table() self.shipCount = ship_count # Next string used in charts. Don't modify it! print "Empire Ship Count: ", ship_count print "Empire standard fighter summary: ", CombatRatingsAI.get_empire_standard_fighter( ).get_stats() print "------------------------"
def get_invasion_fleets(): invasion_timer.start("gathering initial info") universe = fo.getUniverse() empire = fo.getEmpire() empire_id = fo.empireID() home_system_id = PlanetUtilsAI.get_capital_sys_id() visible_system_ids = list(foAI.foAIstate.visInteriorSystemIDs) + list( foAI.foAIstate.visBorderSystemIDs) if home_system_id != INVALID_ID: accessible_system_ids = [ sys_id for sys_id in visible_system_ids if (sys_id != INVALID_ID) and universe.systemsConnected(sys_id, home_system_id, empire_id) ] else: print "Warning: Empire has no identifiable homeworld; will treat all visible planets as accessible." # TODO: check if any troop ships owned, use their system as home system accessible_system_ids = visible_system_ids acessible_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids( accessible_system_ids) all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids( acessible_planet_ids) # includes unpopulated outposts all_populated_planets = PlanetUtilsAI.get_populated_planet_ids( acessible_planet_ids) # includes unowned natives empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire( universe.planetIDs) invadable_planet_ids = set(all_owned_planet_ids).union( all_populated_planets) - set(empire_owned_planet_ids) invasion_targeted_planet_ids = get_invasion_targeted_planet_ids( universe.planetIDs, MissionType.INVASION) invasion_targeted_planet_ids.extend( get_invasion_targeted_planet_ids(universe.planetIDs, MissionType.ORBITAL_INVASION)) all_invasion_targeted_system_ids = set( PlanetUtilsAI.get_systems(invasion_targeted_planet_ids)) invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role( MissionType.INVASION) num_invasion_fleets = len( FleetUtilsAI.extract_fleet_ids_without_mission_types( invasion_fleet_ids)) print "Current Invasion Targeted SystemIDs: ", PlanetUtilsAI.sys_name_ids( AIstate.invasionTargetedSystemIDs) print "Current Invasion Targeted PlanetIDs: ", PlanetUtilsAI.planet_name_ids( invasion_targeted_planet_ids) print invasion_fleet_ids and "Invasion Fleet IDs: %s" % invasion_fleet_ids or "Available Invasion Fleets: 0" print "Invasion Fleets Without Missions: %s" % num_invasion_fleets invasion_timer.start("planning troop base production") reserved_troop_base_targets = [] if foAI.foAIstate.character.may_invade_with_bases(): available_pp = {} for el in empire.planetsWithAvailablePP: # keys are sets of ints; data is doubles avail_pp = el.data() for pid in el.key(): available_pp[pid] = avail_pp # For planning base trooper invasion targets we have a two-pass system. (1) In the first pass we consider all # the invasion targets and figure out which ones appear to be suitable for using base troopers against (i.e., we # already have a populated planet in the same system that could build base troopers) and we have at least a # minimal amount of PP available, and (2) in the second pass we go through the reserved base trooper target list # and check to make sure that there does not appear to be too much military action still needed before the # target is ready to be invaded, we double check that not too many base troopers would be needed, and if things # look clear then we queue up the base troopers on the Production Queue and keep track of where we are building # them, and how many; we may also disqualify and remove previously qualified targets (in case, for example, # we lost our base trooper source planet since it was first added to list). # # For planning and tracking base troopers under construction, we use a dictionary store in # foAI.foAIstate.qualifyingTroopBaseTargets, keyed by the invasion target planet ID. We only store values # for invasion targets that appear likely to be suitable for base trooper use, and store a 2-item list. # The first item in this list is the ID of the planet where we expect to build the base troopers, and the second # entry initially is set to INVALID_ID (-1). The presence of this entry in qualifyingTroopBaseTargets # flags this target as being reserved as a base-trooper invasion target. # In the second pass, if/when we actually start construction, then we modify the record, replacing that second # value with the ID of the planet where the troopers are actually being built. (Right now that is always the # same as the source planet originally identified, but we could consider reevaluating that, or use that second # value to instead record how many base troopers have been queued, so that on later turns we can assess if the # process got delayed & perhaps more troopers need to be queued). secure_ai_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [MissionType.SECURE]) # Pass 1: identify qualifying base troop invasion targets for pid in invadable_planet_ids: # TODO: reorganize if pid in foAI.foAIstate.qualifyingTroopBaseTargets: continue planet = universe.getPlanet(pid) if not planet: continue sys_id = planet.systemID sys_partial_vis_turn = get_partial_visibility_turn(sys_id) planet_partial_vis_turn = get_partial_visibility_turn(pid) if planet_partial_vis_turn < sys_partial_vis_turn: continue best_base_planet = INVALID_ID best_trooper_count = 0 for pid2 in state.get_empire_planets_by_system( sys_id, include_outposts=False): if available_pp.get( pid2, 0 ) < 2: # TODO: improve troop base PP sufficiency determination break planet2 = universe.getPlanet(pid2) if not planet2 or planet2.speciesName not in ColonisationAI.empire_ship_builders: continue best_base_trooper_here = \ ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_ORBITAL_INVASION, pid2)[1] if not best_base_trooper_here: continue troops_per_ship = best_base_trooper_here.troopCapacity if not troops_per_ship: continue species_troop_grade = CombatRatingsAI.get_species_troops_grade( planet2.speciesName) troops_per_ship = CombatRatingsAI.weight_attack_troops( troops_per_ship, species_troop_grade) if troops_per_ship > best_trooper_count: best_base_planet = pid2 best_trooper_count = troops_per_ship if best_base_planet != INVALID_ID: foAI.foAIstate.qualifyingTroopBaseTargets.setdefault( pid, [best_base_planet, INVALID_ID]) # Pass 2: for each target previously identified for base troopers, check that still qualifies and # check how many base troopers would be needed; if reasonable then queue up the troops and record this in # foAI.foAIstate.qualifyingTroopBaseTargets for pid in foAI.foAIstate.qualifyingTroopBaseTargets.keys(): planet = universe.getPlanet(pid) if planet and planet.owner == empire_id: del foAI.foAIstate.qualifyingTroopBaseTargets[pid] continue if pid in invasion_targeted_planet_ids: # TODO: consider overriding standard invasion mission continue if foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] != -1: reserved_troop_base_targets.append(pid) if planet: all_invasion_targeted_system_ids.add(planet.systemID) # TODO: evaluate changes to situation, any more troops needed, etc. continue # already building for here _, planet_troops = evaluate_invasion_planet( pid, secure_ai_fleet_missions, False) sys_id = planet.systemID this_sys_status = foAI.foAIstate.systemStatus.get(sys_id, {}) troop_tally = 0 for _fid in this_sys_status.get('myfleets', []): troop_tally += FleetUtilsAI.count_troops_in_fleet(_fid) if troop_tally > planet_troops: # base troopers appear unneeded del foAI.foAIstate.qualifyingTroopBaseTargets[pid] continue if (planet.currentMeterValue(fo.meterType.shield) > 0 and (this_sys_status.get('myFleetRating', 0) < 0.8 * this_sys_status.get('totalThreat', 0) or this_sys_status.get('myFleetRatingVsPlanets', 0) < this_sys_status.get('planetThreat', 0))): # this system not secured, so ruling out invasion base troops for now # don't immediately delete from qualifyingTroopBaseTargets or it will be opened up for regular troops continue loc = foAI.foAIstate.qualifyingTroopBaseTargets[pid][0] best_base_trooper_here = ProductionAI.get_best_ship_info( PriorityType.PRODUCTION_ORBITAL_INVASION, loc)[1] loc_planet = universe.getPlanet(loc) if best_base_trooper_here is None: # shouldn't be possible at this point, but just to be safe warn( "Could not find a suitable orbital invasion design at %s" % loc_planet) continue # TODO: have TroopShipDesigner give the expected number of troops including species effects directly troops_per_ship = best_base_trooper_here.troopCapacity species_troop_grade = CombatRatingsAI.get_species_troops_grade( loc_planet.speciesName) troops_per_ship = CombatRatingsAI.weight_attack_troops( troops_per_ship, species_troop_grade) if not troops_per_ship: warn( "The best orbital invasion design at %s seems not to have any troop capacity." % loc_planet) continue _, col_design, build_choices = ProductionAI.get_best_ship_info( PriorityType.PRODUCTION_ORBITAL_INVASION, loc) if not col_design: continue if loc not in build_choices: warn( 'Best troop design %s can not be produced at planet with id: %s\d' % (col_design, build_choices)) continue n_bases = math.ceil( (planet_troops + 1) / troops_per_ship) # TODO: reconsider this +1 safety factor # TODO: evaluate cost and time-to-build of best base trooper here versus cost and time-to-build-and-travel # for best regular trooper elsewhere # For now, we assume what building base troopers is best so long as either (1) we would need no more than # MAX_BASE_TROOPERS_POOR_INVADERS base troop ships, or (2) our base troopers have more than 1 trooper per # ship and we would need no more than MAX_BASE_TROOPERS_GOOD_INVADERS base troop ships if (n_bases <= MAX_BASE_TROOPERS_POOR_INVADERS or (troops_per_ship > 1 and n_bases <= MAX_BASE_TROOPERS_GOOD_INVADERS)): print "ruling out base invasion troopers for %s due to high number (%d) required." % ( planet, n_bases) del foAI.foAIstate.qualifyingTroopBaseTargets[pid] continue print "Invasion base planning, need %d troops at %d pership, will build %d ships." % ( (planet_troops + 1), troops_per_ship, n_bases) retval = fo.issueEnqueueShipProductionOrder(col_design.id, loc) print "Enqueueing %d Troop Bases at %s for %s" % ( n_bases, PlanetUtilsAI.planet_name_ids( [loc]), PlanetUtilsAI.planet_name_ids([pid])) if retval != 0: all_invasion_targeted_system_ids.add(planet.systemID) reserved_troop_base_targets.append(pid) foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] = loc fo.issueChangeProductionQuantityOrder( empire.productionQueue.size - 1, 1, int(n_bases)) fo.issueRequeueProductionOrder(empire.productionQueue.size - 1, 0) invasion_timer.start("evaluating target planets") # TODO: check if any invasion_targeted_planet_ids need more troops assigned evaluated_planet_ids = list( set(invadable_planet_ids) - set(invasion_targeted_planet_ids) - set(reserved_troop_base_targets)) evaluated_planets = assign_invasion_values(evaluated_planet_ids) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, (pscore, ptroops) in evaluated_planets.items()] sorted_planets.sort(key=lambda x: x[1], reverse=True) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, pscore, ptroops in sorted_planets] invasion_table = Table( [Text('Planet'), Float('Score'), Text('Species'), Float('Troops')], table_name="Potential Targets for Invasion Turn %d" % fo.currentTurn()) for pid, pscore, ptroops in sorted_planets: planet = universe.getPlanet(pid) invasion_table.add_row([ planet, pscore, planet and planet.speciesName or "unknown", ptroops ]) print invasion_table.print_table() sorted_planets = filter(lambda x: x[1] > 0, sorted_planets) # export opponent planets for other AI modules AIstate.opponentPlanetIDs = [pid for pid, _, _ in sorted_planets] AIstate.invasionTargets = sorted_planets # export invasion targeted systems for other AI modules AIstate.invasionTargetedSystemIDs = list(all_invasion_targeted_system_ids) invasion_timer.stop(section_name="evaluating %d target planets" % (len(evaluated_planet_ids))) invasion_timer.stop_print_and_clear()
def update_system_status(self): print 10 * "=", "Updating System Threats", 10 * "=" universe = fo.getUniverse() empire = fo.getEmpire() empire_id = fo.empireID() destroyed_object_ids = universe.destroyedObjectIDs(empire_id) supply_unobstructed_systems = set(empire.supplyUnobstructedSystems) min_hidden_attack = 4 min_hidden_health = 8 system_id_list = universe.systemIDs # will normally look at this, the list of all known systems # for use in debugging verbose = False # assess enemy fleets that may have been momentarily visible cur_e_fighters = { CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0] } # start with a dummy entry old_e_fighters = { CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0] } # start with a dummy entry enemy_fleet_ids = [] enemies_by_system = {} my_fleets_by_system = {} fleet_spot_position = {} saw_enemies_at_system = {} my_milship_rating = MilitaryAI.cur_best_mil_ship_rating() current_turn = fo.currentTurn() for fleet_id in universe.fleetIDs: fleet = universe.getFleet(fleet_id) if fleet is None: continue if not fleet.empty: # TODO: check if currently in system and blockaded before accepting destination as location this_system_id = (fleet.nextSystemID != INVALID_ID and fleet.nextSystemID) or fleet.systemID if fleet.ownedBy(empire_id): if fleet_id not in destroyed_object_ids: my_fleets_by_system.setdefault(this_system_id, []).append(fleet_id) fleet_spot_position.setdefault(fleet.systemID, []).append(fleet_id) else: dead_fleet = fleet_id in destroyed_object_ids if not fleet.ownedBy(-1) and (fleet.hasArmedShips or fleet.hasFighterShips): ship_stats = CombatRatingsAI.FleetCombatStats( fleet_id).get_ship_stats(hashable=True) e_f_dict = [ cur_e_fighters, old_e_fighters ][dead_fleet] # track old/dead enemy fighters for rating assessments in case not enough current info for stats in ship_stats: attacks = stats[0] if attacks: e_f_dict.setdefault(stats, [0])[0] += 1 partial_vis_turn = universe.getVisibilityTurnsMap( fleet_id, empire_id).get(fo.visibility.partial, -9999) if not dead_fleet: # TODO: consider checking death of individual ships. If ships had been moved from this fleet # into another fleet, we might have witnessed their death in that other fleet but if this fleet # had not been seen since before that transfer then the ships might also still be listed here. sys_status = self.systemStatus.setdefault( this_system_id, {}) sys_status['enemy_ship_count'] = sys_status.get( 'enemy_ship_count', 0) + len(fleet.shipIDs) if partial_vis_turn >= current_turn - 1: # only interested in immediately recent data saw_enemies_at_system[fleet.systemID] = True enemy_fleet_ids.append(fleet_id) enemies_by_system.setdefault(this_system_id, []).append(fleet_id) if not fleet.ownedBy(-1): self.misc.setdefault('enemies_sighted', {}).setdefault( current_turn, []).append(fleet_id) rating = CombatRatingsAI.get_fleet_rating( fleet_id, enemy_stats=CombatRatingsAI. get_empire_standard_fighter()) if rating > 0.25 * my_milship_rating: self.misc.setdefault( 'dangerous_enemies_sighted', {}).setdefault(current_turn, []).append(fleet_id) e_f_dict = [cur_e_fighters, old_e_fighters][len(cur_e_fighters) == 1] std_fighter = sorted([(v, k) for k, v in e_f_dict.items()])[-1][1] self.__empire_standard_enemy = std_fighter self.empire_standard_enemy_rating = self.get_standard_enemy( ).get_rating() # TODO: If no current information available, rate against own fighters # assess fleet and planet threats & my local fleets for sys_id in system_id_list: sys_status = self.systemStatus.setdefault(sys_id, {}) system = universe.getSystem(sys_id) if verbose: print "AIState threat evaluation for %s" % system # update fleets sys_status['myfleets'] = my_fleets_by_system.get(sys_id, []) sys_status['myFleetsAccessible'] = fleet_spot_position.get( sys_id, []) local_enemy_fleet_ids = enemies_by_system.get(sys_id, []) sys_status['localEnemyFleetIDs'] = local_enemy_fleet_ids if system: sys_status['name'] = system.name for fid in system.fleetIDs: if fid in destroyed_object_ids: # TODO: double check are these checks/deletes necessary? self.delete_fleet_info( fid) # this is safe even if fleet wasn't mine continue fleet = universe.getFleet(fid) if not fleet or fleet.empty: self.delete_fleet_info( fid) # this is safe even if fleet wasn't mine continue # update threats sys_vis_dict = universe.getVisibilityTurnsMap( sys_id, fo.empireID()) partial_vis_turn = sys_vis_dict.get(fo.visibility.partial, -9999) mob_ratings = [] # for mobile unowned monster fleets lost_fleet_rating = 0 enemy_ratings = [] monster_ratings = [] mobile_fleets = [] for fid in local_enemy_fleet_ids: fleet = universe.getFleet(fid) if not fleet: continue fleet_rating = CombatRatingsAI.get_fleet_rating( fid, enemy_stats=CombatRatingsAI.get_empire_standard_fighter()) if fleet.speed == 0: monster_ratings.append(fleet_rating) if verbose: print "\t immobile enemy fleet %s has rating %.1f" % ( fleet, fleet_rating) else: if verbose: print "\t mobile enemy fleet %s has rating %.1f" % ( fleet, fleet_rating) mobile_fleets.append(fid) if fleet.unowned: mob_ratings.append(fleet_rating) else: enemy_ratings.append(fleet_rating) enemy_rating = CombatRatingsAI.combine_ratings_list(enemy_ratings) monster_rating = CombatRatingsAI.combine_ratings_list( monster_ratings) mob_rating = CombatRatingsAI.combine_ratings_list(mob_ratings) if fleetsLostBySystem.get(sys_id, []): lost_fleet_rating = CombatRatingsAI.combine_ratings_list( fleetsLostBySystem[sys_id]) if not system or partial_vis_turn == -9999: # under current visibility rules should not be possible to have any losses or other info here, but just in case... if verbose: print "Have never had partial vis for system %d ( %s ) -- basing threat assessment on old info and lost ships" % ( sys_id, sys_status.get('name', "name unknown")) sys_status.setdefault('local_fleet_threats', set()) sys_status['planetThreat'] = 0 sys_status['fleetThreat'] = int( max( CombatRatingsAI.combine_ratings( enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 1.1 * lost_fleet_rating - monster_rating)) sys_status['monsterThreat'] = int( max(monster_rating, 0.98 * sys_status.get('monsterThreat', 0), 1.1 * lost_fleet_rating - enemy_rating - mob_rating)) sys_status['enemy_threat'] = int( max(enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1 * lost_fleet_rating - monster_rating - mob_rating)) sys_status['mydefenses'] = { 'overall': 0, 'attack': 0, 'health': 0 } sys_status['totalThreat'] = sys_status['fleetThreat'] sys_status['regional_fleet_threats'] = sys_status[ 'local_fleet_threats'].copy() continue # have either stale or current info pattack = 0 phealth = 0 mypattack, myphealth = 0, 0 for pid in system.planetIDs: prating = self.assess_planet_threat(pid, sighting_age=current_turn - partial_vis_turn) planet = universe.getPlanet(pid) if not planet: continue if planet.owner == self.empireID: # TODO: check for diplomatic status mypattack += prating['attack'] myphealth += prating['health'] else: if [ special for special in planet.specials if "_NEST_" in special ]: sys_status['nest_threat'] = 100 pattack += prating['attack'] phealth += prating['health'] sys_status['planetThreat'] = pattack * phealth sys_status['mydefenses'] = { 'overall': mypattack * myphealth, 'attack': mypattack, 'health': myphealth } if max( sys_status.get('totalThreat', 0), pattack * phealth ) >= 0.6 * lost_fleet_rating: # previous threat assessment could account for losses, ignore the losses now lost_fleet_rating = 0 # TODO use sitrep combat info rather than estimating stealthed enemies by fleets lost to them # TODO also only consider past stealthed fleet threat to still be present if the system is still obstructed # TODO: track visibility across turns in order to distinguish the blip of visibility in (losing) combat, # which FO currently treats as being for the previous turn, partially superseding the previous visibility for that turn if not partial_vis_turn == current_turn: # (universe.getVisibility(sys_id, self.empire_id) >= fo.visibility.partial): sys_status.setdefault('local_fleet_threats', set()) sys_status['currently_visible'] = False # print "Stale visibility for system %d ( %s ) -- last seen %d, current Turn %d -- basing threat assessment on old info and lost ships"%(sys_id, sys_status.get('name', "name unknown"), partial_vis_turn, currentTurn) sys_status['fleetThreat'] = int( max( CombatRatingsAI.combine_ratings( enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 2.0 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating))) sys_status['enemy_threat'] = int( max( enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating))) sys_status['monsterThreat'] = int( max(monster_rating, 0.98 * sys_status.get('monsterThreat', 0))) # sys_status['totalThreat'] = ((pattack + enemy_attack + monster_attack) ** 0.8) * ((phealth + enemy_health + monster_health)** 0.6) # reevaluate this sys_status['totalThreat'] = max( CombatRatingsAI.combine_ratings_list([ enemy_rating, mob_rating, monster_rating, pattack * phealth ]), 2 * lost_fleet_rating, 0.98 * sys_status.get('totalThreat', 0)) else: # system considered visible #TODO: reevaluate as visibility rules change sys_status['currently_visible'] = True sys_status['local_fleet_threats'] = set(mobile_fleets) sys_status['fleetThreat'] = int( max( CombatRatingsAI.combine_ratings( enemy_rating, mob_rating), 2 * lost_fleet_rating - monster_rating)) # includes mobile monsters if verbose: print "enemy threat calc parts: enemy rating %.1f, lost fleet rating %.1f, monster_rating %.1f" % ( enemy_rating, lost_fleet_rating, monster_rating) sys_status['enemy_threat'] = int( max(enemy_rating, 2 * lost_fleet_rating - monster_rating)) # does NOT include mobile monsters sys_status['monsterThreat'] = monster_rating sys_status[ 'totalThreat'] = CombatRatingsAI.combine_ratings_list([ enemy_rating, mob_rating, monster_rating, pattack * phealth ]) sys_status['regional_fleet_threats'] = sys_status[ 'local_fleet_threats'].copy() sys_status['fleetThreat'] = max(sys_status['fleetThreat'], sys_status.get('nest_threat', 0)) sys_status['totalThreat'] = max(sys_status['totalThreat'], sys_status.get('nest_threat', 0)) if partial_vis_turn > 0 and sys_id not in supply_unobstructed_systems: # has been seen with Partial Vis, but is currently supply-blocked sys_status['fleetThreat'] = max( sys_status['fleetThreat'], min_hidden_attack * min_hidden_health) sys_status['totalThreat'] = max( sys_status['totalThreat'], ((pattack + min_hidden_attack)**0.8) * ((phealth + min_hidden_health)**0.6)) if verbose and sys_status['fleetThreat'] > 0: print "%s intermediate status: %s" % (system, sys_status) enemy_supply, enemy_near_supply = self.assess_enemy_supply( ) # TODO: assess change in enemy supply over time # assess secondary threats (threats of surrounding systems) and update my fleet rating for sys_id in system_id_list: sys_status = self.systemStatus[sys_id] sys_status['enemies_supplied'] = enemy_supply.get(sys_id, []) sys_status['enemies_nearly_supplied'] = enemy_near_supply.get( sys_id, []) my_ratings_list = [] my_ratings_against_planets_list = [] for fid in sys_status['myfleets']: this_rating = self.get_rating(fid, True, self.get_standard_enemy()) my_ratings_list.append(this_rating) my_ratings_against_planets_list.append( self.get_rating(fid, against_planets=True)) if sys_id != INVALID_ID: sys_status[ 'myFleetRating'] = CombatRatingsAI.combine_ratings_list( my_ratings_list) sys_status[ 'myFleetRatingVsPlanets'] = CombatRatingsAI.combine_ratings_list( my_ratings_against_planets_list) sys_status[ 'all_local_defenses'] = CombatRatingsAI.combine_ratings( sys_status['myFleetRating'], sys_status['mydefenses']['overall']) sys_status['neighbors'] = set( dict_from_map( universe.getSystemNeighborsMap(sys_id, self.empireID))) for sys_id in system_id_list: sys_status = self.systemStatus[sys_id] neighbors = sys_status.get('neighbors', set()) this_system = fo.getUniverse().getSystem(sys_id) if verbose: print "Regional Assessment for %s with local fleet threat %.1f" % ( this_system, sys_status.get('fleetThreat', 0)) jumps2 = set() jumps3 = set() jumps4 = set() for seta, setb in [(neighbors, jumps2), (jumps2, jumps3), (jumps3, jumps4)]: for sys2id in seta: setb.update( self.systemStatus.get(sys2id, {}).get('neighbors', set())) jump2ring = jumps2 - neighbors - {sys_id} jump3ring = jumps3 - jumps2 - neighbors - {sys_id} jump4ring = jumps4 - jumps3 - jumps2 - neighbors - {sys_id} sys_status['2jump_ring'] = jump2ring sys_status['3jump_ring'] = jump3ring sys_status['4jump_ring'] = jump4ring threat, max_threat, myrating, j1_threats = self.area_ratings( neighbors, ref_sys_name="neighbors %s" % this_system) if verbose else self.area_ratings(neighbors) sys_status['neighborThreat'] = threat sys_status['max_neighbor_threat'] = max_threat sys_status['my_neighbor_rating'] = myrating threat, max_threat, myrating, j2_threats = self.area_ratings( jump2ring, ref_sys_name="jump2 %s" % this_system) if verbose else self.area_ratings(jump2ring) sys_status['jump2_threat'] = threat sys_status['my_jump2_rating'] = myrating threat, max_threat, myrating, j3_threats = self.area_ratings( jump3ring) sys_status['jump3_threat'] = threat sys_status['my_jump3_rating'] = myrating threat_keys = ['fleetThreat', 'neighborThreat', 'jump2_threat' ] # for local system includes both enemies and mobs sys_status[ 'regional_threat'] = CombatRatingsAI.combine_ratings_list( map(lambda x: sys_status.get(x, 0), threat_keys)) # TODO: investigate cases where regional_threat has been nonzero but no regional_threat_fleets # (probably due to attenuating history of past threats) sys_status.setdefault('regional_fleet_threats', set()).update(j1_threats, j2_threats)
def get_invasion_fleets(): invasion_timer.start("gathering initial info") universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID all_invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role( MissionType.INVASION) AIstate.invasionFleetIDs = FleetUtilsAI.extract_fleet_ids_without_mission_types( all_invasion_fleet_ids) home_system_id = PlanetUtilsAI.get_capital_sys_id() visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys( ) + foAI.foAIstate.visBorderSystemIDs.keys() if home_system_id != -1: accessible_system_ids = [ sys_id for sys_id in visible_system_ids if (sys_id != -1) and universe.systemsConnected(sys_id, home_system_id, empire_id) ] else: print "Warning: Empire has no identifiable homeworld; will treat all visible planets as accessible." accessible_system_ids = visible_system_ids # TODO: check if any troop ships owned, use their system as home system acessible_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids( accessible_system_ids) all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids( acessible_planet_ids) # includes unpopulated outposts all_populated_planets = PlanetUtilsAI.get_populated_planet_ids( acessible_planet_ids) # includes unowned natives empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire( universe.planetIDs) invadable_planet_ids = set(all_owned_planet_ids).union( all_populated_planets) - set(empire_owned_planet_ids) invasion_targeted_planet_ids = get_invasion_targeted_planet_ids( universe.planetIDs, MissionType.INVASION) invasion_targeted_planet_ids.extend( get_invasion_targeted_planet_ids(universe.planetIDs, MissionType.ORBITAL_INVASION)) all_invasion_targeted_system_ids = set( PlanetUtilsAI.get_systems(invasion_targeted_planet_ids)) invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role( MissionType.INVASION) num_invasion_fleets = len( FleetUtilsAI.extract_fleet_ids_without_mission_types( invasion_fleet_ids)) print "Current Invasion Targeted SystemIDs: ", PlanetUtilsAI.sys_name_ids( AIstate.invasionTargetedSystemIDs) print "Current Invasion Targeted PlanetIDs: ", PlanetUtilsAI.planet_name_ids( invasion_targeted_planet_ids) print invasion_fleet_ids and "Invasion Fleet IDs: %s" % invasion_fleet_ids or "Available Invasion Fleets: 0" print "Invasion Fleets Without Missions: %s" % num_invasion_fleets invasion_timer.start("planning troop base production") # only do base invasions if aggression is typical or above reserved_troop_base_targets = [] if foAI.foAIstate.aggression > fo.aggression.typical: available_pp = {} for el in empire.planetsWithAvailablePP: # keys are sets of ints; data is doubles avail_pp = el.data() for pid in el.key(): available_pp[pid] = avail_pp for pid in invadable_planet_ids: # TODO: reorganize planet = universe.getPlanet(pid) if not planet: continue sys_id = planet.systemID sys_partial_vis_turn = universe.getVisibilityTurnsMap( planet.systemID, empire_id).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap( pid, empire_id).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: continue for pid2 in state.get_empire_inhabited_planets_by_system().get( sys_id, []): if available_pp.get( pid2, 0 ) < 2: # TODO: improve troop base PP sufficiency determination break planet2 = universe.getPlanet(pid2) if not planet2: continue if pid not in foAI.foAIstate.qualifyingTroopBaseTargets and planet2.speciesName in ColonisationAI.empire_ship_builders: foAI.foAIstate.qualifyingTroopBaseTargets.setdefault( pid, [pid2, -1]) break for pid in list(foAI.foAIstate.qualifyingTroopBaseTargets): planet = universe.getPlanet( pid ) # TODO: also check that still have a colony in this system that can make troops if planet and planet.owner == empire_id: del foAI.foAIstate.qualifyingTroopBaseTargets[pid] secure_ai_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [MissionType.SECURE]) for pid in (set(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) - set(invasion_targeted_planet_ids) ): # TODO: consider overriding standard invasion mission planet = universe.getPlanet(pid) if foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] != -1: reserved_troop_base_targets.append(pid) if planet: all_invasion_targeted_system_ids.add(planet.systemID) continue # already building for here sys_id = planet.systemID this_sys_status = foAI.foAIstate.systemStatus.get(sys_id, {}) if (planet.currentMeterValue(fo.meterType.shield) > 0 and this_sys_status.get('myFleetRating', 0) < 0.8 * this_sys_status.get('totalThreat', 0)): continue loc = foAI.foAIstate.qualifyingTroopBaseTargets[pid][0] best_base_trooper_here = ProductionAI.get_best_ship_info( PriorityType.PRODUCTION_ORBITAL_INVASION, loc)[1] loc_planet = universe.getPlanet(loc) if best_base_trooper_here is None: # shouldn't be possible at this point, but just to be safe print "Could not find a suitable orbital invasion design at %s" % loc_planet continue # TODO: have TroopShipDesigner give the expected number of troops including species effects directly troops_per_ship = best_base_trooper_here.troopCapacity _, _, species_troop_grade = foAI.foAIstate.get_piloting_grades( loc_planet.speciesName) troops_per_ship = foAI.foAIstate.weight_attack_troops( troops_per_ship, species_troop_grade) if not troops_per_ship: print "The best orbital invasion design at %s seems not to have any troop capacity." % loc_planet continue this_score, p_troops = evaluate_invasion_planet( pid, empire, secure_ai_fleet_missions, False) _, col_design, build_choices = ProductionAI.get_best_ship_info( PriorityType.PRODUCTION_ORBITAL_INVASION, loc) if not col_design: continue if loc not in build_choices: sys.stderr.write( 'Best troop design %s can not be produces in at planet with id: %s\d' % (col_design, build_choices)) n_bases = math.ceil( (p_troops + 1) / troops_per_ship) # TODO: reconsider this +1 safety factor print "Invasion base planning, need %d troops at %d pership, will build %d ships." % ( (p_troops + 1), troops_per_ship, n_bases) retval = fo.issueEnqueueShipProductionOrder(col_design.id, loc) print "Enqueueing %d Troop Bases at %s for %s" % ( n_bases, PlanetUtilsAI.planet_name_ids( [loc]), PlanetUtilsAI.planet_name_ids([pid])) if retval != 0: all_invasion_targeted_system_ids.add(planet.systemID) reserved_troop_base_targets.append(pid) foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] = loc fo.issueChangeProductionQuantityOrder( empire.productionQueue.size - 1, 1, int(n_bases)) fo.issueRequeueProductionOrder(empire.productionQueue.size - 1, 0) invasion_timer.start("evaluating target planets") # TODO: check if any invasion_targeted_planet_ids need more troops assigned evaluated_planet_ids = list( set(invadable_planet_ids) - set(invasion_targeted_planet_ids) - set(reserved_troop_base_targets)) evaluated_planets = assign_invasion_values(evaluated_planet_ids, empire) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, (pscore, ptroops) in evaluated_planets.items()] sorted_planets.sort(key=lambda x: x[1], reverse=True) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, pscore, ptroops in sorted_planets] invasion_table = Table( [Text('Planet'), Float('Score'), Text('Species'), Float('Troops')], table_name="Potential Targets for Invasion Turn %d" % fo.currentTurn()) for pid, pscore, ptroops in sorted_planets: planet = universe.getPlanet(pid) invasion_table.add_row([ planet, pscore, planet and planet.speciesName or "unknown", ptroops ]) print invasion_table.print_table() sorted_planets = filter(lambda x: x[1] > 0, sorted_planets) # export opponent planets for other AI modules AIstate.opponentPlanetIDs = [pid for pid, _, _ in sorted_planets] AIstate.invasionTargets = sorted_planets # export invasion targeted systems for other AI modules AIstate.invasionTargetedSystemIDs = list(all_invasion_targeted_system_ids) invasion_timer.stop(section_name="evaluating %d target planets" % (len(evaluated_planet_ids))) invasion_timer.end()
def get_military_fleets(mil_fleets_ids=None, try_reset=True, thisround="Main"): """Get armed military fleets.""" global _military_allocations universe = fo.getUniverse() empire_id = fo.empireID() home_system_id = PlanetUtilsAI.get_capital_sys_id() all_military_fleet_ids = (mil_fleets_ids if mil_fleets_ids is not None else FleetUtilsAI.get_empire_fleet_ids_by_role( MissionType.MILITARY)) if try_reset and (fo.currentTurn() + empire_id) % 30 == 0 and thisround == "Main": try_again(all_military_fleet_ids, try_reset=False, thisround=thisround + " Reset") return mil_fleets_ids = list( FleetUtilsAI.extract_fleet_ids_without_mission_types( all_military_fleet_ids)) mil_needing_repair_ids, mil_fleets_ids = avail_mil_needing_repair( mil_fleets_ids, split_ships=True) avail_mil_rating = sum( map(CombatRatingsAI.get_fleet_rating, mil_fleets_ids)) if not mil_fleets_ids: if "Main" in thisround: _military_allocations = [] return [] # for each system, get total rating of fleets assigned to it already_assigned_rating = {} already_assigned_rating_vs_planets = {} systems_status = foAI.foAIstate.systemStatus enemy_sup_factor = {} # enemy supply for sys_id in universe.systemIDs: already_assigned_rating[sys_id] = 0 already_assigned_rating_vs_planets[sys_id] = 0 enemy_sup_factor[sys_id] = min( 2, len( systems_status.get(sys_id, {}).get('enemies_nearly_supplied', []))) for fleet_id in [ fid for fid in all_military_fleet_ids if fid not in mil_fleets_ids ]: ai_fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id) if not ai_fleet_mission.target: # shouldn't really be possible continue last_sys = ai_fleet_mission.target.get_system( ).id # will count this fleet as assigned to last system in target list # TODO last_sys or target sys? this_rating = CombatRatingsAI.get_fleet_rating(fleet_id) this_rating_vs_planets = CombatRatingsAI.get_fleet_rating_against_planets( fleet_id) already_assigned_rating[last_sys] = CombatRatingsAI.combine_ratings( already_assigned_rating.get(last_sys, 0), this_rating) already_assigned_rating_vs_planets[ last_sys] = CombatRatingsAI.combine_ratings( already_assigned_rating_vs_planets.get(last_sys, 0), this_rating_vs_planets) for sys_id in universe.systemIDs: my_defense_rating = systems_status.get(sys_id, {}).get('mydefenses', {}).get('overall', 0) already_assigned_rating[sys_id] = CombatRatingsAI.combine_ratings( my_defense_rating, already_assigned_rating[sys_id]) if _verbose_mil_reporting and already_assigned_rating[sys_id]: print "\t System %s already assigned rating %.1f" % ( universe.getSystem(sys_id), already_assigned_rating[sys_id]) # get systems to defend capital_id = PlanetUtilsAI.get_capital() if capital_id is not None: capital_planet = universe.getPlanet(capital_id) else: capital_planet = None # TODO: if no owned planets try to capture one! if capital_planet: capital_sys_id = capital_planet.systemID else: # should be rare, but so as to not break code below, pick a randomish mil-centroid system capital_sys_id = None # unless we can find one to use system_dict = {} for fleet_id in all_military_fleet_ids: status = foAI.foAIstate.fleetStatus.get(fleet_id, None) if status is not None: sys_id = status['sysID'] if not list(universe.getSystem(sys_id).planetIDs): continue system_dict[sys_id] = system_dict.get(sys_id, 0) + status.get( 'rating', 0) ranked_systems = sorted([(val, sys_id) for sys_id, val in system_dict.items()]) if ranked_systems: capital_sys_id = ranked_systems[-1][-1] else: try: capital_sys_id = foAI.foAIstate.fleetStatus.items( )[0][1]['sysID'] except: pass num_targets = max(10, PriorityAI.allotted_outpost_targets) top_target_planets = ([ pid for pid, pscore, trp in AIstate. invasionTargets[:PriorityAI.allottedInvasionTargets] if pscore > 20 ] + [ pid for pid, (pscore, spec) in foAI.foAIstate.colonisableOutpostIDs.items( )[:num_targets] if pscore > 20 ] + [ pid for pid, (pscore, spec) in foAI.foAIstate.colonisablePlanetIDs.items() [:num_targets] if pscore > 20 ]) top_target_planets.extend(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) base_col_target_systems = PlanetUtilsAI.get_systems(top_target_planets) top_target_systems = [] for sys_id in AIstate.invasionTargetedSystemIDs + base_col_target_systems: if sys_id not in top_target_systems: if foAI.foAIstate.systemStatus[sys_id][ 'totalThreat'] > get_tot_mil_rating(): continue top_target_systems.append( sys_id) # doing this rather than set, to preserve order try: # capital defense allocation_helper = AllocationHelper( already_assigned_rating, already_assigned_rating_vs_planets, avail_mil_rating, try_reset) if capital_sys_id is not None: CapitalDefenseAllocator(capital_sys_id, allocation_helper).allocate() # defend other planets empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire( universe.planetIDs) empire_occupied_system_ids = list( set(PlanetUtilsAI.get_systems(empire_planet_ids)) - {capital_sys_id}) for sys_id in empire_occupied_system_ids: PlanetDefenseAllocator(sys_id, allocation_helper).allocate() # attack / protect high priority targets for sys_id in top_target_systems: TopTargetAllocator(sys_id, allocation_helper).allocate() # enemy planets other_targeted_system_ids = [ sys_id for sys_id in set( PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if sys_id not in top_target_systems ] for sys_id in other_targeted_system_ids: TargetAllocator(sys_id, allocation_helper).allocate() # colony / outpost targets other_targeted_system_ids = [ sys_id for sys_id in list( set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if sys_id not in top_target_systems ] for sys_id in other_targeted_system_ids: OutpostTargetAllocator(sys_id, allocation_helper).allocate() # TODO blockade enemy systems # interior systems targetable_ids = set(state.get_systems_by_supply_tier(0)) current_mil_systems = [ sid for sid, _, _, _, _ in allocation_helper.allocations ] interior_targets1 = targetable_ids.difference(current_mil_systems) interior_targets = [ sid for sid in interior_targets1 if (allocation_helper.threat_bias + systems_status.get(sid, {}).get('totalThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid]) ] for sys_id in interior_targets: InteriorTargetsAllocator(sys_id, allocation_helper).allocate() # TODO Exploration targets # border protections visible_system_ids = foAI.foAIstate.visInteriorSystemIDs | foAI.foAIstate.visBorderSystemIDs accessible_system_ids = ([ sys_id for sys_id in visible_system_ids if universe.systemsConnected(sys_id, home_system_id, empire_id) ] if home_system_id != INVALID_ID else []) current_mil_systems = [ sid for sid, alloc, rvp, take_any, _ in allocation_helper.allocations if alloc > 0 ] border_targets1 = [ sid for sid in accessible_system_ids if sid not in current_mil_systems ] border_targets = [ sid for sid in border_targets1 if (allocation_helper.threat_bias + systems_status.get(sid, {}).get('fleetThreat', 0) + systems_status.get(sid, {}).get('planetThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid]) ] for sys_id in border_targets: BorderSecurityAllocator(sys_id, allocation_helper).allocate() except ReleaseMilitaryException: try_again(all_military_fleet_ids) return new_allocations = [] remaining_mil_rating = avail_mil_rating # for top categories assign max_alloc right away as available for cat in ['capitol', 'occupied', 'topTargets']: for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get( cat, []): if remaining_mil_rating <= 0: break this_alloc = min(remaining_mil_rating, max_alloc) new_allocations.append((sid, this_alloc, alloc, rvp, take_any)) remaining_mil_rating -= this_alloc base_allocs = set() # for lower priority categories, first assign base_alloc around to all, then top up as available for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']: for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get( cat, []): if remaining_mil_rating <= 0: break base_allocs.add(sid) remaining_mil_rating -= alloc for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']: for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get( cat, []): if sid not in base_allocs: break if remaining_mil_rating <= 0: new_allocations.append((sid, alloc, alloc, rvp, take_any)) else: new_rating = min(remaining_mil_rating + alloc, max_alloc) new_allocations.append((sid, new_rating, alloc, rvp, take_any)) remaining_mil_rating -= (new_rating - alloc) if "Main" in thisround: _military_allocations = new_allocations if _verbose_mil_reporting or "Main" in thisround: print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % ( thisround, dict([(sid, alloc) for sid, alloc, _, _, _ in new_allocations])) print "(Apparently) remaining military rating: %.1f" % remaining_mil_rating return new_allocations
def score(self): if self.__last_score_update != fo.currentTurn(): self.__update_score() return self.__score
def generate_fleet_orders_for_fleet_missions(): """Generates fleet orders from targets.""" print("Generating fleet orders") # The following fleet lists are based on *Roles* -- Secure type missions are done by fleets with Military Roles print "Fleets by Role\n" print "Exploration Fleets : %s" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_EXPLORATION) print "Colonization Fleets:%s" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_COLONISATION) print "Outpost Fleets :%s" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_OUTPOST) print "Invasion Fleets :%s" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_INVASION) print "Military Fleets :%s" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_MILITARY) print "Orbital Defense Fleets :%s" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_ORBITAL_DEFENSE) print "Outpost Base Fleets :%s" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_ORBITAL_OUTPOST) print "Invasion Base Fleets :%s" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_ORBITAL_INVASION) print "Securing Fleets :%s (currently FLEET_MISSION_MILITARY should be used instead of this Role)" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_SECURE) print "Unclassifiable Fleets :%s" % get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_INVALID) if fo.currentTurn() < 50: print print "Explored systems :" print_systems( foAI.foAIstate.get_explorable_systems( AIExplorableSystemType.EXPLORABLE_SYSTEM_EXPLORED)) print "Unexplored systems:" print_systems( foAI.foAIstate.get_explorable_systems( AIExplorableSystemType.EXPLORABLE_SYSTEM_UNEXPLORED)) print exploration_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [AIFleetMissionType.FLEET_MISSION_EXPLORATION]) if exploration_fleet_missions: print "Exploration targets: " else: print "Exploration targets: None" for explorationAIFleetMission in exploration_fleet_missions: print " %s" % explorationAIFleetMission colonisation_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [AIFleetMissionType.FLEET_MISSION_COLONISATION]) if colonisation_fleet_missions: print "Colonization targets: " else: print "Colonization targets: None" for colonisation_fleet_mission in colonisation_fleet_missions: print " %s" % colonisation_fleet_mission outpost_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [AIFleetMissionType.FLEET_MISSION_OUTPOST]) if outpost_fleet_missions: print "Outpost targets: " else: print "Outpost targets: None" for outpost_fleet_mission in outpost_fleet_missions: print " %s" % outpost_fleet_mission outpost_base_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [AIFleetMissionType.FLEET_MISSION_ORBITAL_OUTPOST]) if outpost_base_fleet_missions: print "Outpost Base targets (must have been interrupted by combat): " else: print "Outpost targets: None (as expected, due to expected timing of order submission and execution)" for outpost_fleet_mission in outpost_base_fleet_missions: print " %s" % outpost_fleet_mission invasion_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [AIFleetMissionType.FLEET_MISSION_INVASION]) if invasion_fleet_missions: print "Invasion targets: " else: print "Invasion targets: None" for invasion_fleet_mission in invasion_fleet_missions: print " %s" % invasion_fleet_mission troop_base_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [AIFleetMissionType.FLEET_MISSION_ORBITAL_INVASION]) if troop_base_fleet_missions: print "Invasion Base targets (must have been interrupted by combat): " else: print "Invasion Base targets: None (as expected, due to expected timing of order submission and execution)" for invasion_fleet_mission in troop_base_fleet_missions: print " %s" % invasion_fleet_mission military_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [AIFleetMissionType.FLEET_MISSION_MILITARY]) if military_fleet_missions: print "General Military targets: " else: print "General Military targets: None" for military_fleet_mission in military_fleet_missions: print " %s" % military_fleet_mission secure_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [AIFleetMissionType.FLEET_MISSION_SECURE]) if secure_fleet_missions: print "Secure targets: " else: print "Secure targets: None" for secure_fleet_mission in secure_fleet_missions: print " %s" % secure_fleet_mission orb_defense_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types( [AIFleetMissionType.FLEET_MISSION_ORBITAL_DEFENSE]) if orb_defense_fleet_missions: print "Orbital Defense targets: " else: print "Orbital Defense targets: None" for orb_defence_fleet_mission in orb_defense_fleet_missions: print " %s" % orb_defence_fleet_mission fleet_missions = foAI.foAIstate.get_all_fleet_missions() for mission in fleet_missions: mission.generate_fleet_orders()
def __print_candidate_table(candidates, mission, show_detail=False): universe = fo.getUniverse() if mission == "Colonization": first_column = Text("Score/Species") def get_first_column_value(x): return round(x[0], 2), x[1] elif mission == "Outposts": first_column = Number("Score") get_first_column_value = itemgetter(0) else: warning("__print_candidate_table(%s, %s): Invalid mission type" % (candidates, mission)) return columns = [first_column, Text("Planet"), Text("System"), Sequence("Specials")] if show_detail: columns.append(Sequence("Detail")) candidate_table = Table(*columns, table_name="Potential Targets for %s in Turn %d" % (mission, fo.currentTurn())) for planet_id, score_tuple in candidates: if score_tuple[0] > 0.5: planet = universe.getPlanet(planet_id) entries = [ get_first_column_value(score_tuple), planet, universe.getSystem(planet.systemID), planet.specials, ] if show_detail: entries.append(score_tuple[-1]) candidate_table.add_row(*entries) candidate_table.print_table(info)
def refresh(self): """Turn start AIstate cleanup/refresh.""" universe = fo.getUniverse() # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats fleetsLostBySystem.clear() invasionTargets[:] = [] exploration_center = PlanetUtilsAI.get_capital_sys_id() if exploration_center == INVALID_ID: # a bad state probably from an old savegame, or else empire has lost (or almost has) exploration_center = self.__origin_home_system_id # check if planets in cache is still present. Remove destroyed. for system_id, info in sorted(self.systemStatus.items()): self.systemStatus[system_id][ 'enemy_ship_count'] = 0 # clear now in prep for update_system_status() planet_dict = info.get('planets', {}) cache_planet_set = set(planet_dict) system_planet_set = set( *(sys.planetIDs for sys in [universe.getSystem(system_id)] if sys)) diff = cache_planet_set - system_planet_set if diff: print "Removing destroyed planets from systemStatus for system %s: planets to be removed: %s" % ( system_id, sorted(diff)) for key in diff: del planet_dict[key] ExplorationAI.graphFlags.clear() if fo.currentTurn() < 50: print "-------------------------------------------------" print "Border Exploration Update (relative to %s)" % ( PlanetUtilsAI.sys_name_ids([exploration_center, INVALID_ID ])[0]) print "-------------------------------------------------" if self.visBorderSystemIDs.keys() == [INVALID_ID]: self.visBorderSystemIDs.clear() self.visBorderSystemIDs[exploration_center] = 1 for sys_id in self.visBorderSystemIDs.keys( ): # This dict modified during iteration. if fo.currentTurn() < 50: print "Considering border system %s" % ( PlanetUtilsAI.sys_name_ids([sys_id, INVALID_ID])[0]) ExplorationAI.follow_vis_system_connections( sys_id, exploration_center) newly_explored = ExplorationAI.update_explored_systems() nametags = [] for sys_id in newly_explored: newsys = universe.getSystem(sys_id) nametags.append( "ID:%4d -- %-20s" % (sys_id, (newsys and newsys.name) or "name unknown") ) # an explored system *should* always be able to be gotten if newly_explored: print "-------------------------------------------------" print "Newly explored systems:\n%s" % "\n".join(nametags) print "-------------------------------------------------" # cleanup fleet roles # self.update_fleet_locs() self.__clean_fleet_roles() self.__clean_fleet_missions(FleetUtilsAI.get_empire_fleet_ids()) print "Fleets lost by system: %s" % fleetsLostBySystem self.update_system_status()
def survey_universe(): colonization_timer.start("Categorizing Visible Planets") universe = fo.getUniverse() empire_id = fo.empireID() current_turn = fo.currentTurn() # set up / reset various variables; the 'if' is purely for code folding convenience if True: AIstate.empireStars.clear() empire_metabolisms.clear() active_growth_specials.clear() # var setup done aistate = get_aistate() for sys_id in universe.systemIDs: system = universe.getSystem(sys_id) if not system: continue local_ast = False local_gg = False empire_has_qualifying_planet = False if sys_id in AIstate.colonyTargetedSystemIDs: empire_has_qualifying_planet = True for pid in system.planetIDs: planet = universe.getPlanet(pid) if not planet: continue if pid in aistate.colonisablePlanetIDs: empire_has_qualifying_planet = True if planet.size == fo.planetSize.asteroids: local_ast = True elif planet.size == fo.planetSize.gasGiant: local_gg = True spec_name = planet.speciesName this_spec = fo.getSpecies(spec_name) owner_id = planet.owner planet_population = planet.currentMeterValue(fo.meterType.population) buildings_here = [universe.getBuilding(bldg).buildingTypeName for bldg in planet.buildingIDs] ship_facilities = set(AIDependencies.SHIP_FACILITIES).intersection(buildings_here) weapons_grade = "WEAPONS_0.0" if owner_id == empire_id: if planet_population > 0.0 and this_spec: empire_has_qualifying_planet = True for metab in [tag for tag in this_spec.tags if tag in AIDependencies.metabolismBoostMap]: empire_metabolisms[metab] = empire_metabolisms.get(metab, 0.0) + planet.habitableSize if this_spec.canProduceShips: pilot_val = rate_piloting_tag(spec_name) if spec_name == "SP_ACIREMA": pilot_val += 1 weapons_grade = "WEAPONS_%.1f" % pilot_val set_pilot_rating_for_planet(pid, pilot_val) yard_here = [] if "BLD_SHIPYARD_BASE" in buildings_here: set_ship_builders(spec_name, pid) yard_here = [pid] if this_spec.canColonize and planet.currentMeterValue(fo.meterType.targetPopulation) >= 3: set_colony_builders(spec_name, yard_here) set_building_locations(weapons_grade, ship_facilities, pid, sys_id) for special in planet.specials: if special_is_nest(special): set_have_nest() if special in AIDependencies.metabolismBoosts: set_growth_special(special, pid) if planet.focus == FocusType.FOCUS_GROWTH: active_growth_specials.setdefault(special, []).append(pid) elif owner_id != -1: if get_partial_visibility_turn(pid) >= current_turn - 1: # only interested in immediately recent data aistate.misc.setdefault("enemies_sighted", {}).setdefault(current_turn, []).append(pid) if empire_has_qualifying_planet: if local_ast: set_have_asteroids() elif local_gg: set_have_gas_giant() if sys_id in get_owned_planets(): AIstate.empireStars.setdefault(system.starType, []).append(sys_id) sys_status = aistate.systemStatus.setdefault(sys_id, {}) if sys_status.get("fleetThreat", 0) > 0: set_colonies_is_under_attack() if sys_status.get("neighborThreat", 0) > 0: set_colonies_is_under_treat() _print_empire_species_roster() rating_list = sorted(get_pilot_ratings().values(), reverse=True) summarize_pilot_ratings(rating_list) colonization_timer.stop()
def allotted_invasion_targets(): return min(1 + int(fo.currentTurn() // 50), 3)
def allotted_invasion_targets(): return 1 + int(fo.currentTurn() // 25)
def _calculate_colonisation_priority(): """Calculates the demand for colony ships by colonisable planets.""" global allottedColonyTargets aistate = get_aistate() enemies_sighted = aistate.misc.get('enemies_sighted', {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() total_pp = fo.getEmpire().productionPoints num_colonies = get_number_of_colonies() colony_growth_barrier = aistate.character.max_number_colonies() if num_colonies > colony_growth_barrier: return 0.0 colony_cost, turns_to_build = (ColonisationAI.colony_pod_cost_turns()) mil_prio = aistate.get_priority(PriorityType.PRODUCTION_MILITARY) allotted_portion = ([[[0.6, 0.8], [0.3, 0.4]], [[0.8, 0.9], [0.4, 0.6]]][galaxy_is_sparse][any(enemies_sighted)]) allotted_portion = aistate.character.preferred_colonization_portion( allotted_portion) # if ( get_aistate().get_priority(AIPriorityType.PRIORITY_PRODUCTION_COLONISATION) # > 2 * get_aistate().get_priority(AIPriorityType.PRIORITY_PRODUCTION_MILITARY)): # allotted_portion *= 1.5 if mil_prio < 100: allotted_portion *= 2 elif mil_prio < 200: allotted_portion *= 1.5 elif fo.currentTurn() > 100: allotted_portion *= 0.75**(num_colonies / 10.0) # allottedColonyTargets = 1+ int(fo.currentTurn()/50) allottedColonyTargets = 1 + int( total_pp * turns_to_build * allotted_portion / colony_cost) outpost_prio = aistate.get_priority(PriorityType.PRODUCTION_OUTPOST) # if have no SP_SLY, and have any outposts to build, don't build colony ships TODO: make more complex assessment colonizers = list(ColonisationAI.empire_colonizers) if "SP_SLY" not in colonizers and outpost_prio > 0: return 0.0 min_score = ColonisationAI.MINIMUM_COLONY_SCORE minimal_top = min_score + 2 # one more than the conditional floor set by ColonisationAI.revise_threat_factor() minimal_opportunities = [ species_name for (_, (score, species_name)) in aistate.colonisablePlanetIDs.items() if min_score < score <= minimal_top ] decent_opportunities = [ species_name for (_, (score, species_name)) in aistate.colonisablePlanetIDs.items() if score > minimal_top ] minimal_planet_factor = 0.2 # count them for something, but not much num_colonisable_planet_ids = len( decent_opportunities ) + minimal_planet_factor * len(minimal_opportunities) if num_colonisable_planet_ids == 0: return 1 colony_ship_ids = FleetUtilsAI.get_empire_fleet_ids_by_role( MissionType.COLONISATION) num_colony_ships = len( FleetUtilsAI.extract_fleet_ids_without_mission_types(colony_ship_ids)) colonisation_priority = 60 * (1.0 + num_colonisable_planet_ids - num_colony_ships) / ( num_colonisable_planet_ids + 1) if colonizers == ["SP_SLY"]: colonisation_priority *= 2 elif "SP_SLY" in colonizers: colony_opportunities = minimal_opportunities + decent_opportunities colonisation_priority *= (1.0 + colony_opportunities.count("SP_SLY") ) / len(colony_opportunities) # print # print "Number of Colony Ships : " + str(num_colony_ships) # print "Number of Colonisable planets : " + str(num_colonisable_planet_ids) # print "Priority for colony ships : " + str(colonisation_priority) if colonisation_priority < 1: return 0 return colonisation_priority
def target_number_of_orbitals(self): aggression_index = max(1, self.aggression) return min( int(((fo.currentTurn() + 4) / (8.0 * aggression_index**1.5))**0.8), fo.aggression.maniacal - aggression_index)
def generate_fleet_orders_for_fleet_missions(): """Generates fleet orders from targets.""" print("Generating fleet orders") # The following fleet lists are based on *Roles* -- Secure type missions are done by fleets with Military Roles debug("Fleets by Role\n") debug("Exploration Fleets: %s" % get_empire_fleet_ids_by_role(MissionType.EXPLORATION)) debug("Colonization Fleets: %s" % get_empire_fleet_ids_by_role(MissionType.COLONISATION)) debug("Outpost Fleets: %s" % get_empire_fleet_ids_by_role(MissionType.OUTPOST)) debug("Invasion Fleets: %s" % get_empire_fleet_ids_by_role(MissionType.INVASION)) debug("Military Fleets: %s" % get_empire_fleet_ids_by_role(MissionType.MILITARY)) debug("Orbital Defense Fleets: %s" % get_empire_fleet_ids_by_role(MissionType.ORBITAL_DEFENSE)) debug("Outpost Base Fleets: %s" % get_empire_fleet_ids_by_role(MissionType.ORBITAL_OUTPOST)) debug("Invasion Base Fleets: %s" % get_empire_fleet_ids_by_role(MissionType.ORBITAL_INVASION)) debug( "Securing Fleets: %s (currently FLEET_MISSION_MILITARY should be used instead of this Role)" % (get_empire_fleet_ids_by_role(MissionType.SECURE))) aistate = get_aistate() if fo.currentTurn() < 50: debug('') debug("Explored systems:") _print_systems_and_supply(aistate.get_explored_system_ids()) debug("Unexplored systems:") _print_systems_and_supply(aistate.get_unexplored_system_ids()) debug('') exploration_fleet_missions = aistate.get_fleet_missions_with_any_mission_types( [MissionType.EXPLORATION]) if exploration_fleet_missions: debug("Exploration targets:") for explorationAIFleetMission in exploration_fleet_missions: debug(" - %s" % explorationAIFleetMission) else: debug("Exploration targets: None") colonisation_fleet_missions = aistate.get_fleet_missions_with_any_mission_types( [MissionType.COLONISATION]) if colonisation_fleet_missions: debug("Colonization targets: ") else: debug("Colonization targets: None") for colonisation_fleet_mission in colonisation_fleet_missions: debug(" %s" % colonisation_fleet_mission) outpost_fleet_missions = aistate.get_fleet_missions_with_any_mission_types( [MissionType.OUTPOST]) if outpost_fleet_missions: debug("Outpost targets: ") else: debug("Outpost targets: None") for outpost_fleet_mission in outpost_fleet_missions: debug(" %s" % outpost_fleet_mission) outpost_base_fleet_missions = aistate.get_fleet_missions_with_any_mission_types( [MissionType.ORBITAL_OUTPOST]) if outpost_base_fleet_missions: debug("Outpost Base targets (must have been interrupted by combat): ") else: debug( "Outpost Base targets: None (as expected, due to expected timing of order submission and execution)" ) for outpost_fleet_mission in outpost_base_fleet_missions: debug(" %s" % outpost_fleet_mission) invasion_fleet_missions = aistate.get_fleet_missions_with_any_mission_types( [MissionType.INVASION]) if invasion_fleet_missions: debug("Invasion targets: ") else: debug("Invasion targets: None") for invasion_fleet_mission in invasion_fleet_missions: debug(" %s" % invasion_fleet_mission) troop_base_fleet_missions = aistate.get_fleet_missions_with_any_mission_types( [MissionType.ORBITAL_INVASION]) if troop_base_fleet_missions: debug("Invasion Base targets (must have been interrupted by combat): ") else: debug( "Invasion Base targets: None (as expected, due to expected timing of order submission and execution)" ) for invasion_fleet_mission in troop_base_fleet_missions: debug(" %s" % invasion_fleet_mission) military_fleet_missions = aistate.get_fleet_missions_with_any_mission_types( [MissionType.MILITARY]) if military_fleet_missions: debug("General Military targets: ") else: debug("General Military targets: None") for military_fleet_mission in military_fleet_missions: debug(" %s" % military_fleet_mission) secure_fleet_missions = aistate.get_fleet_missions_with_any_mission_types( [MissionType.SECURE]) if secure_fleet_missions: debug("Secure targets: ") else: debug("Secure targets: None") for secure_fleet_mission in secure_fleet_missions: debug(" %s" % secure_fleet_mission) orb_defense_fleet_missions = aistate.get_fleet_missions_with_any_mission_types( [MissionType.ORBITAL_DEFENSE]) if orb_defense_fleet_missions: debug("Orbital Defense targets: ") else: debug("Orbital Defense targets: None") for orb_defence_fleet_mission in orb_defense_fleet_missions: debug(" %s" % orb_defence_fleet_mission) fleet_missions = aistate.get_all_fleet_missions() destroyed_objects = fo.getUniverse().destroyedObjectIDs(fo.empireID()) # merge fleets where appropriate before generating fleet orders. # This allows us to consider the full fleet strength when determining # e.g. whether to engage or avoid an enemy. for mission in fleet_missions: fleet_id = mission.fleet.id fleet = mission.fleet.get_object() if not fleet or not fleet.shipIDs or fleet_id in destroyed_objects: continue mission.check_mergers() # get new set of fleet missions without fleets that are empty after merge fleet_missions = aistate.get_all_fleet_missions() for mission in fleet_missions: mission.generate_fleet_orders()