def calculatePriorities(): "calculates the priorities of the AI player" print("calculating priorities") foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESOURCE_RESEARCH, calculateResearchPriority()) ColonisationAI.getColonyFleets() # sets foAI.foAIstate.colonisablePlanetIDs and foAI.foAIstate.outpostPlanetIDs InvasionAI.getInvasionFleets() # sets AIstate.invasionFleetIDs, AIstate.opponentPlanetIDs, and AIstate.invasionTargetedPlanetIDs MilitaryAI.getMilitaryFleets() # sets AIstate.militaryFleetIDs and AIstate.militaryTargetedSystemIDs foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESOURCE_PRODUCTION, calculateIndustryPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESOURCE_TRADE, 0) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESOURCE_CONSTRUCTION, 0) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_PRODUCTION_EXPLORATION, calculateExplorationPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_PRODUCTION_OUTPOST, calculateOutpostPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_PRODUCTION_COLONISATION, calculateColonisationPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_PRODUCTION_INVASION, calculateInvasionPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_PRODUCTION_MILITARY, calculateMilitaryPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_PRODUCTION_BUILDINGS, 25) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESEARCH_LEARNING, calculateLearningPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESEARCH_GROWTH, calculateGrowthPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESEARCH_PRODUCTION, calculateTechsProductionPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESEARCH_CONSTRUCTION, calculateConstructionPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESEARCH_ECONOMICS, 0) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESEARCH_SHIPS, calculateShipsPriority()) foAI.foAIstate.setPriority(AIPriorityType.PRIORITY_RESEARCH_DEFENSE, 0)
def generateOrders(): print ("Genearting Orders") universe = fo.getUniverse() empire = fo.getEmpire() planetID = empire.capitalID planet = universe.getPlanet(planetID) print "EmpireID: " + str(empire.empireID) + " Name: " + empire.name + " Turn: " + str(fo.currentTurn()) print "CapitalID: " + str(planetID) + " Name: " + planet.name + " Species: " + planet.speciesName # turn cleanup splitFleet() identifyShipDesigns() identifyFleetsRoles() foAIstate.clean(ExplorationAI.getHomeSystemID(), FleetUtilsAI.getEmpireFleetIDs()) # ...missions # ...demands/priorities print("Calling AI Modules") # call AI modules PriorityAI.calculatePriorities() ExplorationAI.assignScoutsToExploreSystems() ColonisationAI.assignColonyFleetsToColonise() InvasionAI.assignInvasionFleetsToInvade() MilitaryAI.assignMilitaryFleetsToSystems() FleetUtilsAI.generateAIFleetOrdersForAIFleetMissions() FleetUtilsAI.issueAIFleetOrdersForAIFleetMissions() ResearchAI.generateResearchOrders() ProductionAI.generateProductionOrders() ResourcesAI.generateResourcesOrders() foAIstate.afterTurnCleanup() fo.doneTurn()
def trooper_move_reqs_met(main_fleet_mission, order, verbose): """ Indicates whether or not move requirements specific to invasion troopers are met for the provided mission and order. :type main_fleet_mission: AIFleetMission.AIFleetMission :type order: OrderMove :param verbose: whether to print verbose decision details :type verbose: bool :rtype: bool """ # Don't advance outside of our fleet-supply zone unless the target either has no shields at all or there # is already a military fleet assigned to secure the target, and don't take final jump unless the planet is # (to the AI's knowledge) down to zero shields. Additional checks will also be done by the later # generic movement code invasion_target = main_fleet_mission.target invasion_planet = invasion_target.get_object() invasion_system = invasion_target.get_system() supplied_systems = fo.getEmpire().fleetSupplyableSystemIDs # if about to leave supply lines if order.target.id not in supplied_systems or fo.getUniverse( ).jumpDistance(order.fleet.id, invasion_system.id) < 5: if invasion_planet.currentMeterValue(fo.meterType.maxShield): military_support_fleets = MilitaryAI.get_military_fleets_with_target_system( invasion_system.id) if not military_support_fleets: if verbose: debug( "trooper_move_reqs_met() holding Invasion fleet %d before leaving supply " "because target (%s) has nonzero max shields and there is not yet a military fleet " "assigned to secure the target system." % (order.fleet.id, invasion_planet)) return False # if there is a threat in the enemy system, do give military ships at least 1 turn to clear it delay_to_move_troops = 1 if MilitaryAI.get_system_local_threat( order.target.id) else 0 def eta(fleet_id): return FleetUtilsAI.calculate_estimated_time_of_arrival( fleet_id, invasion_system.id) eta_this_fleet = eta(order.fleet.id) if all(((eta_this_fleet - delay_to_move_troops) <= eta(fid) and eta(fid)) for fid in military_support_fleets): if verbose: debug( "trooper_move_reqs_met() holding Invasion fleet %d before leaving supply " "because target (%s) has nonzero max shields and no assigned military fleet would arrive" "at least %d turn earlier than the invasion fleet" % (order.fleet.id, invasion_planet, delay_to_move_troops)) return False if verbose: debug( "trooper_move_reqs_met() allowing Invasion fleet %d to leave supply " "because target (%s) has zero max shields or there is a military fleet assigned to secure " "the target system which will arrive at least 1 turn before the invasion fleet.", order.fleet.id, invasion_planet) return True
def _portion_of_fleet_needed_here(self): """Calculate the portion of the fleet needed in target system considering enemy forces.""" # TODO check rating against planets if assertion_fails(self.type in COMBAT_MISSION_TYPES, msg=str(self)): return 0 if assertion_fails(self.target and self.target.id != INVALID_ID, msg=str(self)): return 0 system_id = self.target.id aistate = get_aistate() local_defenses = MilitaryAI.get_my_defense_rating_in_system(system_id) potential_threat = CombatRatingsAI.combine_ratings( MilitaryAI.get_system_local_threat(system_id), MilitaryAI.get_system_neighbor_threat(system_id) ) universe = fo.getUniverse() system = universe.getSystem(system_id) # tally planetary defenses total_defense = total_shields = 0 for planet_id in system.planetIDs: planet = universe.getPlanet(planet_id) total_defense += planet.currentMeterValue(fo.meterType.defense) total_shields += planet.currentMeterValue(fo.meterType.shield) planetary_ratings = total_defense * (total_shields + total_defense) potential_threat += planetary_ratings # TODO: rewrite to return min rating vs planets as well # consider safety factor just once here rather than everywhere below safety_factor = aistate.character.military_safety_factor() potential_threat *= safety_factor fleet_rating = CombatRatingsAI.get_fleet_rating(self.fleet.id) return CombatRatingsAI.rating_needed(potential_threat, local_defenses) / float(fleet_rating)
def calculatePriorities(): "calculates the priorities of the AI player" print("checking statuses") # Industry, Research, Colony, Invasion, Military foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_PRODUCTION, 50) # let this one stay fixed & just adjust Research foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_RESEARCH, calculateResearchPriority()) ColonisationAI.getColonyFleets() # sets foAI.foAIstate.colonisablePlanetIDs and foAI.foAIstate.outpostPlanetIDs and many other values used by other modules InvasionAI.getInvasionFleets() # sets AIstate.invasionFleetIDs, AIstate.opponentPlanetIDs, and AIstate.invasionTargetedPlanetIDs MilitaryAI.getMilitaryFleets() # sets AIstate.militaryFleetIDs and AIstate.militaryTargetedSystemIDs print("calculating priorities") calculateIndustryPriority()#purely for reporting purposes foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_TRADE, 0) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_CONSTRUCTION, 0) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_EXPLORATION, calculateExplorationPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_OUTPOST, calculateOutpostPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_COLONISATION, calculateColonisationPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_INVASION, calculateInvasionPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY, calculateMilitaryPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_BUILDINGS, 25) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_LEARNING, calculateLearningPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_GROWTH, calculateGrowthPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_PRODUCTION, calculateTechsProductionPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_CONSTRUCTION, calculateConstructionPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_ECONOMICS, 0) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_SHIPS, calculateShipsPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_DEFENSE, 0)
def calculate_priorities(): """Calculates the priorities of the AI player.""" debug("\n=== Preparing to Calculate Priorities ===") prioritiees_timer.start("setting Production Priority") aistate = get_aistate() # let this one stay fixed & just adjust Research and Influence aistate.set_priority(PriorityType.RESOURCE_PRODUCTION, 50) # RESOURCE_INFLUENCE priority is calculated in PolicyAI debug("\n*** Calculating Research Priority ***\n") prioritiees_timer.start("setting Research Priority") aistate.set_priority( PriorityType.RESOURCE_RESEARCH, _calculate_research_priority()) # TODO: do univ _survey before this debug("\n*** Updating Colonization Status ***\n") prioritiees_timer.start("Evaluating Colonization Status") ColonisationAI.get_colony_fleets( ) # sets aistate.colonisablePlanetIDs and many other values used by other modules debug("\n*** Updating Invasion Status ***\n") prioritiees_timer.start("Evaluating Invasion Status") InvasionAI.get_invasion_fleets( ) # sets AIstate.opponentPlanetIDs, and AIstate.invasionTargetedPlanetIDs debug("\n*** Updating Military Status ***\n") prioritiees_timer.start("Evaluating Military Status") MilitaryAI.get_military_fleets() debug("\n** Calculating Production Priorities ***\n") prioritiees_timer.start("reporting Production Priority") _calculate_industry_priority() # purely for reporting purposes prioritiees_timer.start("setting Exploration Priority") aistate.set_priority(PriorityType.RESOURCE_TRADE, 0) aistate.set_priority(PriorityType.RESOURCE_CONSTRUCTION, 0) aistate.set_priority(PriorityType.PRODUCTION_EXPLORATION, _calculate_exploration_priority()) prioritiees_timer.start("setting Colony Priority") aistate.set_priority(PriorityType.PRODUCTION_COLONISATION, _calculate_colonisation_priority()) prioritiees_timer.start("setting Outpost Priority") aistate.set_priority(PriorityType.PRODUCTION_OUTPOST, _calculate_outpost_priority()) prioritiees_timer.start("setting Invasion Priority") aistate.set_priority(PriorityType.PRODUCTION_INVASION, _calculate_invasion_priority()) prioritiees_timer.start("setting Military Priority") aistate.set_priority(PriorityType.PRODUCTION_MILITARY, _calculate_military_priority()) prioritiees_timer.start("setting other priorities") aistate.set_priority(PriorityType.PRODUCTION_BUILDINGS, 25) prioritiees_timer.stop_print_and_clear()
def calculate_priorities(): """Calculates the priorities of the AI player.""" print "\n", 10 * "=", "Preparing to Calculate Priorities", 10 * "=" prioritiees_timer.start('setting Production Priority') foAI.foAIstate.set_priority(PriorityType.RESOURCE_PRODUCTION, 50) # let this one stay fixed & just adjust Research print "\n*** Calculating Research Priority ***\n" prioritiees_timer.start('setting Research Priority') foAI.foAIstate.set_priority(PriorityType.RESOURCE_RESEARCH, _calculate_research_priority()) # TODO: do univ _survey before this print "\n*** Updating Colonization Status ***\n" prioritiees_timer.start('Evaluating Colonization Status') ColonisationAI.get_colony_fleets() # sets foAI.foAIstate.colonisablePlanetIDs and foAI.foAIstate.outpostPlanetIDs and many other values used by other modules print "\n*** Updating Invasion Status ***\n" prioritiees_timer.start('Evaluating Invasion Status') InvasionAI.get_invasion_fleets() # sets AIstate.invasionFleetIDs, AIstate.opponentPlanetIDs, and AIstate.invasionTargetedPlanetIDs print "\n*** Updating Military Status ***\n" prioritiees_timer.start('Evaluating Military Status') MilitaryAI.get_military_fleets() print("\n** Calculating Production Priorities ***\n") prioritiees_timer.start('reporting Production Priority') _calculate_industry_priority() # purely for reporting purposes prioritiees_timer.start('setting Exploration Priority') foAI.foAIstate.set_priority(PriorityType.RESOURCE_TRADE, 0) foAI.foAIstate.set_priority(PriorityType.RESOURCE_CONSTRUCTION, 0) foAI.foAIstate.set_priority(PriorityType.PRODUCTION_EXPLORATION, _calculate_exploration_priority()) prioritiees_timer.start('setting Colony Priority') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_COLONISATION, _calculate_colonisation_priority()) prioritiees_timer.start('setting Outpost Priority') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_OUTPOST, _calculate_outpost_priority()) prioritiees_timer.start('setting Invasion Priority') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_INVASION, _calculate_invasion_priority()) prioritiees_timer.start('setting Military Priority') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_MILITARY, _calculate_military_priority()) prioritiees_timer.start('setting other priorities') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_BUILDINGS, 25) foAI.foAIstate.set_priority(PriorityType.RESEARCH_LEARNING, _calculate_learning_priority()) foAI.foAIstate.set_priority(PriorityType.RESEARCH_GROWTH, _calculate_growth_priority()) foAI.foAIstate.set_priority(PriorityType.RESEARCH_PRODUCTION, _calculate_techs_production_priority()) foAI.foAIstate.set_priority(PriorityType.RESEARCH_CONSTRUCTION, _calculate_construction_priority()) foAI.foAIstate.set_priority(PriorityType.RESEARCH_ECONOMICS, 0) foAI.foAIstate.set_priority(PriorityType.RESEARCH_SHIPS, _calculate_ships_priority()) foAI.foAIstate.set_priority(PriorityType.RESEARCH_DEFENSE, 0) prioritiees_timer.stop_print_and_clear()
def trooper_move_reqs_met(main_fleet_mission, order, verbose): """ Indicates whether or not move requirements specific to invasion troopers are met for the provided mission and order. :type main_fleet_mission: AIFleetMission.AIFleetMission :type order: OrderMove :param verbose: whether to print verbose decision details :type verbose: bool :rtype: bool """ # Don't advance outside of our fleet-supply zone unless the target either has no shields at all or there # is already a military fleet assigned to secure the target, and don't take final jump unless the planet is # (to the AI's knowledge) down to zero shields. Additional checks will also be done by the later # generic movement code invasion_target = main_fleet_mission.target invasion_planet = invasion_target.get_object() invasion_system = invasion_target.get_system() supplied_systems = fo.getEmpire().fleetSupplyableSystemIDs # if about to leave supply lines if order.target.id not in supplied_systems or fo.getUniverse().jumpDistance(order.fleet.id, invasion_system.id) < 5: if invasion_planet.currentMeterValue(fo.meterType.maxShield): military_support_fleets = MilitaryAI.get_military_fleets_with_target_system(invasion_system.id) if not military_support_fleets: if verbose: print ("trooper_move_reqs_met() holding Invasion fleet %d before leaving supply " "because target (%s) has nonzero max shields and there is not yet a military fleet " "assigned to secure the target system.") % (order.fleet.id, invasion_planet) return False # if there is a threat in the enemy system, do give military ships at least 1 turn to clear it delay_to_move_troops = 1 if MilitaryAI.get_system_local_threat(order.target.id) else 0 def eta(fleet_id): return FleetUtilsAI.calculate_estimated_time_of_arrival(fleet_id, invasion_system.id) eta_this_fleet = eta(order.fleet.id) if all(((eta_this_fleet - delay_to_move_troops) <= eta(fid) and eta(fid)) for fid in military_support_fleets): if verbose: print ("trooper_move_reqs_met() holding Invasion fleet %d before leaving supply " "because target (%s) has nonzero max shields and no assigned military fleet would arrive" "at least %d turn earlier than the invasion fleet") % ( order.fleet.id, invasion_planet, delay_to_move_troops) return False if verbose: print ("trooper_move_reqs_met() allowing Invasion fleet %d to leave supply " "because target (%s) has zero max shields or there is a military fleet assigned to secure " "the target system which will arrive at least 1 turn before the invasion fleet.") % (order.fleet.id, invasion_planet) return True
def calculate_priorities(): """calculates the priorities of the AI player""" print ("checking statuses") # Industry, Research, Colony, Invasion, Military prioritiees_timer.start("setting Production Priority") foAI.foAIstate.set_priority( EnumsAI.AIPriorityType.PRIORITY_RESOURCE_PRODUCTION, 50 ) # let this one stay fixed & just adjust Research prioritiees_timer.start("setting Research Priority") foAI.foAIstate.set_priority( EnumsAI.AIPriorityType.PRIORITY_RESOURCE_RESEARCH, calculateResearchPriority() ) # TODO: do univ _survey before this prioritiees_timer.start("Evaluating Colonization Status") ColonisationAI.get_colony_fleets() # sets foAI.foAIstate.colonisablePlanetIDs and foAI.foAIstate.outpostPlanetIDs and many other values used by other modules prioritiees_timer.start("Evaluating Invasion Status") InvasionAI.get_invasion_fleets() # sets AIstate.invasionFleetIDs, AIstate.opponentPlanetIDs, and AIstate.invasionTargetedPlanetIDs prioritiees_timer.start("Evaluating Military Status") MilitaryAI.get_military_fleets() # sets AIstate.militaryFleetIDs and AIstate.militaryTargetedSystemIDs prioritiees_timer.start("reporting Production Priority") print ("calculating priorities") calculateIndustryPriority() # purely for reporting purposes prioritiees_timer.start("setting Exploration Priority") foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_TRADE, 0) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_CONSTRUCTION, 0) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_EXPLORATION, calculateExplorationPriority()) prioritiees_timer.start("setting Colony Priority") foAI.foAIstate.set_priority( EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_COLONISATION, calculateColonisationPriority() ) prioritiees_timer.start("setting Outpost Priority") foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_OUTPOST, calculateOutpostPriority()) prioritiees_timer.start("setting Invasion Priority") foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_INVASION, calculateInvasionPriority()) prioritiees_timer.start("setting Military Priority") foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY, calculateMilitaryPriority()) prioritiees_timer.start("setting other priorities") foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_BUILDINGS, 25) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_LEARNING, calculateLearningPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_GROWTH, calculateGrowthPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_PRODUCTION, calculateTechsProductionPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_CONSTRUCTION, calculateConstructionPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_ECONOMICS, 0) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_SHIPS, calculateShipsPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_DEFENSE, 0) prioritiees_timer.end()
def _need_repair(self): """ Check if fleet need repair. Check if fleet HP is less of cutoff. TODO make more clever implementation. """ repair_limit = 0.70 universe = fo.getUniverse() fleet_id = self.target_id # if combat fleet, use military repair check if foAI.foAIstate.get_fleet_role(fleet_id) in COMBAT_MISSION_TYPES: return fleet_id in MilitaryAI.avail_mil_needing_repair([fleet_id], False, True)[0] fleet = universe.getFleet(fleet_id) ships_cur_health = 0 ships_max_health = 0 for ship_id in fleet.shipIDs: this_ship = universe.getShip(ship_id) ships_cur_health += this_ship.currentMeterValue( fo.meterType.structure) ships_max_health += this_ship.currentMeterValue( fo.meterType.maxStructure) return ships_cur_health >= repair_limit * ships_max_health
def _need_repair(self, repair_limit=0.70): """Check if fleet needs to be repaired. If the fleet is already at a system where it can be repaired, stay there until fully repaired. Otherwise, repair if fleet health is below specified *repair_limit*. For military fleets, there is a special evaluation called, cf. *MilitaryAI.avail_mil_needing_repair()* :param repair_limit: percentage of health below which the fleet is sent to repair :type repair_limit: float :return: True if fleet needs repair :rtype: bool """ # TODO: More complex evaluation if fleet needs repair (consider self-repair, distance, threat, mission...) fleet_id = self.fleet.id # if we are already at a system where we can repair, make sure we use it... system = self.fleet.get_system() # TODO starlane obstruction is not considered in the next call nearest_dock = MoveUtilsAI.get_best_drydock_system_id(system.id, fleet_id) if nearest_dock == system.id: repair_limit = 0.99 # if combat fleet, use military repair check if get_aistate().get_fleet_role(fleet_id) in COMBAT_MISSION_TYPES: return fleet_id in MilitaryAI.avail_mil_needing_repair([fleet_id], on_mission=bool(self.orders), repair_limit=repair_limit)[0] # TODO: Allow to split fleet to send only damaged ships to repair ships_cur_health, ships_max_health = FleetUtilsAI.get_current_and_max_structure(fleet_id) return ships_cur_health < repair_limit * ships_max_health
def can_issue_order(self, verbose=False): if not super(OrderMove, self).can_issue_order(verbose=verbose): return False # TODO: figure out better way to have invasions (& possibly colonizations) require visibility on target without needing visibility of all intermediate systems # if False and main_mission_type not in [AIFleetMissionType.FLEET_MISSION_ATTACK, # TODO: consider this later # AIFleetMissionType.FLEET_MISSION_MILITARY, # AIFleetMissionType.FLEET_MISSION_SECURE, # AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, # AIFleetMissionType.FLEET_MISSION_EXPLORATION]: # if not universe.getVisibility(target_id, foAI.foAIstate.empireID) >= fo.visibility.partial: # #if not target_id in interior systems # foAI.foAIstate.needsEmergencyExploration.append(fleet.systemID) # return False system_id = self.fleet.get_system().id if system_id == self.target.get_system().id: return True # TODO: already there, but could consider retreating fleet_rating = foAI.foAIstate.get_rating(self.fleet.id).get('overall', 0) target_sys_status = foAI.foAIstate.systemStatus.get(self.target.id, {}) f_threat = target_sys_status.get('fleetThreat', 0) m_threat = target_sys_status.get('monsterThreat', 0) p_threat = target_sys_status.get('planetThreat', 0) threat = f_threat + m_threat + p_threat safety_factor = MilitaryAI.get_safety_factor() universe = fo.getUniverse() if fleet_rating >= safety_factor * threat: return True elif not p_threat and self.target.id in fo.getEmpire().supplyUnobstructedSystems: return True else: sys1 = universe.getSystem(system_id) sys1_name = sys1 and sys1.name or "unknown" targ1 = self.target.get_system() targ1_name = (targ1 and targ1.get_object().name) or "unknown" # following line was poor because AIstate.militaryFleetIDs only covers fleets without current missions # my_other_fleet_rating = sum([foAI.foAIstate.fleetStatus.get(fleet_id, {}).get('rating', 0) for fleet_id in foAI.foAIstate.militaryFleetIDs if ( foAI.foAIstate.fleetStatus.get(fleet_id, {}).get('sysID', -1) == thisSystemID ) ]) # myOtherFleetsRatings = [foAI.foAIstate.fleetStatus.get(fid, {}).get('rating', {}) for fid in foAI.foAIstate.systemStatus.get(target_id, {}).get('myfleets', [])] # my_other_fleet_rating = sum([foAI.foAIstate.fleetStatus.get(fid, {}).get('rating', 0) for fid in foAI.foAIstate.systemStatus.get( target_id, {}).get('myfleets', []) ]) my_other_fleet_rating = foAI.foAIstate.systemStatus.get(self.target.id, {}).get('myFleetRating', 0) # TODO: adjust calc for any departing fleets is_military = foAI.foAIstate.get_fleet_role(self.fleet.id) == AIFleetMissionType.FLEET_MISSION_MILITARY if ((my_other_fleet_rating > 3 * safety_factor * threat) or (is_military and my_other_fleet_rating + fleet_rating > safety_factor * threat) or (is_military and my_other_fleet_rating + fleet_rating > 0.8 * safety_factor * threat and fleet_rating > 0.2 * threat)): if verbose: print "\tAdvancing fleet %d (rating %d) at system %d (%s) into system %d (%s) with threat %d because of sufficient empire fleet strength already at destination" % ( self.fleet.id, fleet_rating, system_id, sys1_name, self.target.id, targ1_name, threat) return True elif threat == p_threat and not self.fleet.get_object().aggressive and not my_other_fleet_rating and not target_sys_status.get('localEnemyFleetIDs', [-1]): if verbose: print ("\tAdvancing fleet %d (rating %d) at system %d (%s) into system %d (%s) with planet threat %d because nonaggressive" + " and no other fleets present to trigger combat") % (self.fleet.id, fleet_rating, system_id, sys1_name, self.target.id, targ1_name, threat) return True else: if verbose: print "\tHolding fleet %d (rating %d) at system %d (%s) before travelling to system %d (%s) with threat %d" % (self.fleet.id, fleet_rating, system_id, sys1_name, self.target.id, targ1_name, threat) needs_vis = foAI.foAIstate.misc.setdefault('needs_vis', []) if self.target.id not in needs_vis: needs_vis.append(self.target.id) return False return True
def _revise_threat_factor( threat_factor: float, planet_value: float, system_id: int, min_planet_value: float = MINIMUM_COLONY_SCORE, ) -> float: """ Check if the threat_factor should be made less severe. If the AI does have enough total military to secure this system, and the target has more than minimal value, don't let the threat_factor discount the adjusted value below min_planet_value +1, so that if there are no other targets the AI could still pursue this one. Otherwise, scoring pressure from MilitaryAI.get_preferred_max_military_portion_for_single_battle might prevent the AI from pursuing a heavily defended but still obtainable target even if it has no softer locations available. :param threat_factor: the base threat factor :param planet_value: the planet score :param system_id: the system ID of subject planet :param min_planet_value: a floor planet value if the AI has enough military to secure the system :return: the (potentially) revised threat_factor """ # the default value below for fleetThreat shouldn't come in to play, but is just to be absolutely sure we don't # send colony ships into some system for which we have not evaluated fleetThreat system_status = get_aistate().systemStatus.get(system_id, {}) system_fleet_treat = system_status.get("fleetThreat", 1000) # TODO: consider taking area threat into account here. Arguments can be made both ways, see discussion in PR2069 sys_total_threat = system_fleet_treat + system_status.get( "monsterThreat", 0) + system_status.get("planetThreat", 0) if (MilitaryAI.get_concentrated_tot_mil_rating() > sys_total_threat) and (planet_value > 2 * min_planet_value): threat_factor = max(threat_factor, (min_planet_value + 1) / planet_value) return threat_factor
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 get_fleet_role(self, fleet_id, force_new=False): """Returns fleet role by ID.""" if not force_new and fleet_id in self.__fleetRoleByID: return self.__fleetRoleByID[fleet_id] else: role = FleetUtilsAI.assess_fleet_role(fleet_id) self.__fleetRoleByID[fleet_id] = role make_aggressive = False if role in [MissionType.COLONISATION, MissionType.OUTPOST, MissionType.ORBITAL_INVASION, MissionType.ORBITAL_OUTPOST ]: pass elif role in [MissionType.EXPLORATION, MissionType.INVASION ]: this_rating = self.get_rating(fleet_id) # Done! n_ships = self.fleetStatus.get(fleet_id, {}).get('nships', 1) # entry sould exist due to above line if float(this_rating) / n_ships >= 0.5 * MilitaryAI.cur_best_mil_ship_rating(): make_aggressive = True else: make_aggressive = True fo.issueAggressionOrder(fleet_id, make_aggressive) return role
def _calculate_exploration_priority(): """Calculates the demand for scouts by unexplored systems.""" empire = fo.getEmpire() num_unexplored_systems = len(ExplorationAI.border_unexplored_system_ids) 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 _need_repair(self, repair_limit=0.70): """Check if fleet needs to be repaired. If the fleet is already at a system where it can be repaired, stay there until fully repaired. Otherwise, repair if fleet health is below specified *repair_limit*. For military fleets, there is a special evaluation called, cf. *MilitaryAI.avail_mil_needing_repair()* :param repair_limit: percentage of health below which the fleet is sent to repair :type repair_limit: float :return: True if fleet needs repair :rtype: bool """ # TODO: More complex evaluation if fleet needs repair (consider self-repair, distance, threat, mission...) fleet_id = self.fleet.id # if we are already at a system where we can repair, make sure we use it... system = self.fleet.get_system() # TODO starlane obstruction is not considered in the next call nearest_dock = MoveUtilsAI.get_best_drydock_system_id( system.id, fleet_id) if nearest_dock == system.id: repair_limit = 0.99 # if combat fleet, use military repair check if get_aistate().get_fleet_role(fleet_id) in COMBAT_MISSION_TYPES: return fleet_id in MilitaryAI.avail_mil_needing_repair( [fleet_id], on_mission=bool(self.orders), repair_limit=repair_limit)[0] # TODO: Allow to split fleet to send only damaged ships to repair ships_cur_health, ships_max_health = FleetUtilsAI.get_current_and_max_structure( fleet_id) return ships_cur_health < repair_limit * ships_max_health
def _check_abort_mission(self, fleet_order: AIFleetOrder): """checks if current mission (targeting a planet) should be aborted""" planet_stealthed = False target_is_planet = fleet_order.target and isinstance( fleet_order.target, TargetPlanet) planet = None if target_is_planet: planet = fleet_order.target.get_object() # Check visibility prediction, but if somehow still have current visibility, don't # abort the mission yet if not EspionageAI.colony_detectable_by_empire( planet.id, empire=fo.empireID()): if get_partial_visibility_turn(planet.id) == fo.currentTurn(): debug( "EspionageAI predicts planet id %d to be stealthed" % planet.id + ", but somehow have current visibity anyway, so won't trigger mission abort" ) else: debug( "EspionageAI predicts we can no longer detect %s, will abort mission" % fleet_order.target) planet_stealthed = True if target_is_planet and not planet_stealthed: # TBD use fleet_order.is_valid or add fleet_order.should_abort if isinstance(fleet_order, OrderColonize): if planet.initialMeterValue(fo.meterType.population) == 0 and ( planet.ownedBy(fo.empireID()) or planet.unowned): return False elif isinstance(fleet_order, OrderOutpost): if planet.unowned: return False elif isinstance(fleet_order, OrderInvade): if planet.owner == fo.getEmpire().empireID: debug( f"Abandoning invasion for {planet}, it already belongs to us" ) return True if planet.currentMeterValue(fo.meterType.maxShield): military_support_fleets = MilitaryAI.get_military_fleets_with_target_system( planet.systemID) if not military_support_fleets: # Maybe try again later... debug( f"Abandoning Invasion mission for {planet} because target has nonzero max shields " f"and there is no military fleet assigned to secure the target system." ) return True return False else: return False # canceling fleet orders debug(" %s" % fleet_order) debug( "Fleet %d had a target planet that is no longer valid for this mission; aborting." % self.fleet.id) FleetUtilsAI.split_fleet(self.fleet.id) return True
def check_mergers(self, context=""): """ Merge local fleets with same mission into this fleet. :param context: Context of the function call for logging purposes :type context: str """ debug("Considering to merge %s", self.__str__()) if self.type not in MERGEABLE_MISSION_TYPES: debug("Mission type does not allow merging.") return if not self.target: debug("Mission has no valid target - do not merge.") return universe = fo.getUniverse() empire_id = fo.empireID() fleet_id = self.fleet.id main_fleet = universe.getFleet(fleet_id) main_fleet_system_id = main_fleet.systemID if main_fleet_system_id == INVALID_ID: debug("Can't merge: fleet in middle of starlane.") return # only merge PROTECT_REGION if there is any threat near target if self.type == MissionType.PROTECT_REGION: neighbor_systems = universe.getImmediateNeighbors( self.target.id, empire_id) if not any( MilitaryAI.get_system_local_threat(sys_id) for sys_id in neighbor_systems): debug("Not merging PROTECT_REGION fleet - no threat nearby.") return destroyed_list = set(universe.destroyedObjectIDs(empire_id)) aistate = get_aistate() system_status = aistate.systemStatus[main_fleet_system_id] other_fleets_here = [ fid for fid in system_status.get('myFleetsAccessible', []) if fid != fleet_id and fid not in destroyed_list and universe.getFleet(fid).ownedBy(empire_id) ] if not other_fleets_here: debug("No other fleets here") return for fid in other_fleets_here: fleet_mission = aistate.get_fleet_mission(fid) if fleet_mission.type != self.type or fleet_mission.target != self.target: debug("Local candidate %s does not have same mission." % fleet_mission) continue FleetUtilsAI.merge_fleet_a_into_b( fid, fleet_id, context="Order %s of mission %s" % (context, self))
def calculate_priorities(): """calculates the priorities of the AI player""" print("checking statuses") # Industry, Research, Colony, Invasion, Military prioritiees_timer.start('setting Production Priority') foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_PRODUCTION, 50) # let this one stay fixed & just adjust Research prioritiees_timer.start('setting Research Priority') foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_RESEARCH, calculateResearchPriority()) #TODO: do univ _survey before this prioritiees_timer.start('Evaluating Colonization Status') ColonisationAI.get_colony_fleets() # sets foAI.foAIstate.colonisablePlanetIDs and foAI.foAIstate.outpostPlanetIDs and many other values used by other modules prioritiees_timer.start('Evaluating Invasion Status') InvasionAI.get_invasion_fleets() # sets AIstate.invasionFleetIDs, AIstate.opponentPlanetIDs, and AIstate.invasionTargetedPlanetIDs prioritiees_timer.start('Evaluating Military Status') MilitaryAI.get_military_fleets() # sets AIstate.militaryFleetIDs and AIstate.militaryTargetedSystemIDs prioritiees_timer.start('reporting Production Priority') print("calculating priorities") calculateIndustryPriority()#purely for reporting purposes prioritiees_timer.start('setting Exploration Priority') foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_TRADE, 0) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_CONSTRUCTION, 0) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_EXPLORATION, calculateExplorationPriority()) prioritiees_timer.start('setting Colony Priority') foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_COLONISATION, calculateColonisationPriority()) prioritiees_timer.start('setting Outpost Priority') foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_OUTPOST, calculateOutpostPriority()) prioritiees_timer.start('setting Invasion Priority') foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_INVASION, calculateInvasionPriority()) prioritiees_timer.start('setting Military Priority') foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY, calculateMilitaryPriority()) prioritiees_timer.start('setting other priorities') foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_BUILDINGS, 25) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_LEARNING, calculateLearningPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_GROWTH, calculateGrowthPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_PRODUCTION, calculateTechsProductionPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_CONSTRUCTION, calculateConstructionPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_ECONOMICS, 0) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_SHIPS, calculateShipsPriority()) foAI.foAIstate.set_priority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_DEFENSE, 0) prioritiees_timer.end()
def calculate_priorities(): """Calculates the priorities of the AI player.""" print "\n", 10 * "=", "Preparing to Calculate Priorities", 10 * "=" prioritiees_timer.start('setting Production Priority') foAI.foAIstate.set_priority(PriorityType.RESOURCE_PRODUCTION, 50) # let this one stay fixed & just adjust Research print "\n*** Calculating Research Priority ***\n" prioritiees_timer.start('setting Research Priority') foAI.foAIstate.set_priority(PriorityType.RESOURCE_RESEARCH, _calculate_research_priority()) # TODO: do univ _survey before this print "\n*** Updating Colonization Status ***\n" prioritiees_timer.start('Evaluating Colonization Status') ColonisationAI.get_colony_fleets() # sets foAI.foAIstate.colonisablePlanetIDs and many other values used by other modules print "\n*** Updating Invasion Status ***\n" prioritiees_timer.start('Evaluating Invasion Status') InvasionAI.get_invasion_fleets() # sets AIstate.opponentPlanetIDs, and AIstate.invasionTargetedPlanetIDs print "\n*** Updating Military Status ***\n" prioritiees_timer.start('Evaluating Military Status') MilitaryAI.get_military_fleets() print("\n** Calculating Production Priorities ***\n") prioritiees_timer.start('reporting Production Priority') _calculate_industry_priority() # purely for reporting purposes prioritiees_timer.start('setting Exploration Priority') foAI.foAIstate.set_priority(PriorityType.RESOURCE_TRADE, 0) foAI.foAIstate.set_priority(PriorityType.RESOURCE_CONSTRUCTION, 0) foAI.foAIstate.set_priority(PriorityType.PRODUCTION_EXPLORATION, _calculate_exploration_priority()) prioritiees_timer.start('setting Colony Priority') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_COLONISATION, _calculate_colonisation_priority()) prioritiees_timer.start('setting Outpost Priority') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_OUTPOST, _calculate_outpost_priority()) prioritiees_timer.start('setting Invasion Priority') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_INVASION, _calculate_invasion_priority()) prioritiees_timer.start('setting Military Priority') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_MILITARY, _calculate_military_priority()) prioritiees_timer.start('setting other priorities') foAI.foAIstate.set_priority(PriorityType.PRODUCTION_BUILDINGS, 25) prioritiees_timer.stop_print_and_clear()
def _determine_colony_threat_factor(planet_id, spec_name, existing_presence): universe = fo.getUniverse() planet = universe.getPlanet(planet_id) if not planet: # should have been checked previously, but belt-and-suspenders error("Can't retrieve planet ID %d" % planet_id) return 0 sys_status = get_aistate().systemStatus.get(planet.systemID, {}) cur_best_mil_ship_rating = max(MilitaryAI.cur_best_mil_ship_rating(), 0.001) local_defenses = sys_status.get("all_local_defenses", 0) local_threat = sys_status.get("fleetThreat", 0) + sys_status.get( "monsterThreat", 0) neighbor_threat = sys_status.get("neighborThreat", 0) jump2_threat = 0.6 * max( 0, sys_status.get("jump2_threat", 0) - sys_status.get("my_neighbor_rating", 0)) area_threat = neighbor_threat + jump2_threat area_threat *= 2.0 / ( existing_presence + 2 ) # once we have a foothold be less scared off by area threats # TODO: evaluate detectability by specific source of area threat, also consider if the subject AI already has # researched techs that would grant a stealth bonus local_enemies = sys_status.get("enemies_nearly_supplied", []) # could more conservatively base detection on get_aistate().misc.get("observed_empires") if not EspionageAI.colony_detectable_by_empire( planet_id, spec_name, empire=local_enemies, default_result=True): area_threat *= 0.05 net_threat = max(0, local_threat + area_threat - local_defenses) # even if our military has lost all warships, rate planets as if we have at least one reference_rating = max( cur_best_mil_ship_rating, MilitaryAI.get_preferred_max_military_portion_for_single_battle() * MilitaryAI.get_concentrated_tot_mil_rating(), ) threat_factor = min(1.0, reference_rating / (net_threat + 0.001))**2 if threat_factor < 0.5: mil_ref_string = "Military rating reference: %.1f" % reference_rating debug( "Significant threat discounting %2d%% at %s, local defense: %.1f, local threat %.1f, area threat %.1f" % (100 * (1 - threat_factor), planet.name, local_defenses, local_threat, area_threat) + mil_ref_string) return threat_factor
def check_mergers(self, context=""): """ Merge local fleets with same mission into this fleet. :param context: Context of the function call for logging purposes :type context: str """ debug("Considering to merge %s", self.__str__()) if self.type not in MERGEABLE_MISSION_TYPES: debug("Mission type does not allow merging.") return if not self.target: debug("Mission has no valid target - do not merge.") return universe = fo.getUniverse() empire_id = fo.empireID() fleet_id = self.fleet.id main_fleet = universe.getFleet(fleet_id) main_fleet_system_id = main_fleet.systemID if main_fleet_system_id == INVALID_ID: debug("Can't merge: fleet in middle of starlane.") return # only merge PROTECT_REGION if there is any threat near target if self.type == MissionType.PROTECT_REGION: neighbor_systems = universe.getImmediateNeighbors(self.target.id, empire_id) if not any(MilitaryAI.get_system_local_threat(sys_id) for sys_id in neighbor_systems): debug("Not merging PROTECT_REGION fleet - no threat nearby.") return destroyed_list = set(universe.destroyedObjectIDs(empire_id)) aistate = get_aistate() system_status = aistate.systemStatus[main_fleet_system_id] other_fleets_here = [fid for fid in system_status.get('myFleetsAccessible', []) if fid != fleet_id and fid not in destroyed_list and universe.getFleet(fid).ownedBy(empire_id)] if not other_fleets_here: debug("No other fleets here") return for fid in other_fleets_here: fleet_mission = aistate.get_fleet_mission(fid) if fleet_mission.type != self.type or fleet_mission.target != self.target: debug("Local candidate %s does not have same mission." % fleet_mission) continue FleetUtilsAI.merge_fleet_a_into_b(fid, fleet_id, context="Order %s of mission %s" % (context, self))
def _need_repair(self): """ Check if fleet need repair. Check if fleet HP is less of cutoff. TODO make more clever implementation. """ repair_limit = 0.70 universe = fo.getUniverse() fleet_id = self.fleet.id # if combat fleet, use military repair check if foAI.foAIstate.get_fleet_role(fleet_id) in COMBAT_MISSION_TYPES: return fleet_id in MilitaryAI.avail_mil_needing_repair([fleet_id], False, bool(self.orders))[0] fleet = universe.getFleet(fleet_id) ships_cur_health = 0 ships_max_health = 0 for ship_id in fleet.shipIDs: this_ship = universe.getShip(ship_id) ships_cur_health += this_ship.currentMeterValue(fo.meterType.structure) ships_max_health += this_ship.currentMeterValue(fo.meterType.maxStructure) return ships_cur_health < repair_limit * ships_max_health
def evaluate_invasion_planet(planet_id, secure_fleet_missions, verbose=True): """Return the invasion value (score, troops) of a planet.""" detail = [] building_values = {"BLD_IMPERIAL_PALACE": 1000, "BLD_CULTURE_ARCHIVES": 1000, "BLD_AUTO_HISTORY_ANALYSER": 100, "BLD_SHIPYARD_BASE": 100, "BLD_SHIPYARD_ORG_ORB_INC": 200, "BLD_SHIPYARD_ORG_XENO_FAC": 200, "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200, "BLD_SHIPYARD_CON_NANOROBO": 300, "BLD_SHIPYARD_CON_GEOINT": 400, "BLD_SHIPYARD_CON_ADV_ENGINE": 1000, "BLD_SHIPYARD_AST": 300, "BLD_SHIPYARD_AST_REF": 1000, "BLD_SHIPYARD_ENRG_SOLAR": 1500, "BLD_INDUSTRY_CENTER": 500, "BLD_GAS_GIANT_GEN": 200, "BLD_SOL_ORB_GEN": 800, "BLD_BLACK_HOLE_POW_GEN": 2000, "BLD_ENCLAVE_VOID": 500, "BLD_NEUTRONIUM_EXTRACTOR": 2000, "BLD_NEUTRONIUM_SYNTH": 2000, "BLD_NEUTRONIUM_FORGE": 1000, "BLD_CONC_CAMP": 100, "BLD_BIOTERROR_PROJECTOR": 1000, "BLD_SHIPYARD_ENRG_COMP": 3000, } # TODO: add more factors, as used for colonization universe = fo.getUniverse() empire_id = fo.empireID() max_jumps = 8 planet = universe.getPlanet(planet_id) if planet is None: # TODO: exclude planets with stealth higher than empireDetection print "invasion AI couldn't access any info for planet id %d" % planet_id return [0, 0] 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: print "invasion AI couldn't get current info on planet id %d (was stealthed at last sighting)" % planet_id # TODO: track detection strength, order new scouting when it goes up return [0, 0] # last time we had partial vis of the system, the planet was stealthed to us species_name = planet.speciesName species = fo.getSpecies(species_name) if not species or AIDependencies.TAG_DESTROYED_ON_CONQUEST in species.tags: # this call iterates over this Empire's available species with which it could colonize after an invasion planet_eval = ColonisationAI.assign_colonisation_values([planet_id], MissionType.INVASION, None, detail) pop_val = max(0.75 * planet_eval.get(planet_id, [0])[0], ColonisationAI.evaluate_planet(planet_id, MissionType.OUTPOST, None, detail)) else: pop_val = ColonisationAI.evaluate_planet(planet_id, MissionType.INVASION, species_name, detail) bld_tally = 0 for bldType in [universe.getBuilding(bldg).buildingTypeName for bldg in planet.buildingIDs]: bval = building_values.get(bldType, 50) bld_tally += bval detail.append("%s: %d" % (bldType, bval)) tech_tally = 0 for unlocked_tech in AIDependencies.SPECIES_TECH_UNLOCKS.get(species_name, []): if not tech_is_complete(unlocked_tech): rp_cost = fo.getTech(unlocked_tech).researchCost(empire_id) tech_tally += rp_cost * 4 detail.append("%s: %d" % (unlocked_tech, rp_cost * 4)) p_sys_id = planet.systemID capitol_id = PlanetUtilsAI.get_capital() least_jumps_path = [] clear_path = True if capitol_id: homeworld = universe.getPlanet(capitol_id) if homeworld: home_system_id = homeworld.systemID eval_system_id = planet.systemID if (home_system_id != INVALID_ID) and (eval_system_id != INVALID_ID): least_jumps_path = list(universe.leastJumpsPath(home_system_id, eval_system_id, empire_id)) max_jumps = len(least_jumps_path) system_status = foAI.foAIstate.systemStatus.get(p_sys_id, {}) system_fleet_treat = system_status.get('fleetThreat', 1000) system_monster_threat = system_status.get('monsterThreat', 0) sys_total_threat = system_fleet_treat + system_monster_threat + system_status.get('planetThreat', 0) max_path_threat = system_fleet_treat mil_ship_rating = MilitaryAI.cur_best_mil_ship_rating() for path_sys_id in least_jumps_path: path_leg_status = foAI.foAIstate.systemStatus.get(path_sys_id, {}) path_leg_threat = path_leg_status.get('fleetThreat', 1000) + path_leg_status.get('monsterThreat', 0) if path_leg_threat > 0.5 * mil_ship_rating: clear_path = False if path_leg_threat > max_path_threat: max_path_threat = path_leg_threat pop = planet.currentMeterValue(fo.meterType.population) target_pop = planet.currentMeterValue(fo.meterType.targetPopulation) troops = planet.currentMeterValue(fo.meterType.troops) max_troops = planet.currentMeterValue(fo.meterType.maxTroops) # TODO: refactor troop determination into function for use in mid-mission updates and also consider defender techs max_troops += AIDependencies.TROOPS_PER_POP * (target_pop - pop) this_system = universe.getSystem(p_sys_id) secure_targets = [p_sys_id] + list(this_system.planetIDs) system_secured = False for mission in secure_fleet_missions: if system_secured: break secure_fleet_id = mission.fleet.id s_fleet = universe.getFleet(secure_fleet_id) if not s_fleet or s_fleet.systemID != p_sys_id: continue if mission.type == MissionType.SECURE: target_obj = mission.target.get_object() if target_obj is not None and target_obj.id in secure_targets: system_secured = True break system_secured = system_secured and system_status.get('myFleetRating', 0) if verbose: print ("Invasion eval of %s\n" " - maxShields: %.1f\n" " - sysFleetThreat: %.1f\n" " - sysMonsterThreat: %.1f") % ( planet, planet.currentMeterValue(fo.meterType.maxShield), system_fleet_treat, system_monster_threat) supply_val = 0 enemy_val = 0 if planet.owner != -1: # value in taking this away from an enemy enemy_val = 20 * (planet.currentMeterValue(fo.meterType.targetIndustry) + 2*planet.currentMeterValue(fo.meterType.targetResearch)) if p_sys_id in ColonisationAI.annexable_system_ids: # TODO: extend to rings supply_val = 100 elif p_sys_id in state.get_systems_by_supply_tier(-1): supply_val = 200 elif p_sys_id in state.get_systems_by_supply_tier(-2): supply_val = 300 elif p_sys_id in state.get_systems_by_supply_tier(-3): supply_val = 400 if max_path_threat > 0.5 * mil_ship_rating: if max_path_threat < 3 * mil_ship_rating: supply_val *= 0.5 else: supply_val *= 0.2 # devalue invasions that would require too much military force threat_factor = min(1, 0.2*MilitaryAI.get_tot_mil_rating()/(sys_total_threat+0.001))**2 design_id, _, locs = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_INVASION) if not locs or not universe.getPlanet(locs[0]): # We are in trouble anyway, so just calculate whatever approximation... build_time = 4 planned_troops = troops if system_secured else min(troops + max_jumps + build_time, max_troops) planned_troops += .01 # we must attack with more troops than there are defenders troop_cost = math.ceil((planned_troops+_TROOPS_SAFETY_MARGIN) / 6.0) * 20 * FleetUtilsAI.get_fleet_upkeep() else: loc = locs[0] species_here = universe.getPlanet(loc).speciesName design = fo.getShipDesign(design_id) cost_per_ship = design.productionCost(empire_id, loc) build_time = design.productionTime(empire_id, loc) troops_per_ship = CombatRatingsAI.weight_attack_troops(design.troopCapacity, CombatRatingsAI.get_species_troops_grade(species_here)) planned_troops = troops if system_secured else min(troops + max_jumps + build_time, max_troops) planned_troops += .01 # we must attack with more troops than there are defenders ships_needed = math.ceil((planned_troops+_TROOPS_SAFETY_MARGIN) / float(troops_per_ship)) troop_cost = ships_needed * cost_per_ship # fleet upkeep is already included in query from server # apply some bias to expensive operations normalized_cost = float(troop_cost) / max(fo.getEmpire().productionPoints, 1) normalized_cost = max(1., normalized_cost) cost_score = (normalized_cost**2 / 50.0) * troop_cost base_score = pop_val + supply_val + bld_tally + tech_tally + enemy_val - cost_score planet_score = retaliation_risk_factor(planet.owner) * threat_factor * max(0, base_score) if clear_path: planet_score *= 1.5 if verbose: print (' - planet score: %.2f\n' ' - troop score: %.2f\n' ' - projected troop cost: %.1f\n' ' - threat factor: %s\n' ' - planet detail: %s\n' ' - popval: %.1f\n' ' - supplyval: %.1f\n' ' - bldval: %s\n' ' - enemyval: %s') % (planet_score, planned_troops, troop_cost, threat_factor, detail, pop_val, supply_val, bld_tally, enemy_val) return [planet_score, planned_troops]
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 print print "Checking orders for fleet %s (on turn %d), with mission type %s" % ( self.fleet.get_object(), fo.currentTurn(), self.type or 'No mission') if MissionType.INVASION == self.type: self._check_retarget_invasion() for fleet_order in self.orders: print "Checking order: %s" % fleet_order if isinstance( fleet_order, (OrderColonize, OrderOutpost, OrderInvade)): # TODO: invasion? if self._check_abort_mission(fleet_order): print "Aborting fleet order %s" % fleet_order return self.check_mergers(context=str(fleet_order)) if fleet_order.can_issue_order(verbose=False): if isinstance( fleet_order, OrderMove ) and order_completed: # only move if all other orders completed print "Issuing fleet order %s" % fleet_order fleet_order.issue_order() elif not isinstance(fleet_order, OrderMove): print "Issuing fleet order %s" % fleet_order fleet_order.issue_order() else: print "NOT issuing (even though can_issue) fleet order %s" % fleet_order print "Order issued: %s" % fleet_order.order_issued if not fleet_order.order_issued: order_completed = False else: # check that we're not held up by a Big Monster if fleet_order.order_issued: # It's unclear why we'd really get to this spot, but it has been observed to happen, perhaps due to # game being reloaded after code changes. # Go on to the next order. continue print "CAN'T issue fleet order %s" % fleet_order if isinstance(fleet_order, OrderMove): this_system_id = fleet_order.target.id this_status = foAI.foAIstate.systemStatus.setdefault( this_system_id, {}) if this_status.get('monsterThreat', 0) > fo.currentTurn( ) * MilitaryAI.cur_best_mil_ship_rating() / 4.0: if (self.type not in ( MissionType.MILITARY, MissionType.SECURE ) or fleet_order != self.orders[ -1] # if this move order is not this mil fleet's final destination, and blocked by Big Monster, release and hope for more effective reassignment ): print "Aborting mission due to being blocked by Big Monster at system %d, threat %d" % ( this_system_id, foAI.foAIstate. systemStatus[this_system_id]['monsterThreat']) print "Full set of orders were:" for this_order in self.orders: print " - %s" % this_order self.clear_fleet_orders() self.clear_target() return # moving to another system stops issuing all orders in system where fleet is # move order is also the last order in system if isinstance(fleet_order, OrderMove): fleet = self.fleet.get_object() if fleet.systemID != fleet_order.target.id: break else: # went through entire order list if order_completed: print "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 = universe.getVisibilityTurnsMap( planet.systemID, fo.empireID()).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap( planet.id, fo.empireID()).get(fo.visibility.partial, -9999) if planet_partial_vis_turn == sys_partial_vis_turn and not planet.currentMeterValue( fo.meterType.population): print "Potential Error: Fleet %d has tentatively completed its colonize mission but will wait to confirm population." % self.fleet.id print " Order details are %s" % last_order print " 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 print " 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 = -1 if last_order and isinstance(last_order, OrderMilitary): last_sys_target = last_order.target.id # if (MissionType.SECURE == self.type) or # not doing this until decide a way to release from a SECURE mission secure_targets = set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs) 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" elif last_sys_target in AIstate.blockadeTargetedSystemIDs: secure_type = "Blockade" else: secure_type = "Unidentified" print "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 fleet_id = self.fleet.id if clear_all: if orders: print "Fleet %d has completed its mission; clearing all orders and targets." % self.fleet.id print "Full set of orders were:" for this_order in orders: print "\t\t %s" % this_order self.clear_fleet_orders() self.clear_target() if foAI.foAIstate.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 print "No Current Orders" else: # TODO: evaluate releasing a smaller portion or none of the ships system_status = foAI.foAIstate.systemStatus.setdefault( last_sys_target, {}) new_fleets = [] threat_present = (system_status.get('totalThreat', 0) != 0) or (system_status.get( 'neighborThreat', 0) != 0) 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: threat_present = True break if not threat_present: print "No current threat in target system; releasing a portion of ships." new_fleets = FleetUtilsAI.split_fleet( self.fleet.id ) # at least first stage of current task is done; release extra ships for potential other deployments else: print "Threat remains in target system; NOT releasing any ships." new_military_fleets = [] for fleet_id in new_fleets: if foAI.foAIstate.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 can_issue_order(self, verbose=False): if not super(OrderMove, self).can_issue_order(verbose=verbose): return False # TODO: figure out better way to have invasions (& possibly colonizations) require visibility on target without needing visibility of all intermediate systems # if False and main_mission_type not in [MissionType.ATTACK, # TODO: consider this later # MissionType.MILITARY, # MissionType.SECURE, # MissionType.HIT_AND_RUN, # MissionType.EXPLORATION]: # if not universe.getVisibility(target_id, foAI.foAIstate.empireID) >= fo.visibility.partial: # #if not target_id in interior systems # foAI.foAIstate.needsEmergencyExploration.append(fleet.systemID) # return False system_id = self.fleet.get_system().id if system_id == self.target.get_system().id: return True # TODO: already there, but could consider retreating fleet_rating = foAI.foAIstate.get_rating(self.fleet.id).get( 'overall', 0) target_sys_status = foAI.foAIstate.systemStatus.get(self.target.id, {}) f_threat = target_sys_status.get('fleetThreat', 0) m_threat = target_sys_status.get('monsterThreat', 0) p_threat = target_sys_status.get('planetThreat', 0) threat = f_threat + m_threat + p_threat safety_factor = MilitaryAI.get_safety_factor() universe = fo.getUniverse() if fleet_rating >= safety_factor * threat: return True elif not p_threat and self.target.id in fo.getEmpire( ).supplyUnobstructedSystems: return True else: sys1 = universe.getSystem(system_id) sys1_name = sys1 and sys1.name or "unknown" target_system = self.target.get_system() target_system_name = (target_system and target_system.get_object().name) or "unknown" my_other_fleet_rating = foAI.foAIstate.systemStatus.get( self.target.id, {}).get('myFleetRating', 0) # TODO: adjust calc for any departing fleets is_military = foAI.foAIstate.get_fleet_role( self.fleet.id) == MissionType.MILITARY total_rating = my_other_fleet_rating + fleet_rating if any( ((my_other_fleet_rating > 3 * safety_factor * threat), (is_military and total_rating > safety_factor * threat), (is_military and total_rating > 0.8 * safety_factor * threat and fleet_rating > 0.2 * threat))): if verbose: print "\tAdvancing fleet %d (rating %d) at system %d (%s) into system %d (%s) with threat %d because of sufficient empire fleet strength already at destination" % ( self.fleet.id, fleet_rating, system_id, sys1_name, self.target.id, target_system_name, threat) return True elif threat == p_threat and not self.fleet.get_object( ).aggressive and not my_other_fleet_rating and not target_sys_status.get( 'localEnemyFleetIDs', [-1]): if verbose: print( "\tAdvancing fleet %d (rating %d) at system %d (%s) into system %d (%s) with planet threat %d because nonaggressive" + " and no other fleets present to trigger combat") % ( self.fleet.id, fleet_rating, system_id, sys1_name, self.target.id, target_system_name, threat) return True else: if verbose: print "\tHolding fleet %d (rating %d) at system %d (%s) before travelling to system %d (%s) with threat %d" % ( self.fleet.id, fleet_rating, system_id, sys1_name, self.target.id, target_system_name, threat) needs_vis = foAI.foAIstate.misc.setdefault('needs_vis', []) if self.target.id not in needs_vis: needs_vis.append(self.target.id) return False return True
def calculatePriorities(): "calculates the priorities of the AI player" print("checking statuses") # Industry, Research, Colony, Invasion, Military times=[] tasks = [] times.append( time() ) tasks.append("init") foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_PRODUCTION, 50) # let this one stay fixed & just adjust Research times.append( time() ) tasks.append( "setting Production Priority" ) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_RESEARCH, calculateResearchPriority()) times.append( time() ) tasks.append( "setting Research Priority") ColonisationAI.getColonyFleets() # sets foAI.foAIstate.colonisablePlanetIDs and foAI.foAIstate.outpostPlanetIDs and many other values used by other modules times.append( time() ) tasks.append( "Evaluating Colonization Status") InvasionAI.getInvasionFleets() # sets AIstate.invasionFleetIDs, AIstate.opponentPlanetIDs, and AIstate.invasionTargetedPlanetIDs times.append( time() ) tasks.append( "Evaluating Invasion Status") MilitaryAI.getMilitaryFleets() # sets AIstate.militaryFleetIDs and AIstate.militaryTargetedSystemIDs times.append( time() ) tasks.append( "Evaluating Military Status") print("calculating priorities") calculateIndustryPriority()#purely for reporting purposes times.append( time() ) tasks.append( "reporting Production Priority") foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_TRADE, 0) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESOURCE_CONSTRUCTION, 0) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_EXPLORATION, calculateExplorationPriority()) times.append( time() ) tasks.append( "setting Exploration Priority") foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_OUTPOST, calculateOutpostPriority()) times.append( time() ) tasks.append( "setting Outpost Priority") foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_COLONISATION, calculateColonisationPriority()) times.append( time() ) tasks.append( "setting Colony Priority") foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_INVASION, calculateInvasionPriority()) times.append( time() ) tasks.append( "setting Invasion Priority") foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY, calculateMilitaryPriority()) times.append( time() ) tasks.append( "setting Military Priority") foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_BUILDINGS, 25) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_LEARNING, calculateLearningPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_GROWTH, calculateGrowthPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_PRODUCTION, calculateTechsProductionPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_CONSTRUCTION, calculateConstructionPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_ECONOMICS, 0) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_SHIPS, calculateShipsPriority()) foAI.foAIstate.setPriority(EnumsAI.AIPriorityType.PRIORITY_RESEARCH_DEFENSE, 0) times.append( time() ) tasks.append( "total processing") for t_index in range(1, len(times)-1): print "calculatePriorities(): %40s took %d msec"%(tasks[t_index], int(1000*(times[t_index]-times[t_index-1]))) print "calculatePriorities(): %40s took %d msec"%(tasks[-1], int(1000*(times[-1]-times[0])))
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 print print "Checking orders for fleet %s (on turn %d), with mission type %s" % (self.fleet.get_object(), fo.currentTurn(), self.type or 'No mission') if MissionType.INVASION == self.type: self._check_retarget_invasion() for fleet_order in self.orders: print "Checking order: %s" % fleet_order if isinstance(fleet_order, (OrderColonize, OrderOutpost, OrderInvade)): # TODO: invasion? if self._check_abort_mission(fleet_order): print "Aborting fleet order %s" % fleet_order return self.check_mergers(context=str(fleet_order)) if fleet_order.can_issue_order(verbose=False): if isinstance(fleet_order, OrderMove) and order_completed: # only move if all other orders completed print "Issuing fleet order %s" % fleet_order fleet_order.issue_order() elif not isinstance(fleet_order, OrderMove): print "Issuing fleet order %s" % fleet_order fleet_order.issue_order() else: print "NOT issuing (even though can_issue) fleet order %s" % fleet_order print "Order issued: %s" % fleet_order.order_issued if not fleet_order.order_issued: order_completed = False else: # check that we're not held up by a Big Monster if fleet_order.order_issued: # It's unclear why we'd really get to this spot, but it has been observed to happen, perhaps due to # game being reloaded after code changes. # Go on to the next order. continue print "CAN'T issue fleet order %s" % fleet_order if isinstance(fleet_order, OrderMove): this_system_id = fleet_order.target.id this_status = foAI.foAIstate.systemStatus.setdefault(this_system_id, {}) if this_status.get('monsterThreat', 0) > fo.currentTurn() * MilitaryAI.cur_best_mil_ship_rating() / 4.0: if (self.type not in (MissionType.MILITARY, MissionType.SECURE) or fleet_order != self.orders[-1] # if this move order is not this mil fleet's final destination, and blocked by Big Monster, release and hope for more effective reassignment ): print "Aborting mission due to being blocked by Big Monster at system %d, threat %d" % (this_system_id, foAI.foAIstate.systemStatus[this_system_id]['monsterThreat']) print "Full set of orders were:" for this_order in self.orders: print " - %s" % this_order self.clear_fleet_orders() self.clear_target() return # moving to another system stops issuing all orders in system where fleet is # move order is also the last order in system if isinstance(fleet_order, OrderMove): fleet = self.fleet.get_object() if fleet.systemID != fleet_order.target.id: break else: # went through entire order list if order_completed: print "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 = universe.getVisibilityTurnsMap(planet.systemID, fo.empireID()).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(planet.id, fo.empireID()).get(fo.visibility.partial, -9999) if planet_partial_vis_turn == sys_partial_vis_turn and not planet.currentMeterValue(fo.meterType.population): print "Potential Error: Fleet %d has tentatively completed its colonize mission but will wait to confirm population." % self.fleet.id print " Order details are %s" % last_order print " 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 print " 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 = -1 if last_order and isinstance(last_order, OrderMilitary): last_sys_target = last_order.target.id # if (MissionType.SECURE == self.type) or # not doing this until decide a way to release from a SECURE mission secure_targets = set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs) 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" elif last_sys_target in AIstate.blockadeTargetedSystemIDs: secure_type = "Blockade" else: secure_type = "Unidentified" print "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 fleet_id = self.fleet.id if clear_all: if orders: print "Fleet %d has completed its mission; clearing all orders and targets." % self.fleet.id print "Full set of orders were:" for this_order in orders: print "\t\t %s" % this_order self.clear_fleet_orders() self.clear_target() if foAI.foAIstate.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 print "No Current Orders" else: # TODO: evaluate releasing a smaller portion or none of the ships system_status = foAI.foAIstate.systemStatus.setdefault(last_sys_target, {}) new_fleets = [] threat_present = (system_status.get('totalThreat', 0) != 0) or (system_status.get('neighborThreat', 0) != 0) 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: threat_present = True break if not threat_present: print "No current threat in target system; releasing a portion of ships." new_fleets = FleetUtilsAI.split_fleet(self.fleet.id) # at least first stage of current task is done; release extra ships for potential other deployments else: print "Threat remains in target system; NOT releasing any ships." new_military_fleets = [] for fleet_id in new_fleets: if foAI.foAIstate.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 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 evaluate_invasion_planet(planet_id, empire, secure_fleet_missions, verbose=True): """Return the invasion value (score, troops) of a planet.""" detail = [] building_values = {"BLD_IMPERIAL_PALACE": 1000, "BLD_CULTURE_ARCHIVES": 1000, "BLD_SHIPYARD_BASE": 100, "BLD_SHIPYARD_ORG_ORB_INC": 200, "BLD_SHIPYARD_ORG_XENO_FAC": 200, "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200, "BLD_SHIPYARD_CON_NANOROBO": 300, "BLD_SHIPYARD_CON_GEOINT": 400, "BLD_SHIPYARD_CON_ADV_ENGINE": 1000, "BLD_SHIPYARD_AST": 300, "BLD_SHIPYARD_AST_REF": 1000, "BLD_SHIPYARD_ENRG_SOLAR": 1500, "BLD_INDUSTRY_CENTER": 500, "BLD_GAS_GIANT_GEN": 200, "BLD_SOL_ORB_GEN": 800, "BLD_BLACK_HOLE_POW_GEN": 2000, "BLD_ENCLAVE_VOID": 500, "BLD_NEUTRONIUM_EXTRACTOR": 2000, "BLD_NEUTRONIUM_SYNTH": 2000, "BLD_NEUTRONIUM_FORGE": 1000, "BLD_CONC_CAMP": 100, "BLD_BIOTERROR_PROJECTOR": 1000, "BLD_SHIPYARD_ENRG_COMP": 3000, } # TODO: add more factors, as used for colonization universe = fo.getUniverse() empire_id = empire.empireID max_jumps = 8 planet = universe.getPlanet(planet_id) if planet is None: # TODO: exclude planets with stealth higher than empireDetection print "invasion AI couldn't access any info for planet id %d" % planet_id return [0, 0] sys_partial_vis_turn = universe.getVisibilityTurnsMap(planet.systemID, empire_id).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(planet_id, empire_id).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: print "invasion AI couldn't get current info on planet id %d (was stealthed at last sighting)" % planet_id # TODO: track detection strength, order new scouting when it goes up return [0, 0] # last time we had partial vis of the system, the planet was stealthed to us species_name = planet.speciesName species = fo.getSpecies(species_name) if not species: # this call iterates over this Empire's available species with which it could colonize after an invasion planet_eval = ColonisationAI.assign_colonisation_values([planet_id], EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, None, empire, detail) pop_val = max(0.75*planet_eval.get(planet_id, [0])[0], ColonisationAI.evaluate_planet(planet_id, EnumsAI.AIFleetMissionType.FLEET_MISSION_OUTPOST, None, empire, detail)) else: pop_val = ColonisationAI.evaluate_planet(planet_id, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, species_name, empire, detail) bld_tally = 0 for bldType in [universe.getObject(bldg).buildingTypeName for bldg in planet.buildingIDs]: bval = building_values.get(bldType, 50) bld_tally += bval detail.append("%s: %d" % (bldType, bval)) p_sys_id = planet.systemID capitol_id = PlanetUtilsAI.get_capital() least_jumps_path = [] clear_path = True if capitol_id: homeworld = universe.getPlanet(capitol_id) if homeworld: home_system_id = homeworld.systemID eval_system_id = planet.systemID if (home_system_id != -1) and (eval_system_id != -1): least_jumps_path = list(universe.leastJumpsPath(home_system_id, eval_system_id, empire_id)) max_jumps = len(least_jumps_path) system_status = foAI.foAIstate.systemStatus.get(p_sys_id, {}) system_fleet_treat = system_status.get('fleetThreat', 1000) system_monster_threat = system_status.get('monsterThreat', 0) sys_total_threat = system_fleet_treat + system_monster_threat + system_status.get('planetThreat', 0) max_path_threat = system_fleet_treat mil_ship_rating = MilitaryAI.cur_best_mil_ship_rating() for path_sys_id in least_jumps_path: path_leg_status = foAI.foAIstate.systemStatus.get(path_sys_id, {}) path_leg_threat = path_leg_status.get('fleetThreat', 1000) + path_leg_status.get('monsterThreat', 0) if path_leg_threat > 0.5 * mil_ship_rating: clear_path = False if path_leg_threat > max_path_threat: max_path_threat = path_leg_threat troops = planet.currentMeterValue(fo.meterType.troops) max_troops = planet.currentMeterValue(fo.meterType.maxTroops) this_system = universe.getSystem(p_sys_id) secure_targets = [p_sys_id] + list(this_system.planetIDs) system_secured = False for mission in secure_fleet_missions: if system_secured: break secure_fleet_id = mission.target_id s_fleet = universe.getFleet(secure_fleet_id) if not s_fleet or s_fleet.systemID != p_sys_id: continue for ai_target in mission.get_targets(EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE): target_obj = ai_target.target_obj if (target_obj is not None) and target_obj.id in secure_targets: system_secured = True break if verbose: print "invasion eval of %s %d --- maxShields %.1f -- sysFleetThreat %.1f -- sysMonsterThreat %.1f" % ( planet.name, planet_id, planet.currentMeterValue(fo.meterType.maxShield), system_fleet_treat, system_monster_threat) supply_val = 0 enemy_val = 0 if planet.owner != -1: # value in taking this away from an enemy enemy_val = 20 * (planet.currentMeterValue(fo.meterType.targetIndustry) + 2*planet.currentMeterValue(fo.meterType.targetResearch)) if p_sys_id in ColonisationAI.annexable_system_ids: # TODO: extend to rings supply_val = 100 elif p_sys_id in ColonisationAI.annexable_ring1: supply_val = 200 elif p_sys_id in ColonisationAI.annexable_ring2: supply_val = 300 elif p_sys_id in ColonisationAI.annexable_ring3: supply_val = 400 if max_path_threat > 0.5 * mil_ship_rating: if max_path_threat < 3 * mil_ship_rating: supply_val *= 0.5 else: supply_val *= 0.2 threat_factor = min(1, 0.2*MilitaryAI.totMilRating/(sys_total_threat+0.001))**2 # devalue invasions that would require too much military force build_time = 4 planned_troops = troops if system_secured else min(troops+max_jumps+build_time, max_troops) if not tech_is_complete("SHP_ORG_HULL"): troop_cost = math.ceil(planned_troops/6.0) * (40*(1+foAI.foAIstate.shipCount * AIDependencies.SHIP_UPKEEP)) else: troop_cost = math.ceil(planned_troops/6.0) * (20*(1+foAI.foAIstate.shipCount * AIDependencies.SHIP_UPKEEP)) planet_score = retaliation_risk_factor(planet.owner) * threat_factor * max(0, pop_val+supply_val+bld_tally+enemy_val-0.8*troop_cost) if clear_path: planet_score *= 1.5 invasion_score = [planet_score, planned_troops] print invasion_score, "projected Troop Cost:", troop_cost, ", threatFactor: ", threat_factor, ", planet detail ", detail, "popval, supplyval, bldval, enemyval", pop_val, supply_val, bld_tally, enemy_val return invasion_score
def evaluate_invasion_planet(planet_id, empire, secure_fleet_missions, verbose=True): """Return the invasion value (score, troops) of a planet.""" detail = [] building_values = {"BLD_IMPERIAL_PALACE": 1000, "BLD_CULTURE_ARCHIVES": 1000, "BLD_SHIPYARD_BASE": 100, "BLD_SHIPYARD_ORG_ORB_INC": 200, "BLD_SHIPYARD_ORG_XENO_FAC": 200, "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200, "BLD_SHIPYARD_CON_NANOROBO": 300, "BLD_SHIPYARD_CON_GEOINT": 400, "BLD_SHIPYARD_CON_ADV_ENGINE": 1000, "BLD_SHIPYARD_AST": 300, "BLD_SHIPYARD_AST_REF": 1000, "BLD_SHIPYARD_ENRG_SOLAR": 1500, "BLD_INDUSTRY_CENTER": 500, "BLD_GAS_GIANT_GEN": 200, "BLD_SOL_ORB_GEN": 800, "BLD_BLACK_HOLE_POW_GEN": 2000, "BLD_ENCLAVE_VOID": 500, "BLD_NEUTRONIUM_EXTRACTOR": 2000, "BLD_NEUTRONIUM_SYNTH": 2000, "BLD_NEUTRONIUM_FORGE": 1000, "BLD_CONC_CAMP": 100, "BLD_BIOTERROR_PROJECTOR": 1000, "BLD_SHIPYARD_ENRG_COMP": 3000, } # TODO: add more factors, as used for colonization universe = fo.getUniverse() empire_id = empire.empireID max_jumps = 8 planet = universe.getPlanet(planet_id) if planet is None: # TODO: exclude planets with stealth higher than empireDetection print "invasion AI couldn't access any info for planet id %d" % planet_id return [0, 0] sys_partial_vis_turn = universe.getVisibilityTurnsMap(planet.systemID, empire_id).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(planet_id, empire_id).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: print "invasion AI couldn't get current info on planet id %d (was stealthed at last sighting)" % planet_id # TODO: track detection strength, order new scouting when it goes up return [0, 0] # last time we had partial vis of the system, the planet was stealthed to us species_name = planet.speciesName species = fo.getSpecies(species_name) if not species: # this call iterates over this Empire's available species with which it could colonize after an invasion planet_eval = ColonisationAI.assign_colonisation_values([planet_id], EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, None, empire, detail) pop_val = max(0.75*planet_eval.get(planet_id, [0])[0], ColonisationAI.evaluate_planet(planet_id, EnumsAI.AIFleetMissionType.FLEET_MISSION_OUTPOST, None, empire, detail)) else: pop_val = ColonisationAI.evaluate_planet(planet_id, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, species_name, empire, detail) bld_tally = 0 for bldType in [universe.getObject(bldg).buildingTypeName for bldg in planet.buildingIDs]: bval = building_values.get(bldType, 50) bld_tally += bval detail.append("%s: %d" % (bldType, bval)) p_sys_id = planet.systemID capitol_id = PlanetUtilsAI.get_capital() least_jumps_path = [] clear_path = True if capitol_id: homeworld = universe.getPlanet(capitol_id) if homeworld: home_system_id = homeworld.systemID eval_system_id = planet.systemID if (home_system_id != -1) and (eval_system_id != -1): least_jumps_path = list(universe.leastJumpsPath(home_system_id, eval_system_id, empire_id)) max_jumps = len(least_jumps_path) system_status = foAI.foAIstate.systemStatus.get(p_sys_id, {}) system_fleet_treat = system_status.get('fleetThreat', 1000) system_monster_threat = system_status.get('monsterThreat', 0) sys_total_threat = system_fleet_treat + system_monster_threat + system_status.get('planetThreat', 0) max_path_threat = system_fleet_treat mil_ship_rating = MilitaryAI.cur_best_mil_ship_rating() for path_sys_id in least_jumps_path: path_leg_status = foAI.foAIstate.systemStatus.get(path_sys_id, {}) path_leg_threat = path_leg_status.get('fleetThreat', 1000) + path_leg_status.get('monsterThreat', 0) if path_leg_threat > 0.5 * mil_ship_rating: clear_path = False if path_leg_threat > max_path_threat: max_path_threat = path_leg_threat pop = planet.currentMeterValue(fo.meterType.population) target_pop = planet.currentMeterValue(fo.meterType.targetPopulation) troops = planet.currentMeterValue(fo.meterType.troops) max_troops = planet.currentMeterValue(fo.meterType.maxTroops) # TODO: refactor troop determination into function for use in mid-mission updates and also consider defender techs max_troops += AIDependencies.TROOPS_PER_POP * (target_pop - pop) this_system = universe.getSystem(p_sys_id) secure_targets = [p_sys_id] + list(this_system.planetIDs) system_secured = False for mission in secure_fleet_missions: if system_secured: break secure_fleet_id = mission.target_id s_fleet = universe.getFleet(secure_fleet_id) if not s_fleet or s_fleet.systemID != p_sys_id: continue for ai_target in mission.get_targets(EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE): target_obj = ai_target.target_obj if (target_obj is not None) and target_obj.id in secure_targets: system_secured = True break system_secured = system_secured and system_status.get('myFleetRating',0) if verbose: print "invasion eval of %s %d --- maxShields %.1f -- sysFleetThreat %.1f -- sysMonsterThreat %.1f" % ( planet.name, planet_id, planet.currentMeterValue(fo.meterType.maxShield), system_fleet_treat, system_monster_threat) supply_val = 0 enemy_val = 0 if planet.owner != -1: # value in taking this away from an enemy enemy_val = 20 * (planet.currentMeterValue(fo.meterType.targetIndustry) + 2*planet.currentMeterValue(fo.meterType.targetResearch)) if p_sys_id in ColonisationAI.annexable_system_ids: # TODO: extend to rings supply_val = 100 elif p_sys_id in ColonisationAI.annexable_ring1: supply_val = 200 elif p_sys_id in ColonisationAI.annexable_ring2: supply_val = 300 elif p_sys_id in ColonisationAI.annexable_ring3: supply_val = 400 if max_path_threat > 0.5 * mil_ship_rating: if max_path_threat < 3 * mil_ship_rating: supply_val *= 0.5 else: supply_val *= 0.2 threat_factor = min(1, 0.2*MilitaryAI.totMilRating/(sys_total_threat+0.001))**2 # devalue invasions that would require too much military force build_time = 4 planned_troops = troops if system_secured else min(troops+max_jumps+build_time, max_troops) if not tech_is_complete("SHP_ORG_HULL"): troop_cost = math.ceil(planned_troops/6.0) * (40*(1+foAI.foAIstate.shipCount * AIDependencies.SHIP_UPKEEP)) else: troop_cost = math.ceil(planned_troops/6.0) * (20*(1+foAI.foAIstate.shipCount * AIDependencies.SHIP_UPKEEP)) planet_score = retaliation_risk_factor(planet.owner) * threat_factor * max(0, pop_val+supply_val+bld_tally+enemy_val-0.8*troop_cost) if clear_path: planet_score *= 1.5 invasion_score = [planet_score, planned_troops] print invasion_score, "projected Troop Cost:", troop_cost, ", threatFactor: ", threat_factor, ", planet detail ", detail, "popval, supplyval, bldval, enemyval", pop_val, supply_val, bld_tally, enemy_val return invasion_score
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 print "--------------" print "Checking orders for fleet %d (on turn %d), with mission types %s" % ( self.target_id, fo.currentTurn(), [AIFleetMissionType.name(mt) for mt in self.get_mission_types()]) print "\t Full Orders are:" for this_order in self.orders: print "\t\t| %s" % this_order print "/t/t------" if AIFleetMissionType.FLEET_MISSION_INVASION in self.get_mission_types( ): self._check_retarget_invasion() for fleet_order in self.orders: print "\t| checking Order: %s" % fleet_order order_type = fleet_order.order_type if order_type in [ AIFleetOrderType.ORDER_COLONISE, AIFleetOrderType.ORDER_OUTPOST, AIFleetOrderType.ORDER_INVADE ]: # TODO: invasion? if self._check_abort_mission(fleet_order): print "\t\t| Aborting fleet order %s" % fleet_order return self.check_mergers(context=str(fleet_order)) if fleet_order.can_issue_order(verbose=True): if order_type == AIFleetOrderType.ORDER_MOVE and order_completed: # only move if all other orders completed print "\t\t| issuing fleet order %s" % fleet_order fleet_order.issue_order() elif order_type not in [ AIFleetOrderType.ORDER_MOVE, AIFleetOrderType.ORDER_DEFEND ]: print "\t\t| issuing fleet order %s" % fleet_order fleet_order.issue_order() else: print "\t\t| NOT issuing (even though can_issue) fleet order %s" % fleet_order print "\t\t| order status-- execution completed: %s" % fleet_order.execution_completed if not fleet_order.execution_completed: order_completed = False else: # check that we're not held up by a Big Monster print "\t\t| CAN'T issue fleet order %s" % fleet_order if order_type == AIFleetOrderType.ORDER_MOVE: this_system_id = fleet_order.target.target_id this_status = foAI.foAIstate.systemStatus.setdefault( this_system_id, {}) if this_status.get('monsterThreat', 0) > fo.currentTurn( ) * ProductionAI.cur_best_mil_ship_rating() / 4.0: first_mission = self.get_mission_types( )[0] if self.get_mission_types( ) else AIFleetMissionType.FLEET_MISSION_INVALID if (first_mission not in ( AIFleetMissionType.FLEET_MISSION_ATTACK, AIFleetMissionType.FLEET_MISSION_MILITARY, AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, AIFleetMissionType.FLEET_MISSION_SECURE, ) or fleet_order != self.orders[ -1] # if this move order is not this mil fleet's final destination, and blocked by Big Monster, release and hope for more effective reassignment ): print "Aborting mission due to being blocked by Big Monster at system %d, threat %d" % ( this_system_id, foAI.foAIstate. systemStatus[this_system_id]['monsterThreat']) print "Full set of orders were:" for this_order in self.orders: print "\t\t %s" % this_order self.clear_fleet_orders() self.clear_targets( ([-1] + self.get_mission_types()[:1])[-1]) return # moving to another system stops issuing all orders in system where fleet is # move order is also the last order in system if order_type == AIFleetOrderType.ORDER_MOVE: fleet = fo.getUniverse().getFleet(self.target_id) if fleet.systemID != fleet_order.target.target_id: break else: # went through entire order list if order_completed: print "\t| Final order is completed" orders = self.orders last_order = orders[-1] if orders else None universe = fo.getUniverse() if last_order and last_order.order_type == AIFleetOrderType.ORDER_COLONISE: planet = universe.getPlanet(last_order.target.target_id) sys_partial_vis_turn = universe.getVisibilityTurnsMap( planet.systemID, fo.empireID()).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap( planet.id, fo.empireID()).get(fo.visibility.partial, -9999) if planet_partial_vis_turn == sys_partial_vis_turn and not planet.currentMeterValue( fo.meterType.population): print "Potential Error: Fleet %d has tentatively completed its colonize mission but will wait to confirm population." % self.target_id print " Order details are %s" % last_order print " Order is valid: %s ; is Executed : %s; is execution completed: %s " % ( last_order.is_valid(), last_order.isExecuted(), last_order.isExecutionCompleted()) if not last_order.is_valid(): source_target = last_order.fleet target_target = last_order.target print " source target validity: %s; target target validity: %s " % ( source_target.valid, target_target.valid) if EnumsAI.TargetType.TARGET_SHIP == source_target.target_type: ship_id = source_target.target_id ship = universe.getShip(ship_id) if not ship: print "Ship id %d not a valid ship id" % ship_id print " source target Ship (%d), species %s, can%s colonize" % ( ship_id, ship.speciesName, ["not", ""][ship.canColonize]) return # colonize order must not have completed yet clearAll = True last_sys_target = -1 if last_order and last_order.order_type == AIFleetOrderType.ORDER_MILITARY: last_sys_target = last_order.target.target_id # if (AIFleetMissionType.FLEET_MISSION_SECURE in self.get_mission_types()) or # not doing this until decide a way to release from a SECURE mission secure_targets = set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs) 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" elif last_sys_target in AIstate.blockadeTargetedSystemIDs: secure_type = "Blockade" else: secure_type = "Unidentified" print "Fleet %d has completed initial stage of its mission to secure system %d (targeted for %s), may release a portion of ships" % ( self.target_id, last_sys_target, secure_type) clearAll = False fleet_id = self.target_id if clearAll: if orders: print "Fleet %d has completed its mission; clearing all orders and targets." % self.target_id print "Full set of orders were:" for this_order in orders: print "\t\t %s" % this_order self.clear_fleet_orders() self.clear_targets( ([-1] + self.get_mission_types()[:1])[-1]) if foAI.foAIstate.get_fleet_role(fleet_id) in ( AIFleetMissionType.FLEET_MISSION_MILITARY, AIFleetMissionType.FLEET_MISSION_ATTACK, AIFleetMissionType.FLEET_MISSION_DEFEND, AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, AIFleetMissionType.FLEET_MISSION_SECURE): allocations = MilitaryAI.get_military_fleets( milFleetIDs=[fleet_id], tryReset=False, thisround="Fleet %d Reassignment" % fleet_id) if allocations: MilitaryAI.assign_military_fleets_to_systems( useFleetIDList=[fleet_id], allocations=allocations) else: # no orders print "No Current Orders" else: #TODO: evaluate releasing a smaller portion or none of the ships system_status = foAI.foAIstate.systemStatus.setdefault( last_sys_target, {}) new_fleets = [] threat_present = (system_status.get('totalThreat', 0) != 0) or (system_status.get( 'neighborThreat', 0) != 0) 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: threat_present = True break if not threat_present: print "No current threat in target system; releasing a portion of ships." new_fleets = FleetUtilsAI.split_fleet( self.target_id ) # at least first stage of current task is done; release extra ships for potential other deployments else: print "Threat remains in target system; NOT releasing any ships." new_military_fleets = [] for fleet_id in new_fleets: if foAI.foAIstate.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( milFleetIDs=new_military_fleets, tryReset=False, thisround="Fleet Reassignment %s" % new_military_fleets) if allocations: MilitaryAI.assign_military_fleets_to_systems( useFleetIDList=new_military_fleets, allocations=allocations)
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" % (self.fleet.get_object(), fo.currentTurn(), self.type or 'No mission')) 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): if isinstance( fleet_order, OrderMove ) and order_completed: # only move if all other orders 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) debug("Order issued: %s" % fleet_order.order_issued) 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" % fleet_order) 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)): warn( "Fleet %d has tentatively completed its " "colonize mission but will wait to confirm population." % self.fleet.id) 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 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: # TODO: evaluate releasing a smaller portion or none of the ships system_status = aistate.systemStatus.setdefault( last_sys_target, {}) new_fleets = [] threat_present = system_status.get( 'totalThreat', 0) + system_status.get( 'neighborThreat', 0) > 0 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): 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) else: debug( "Threat remains in target system; NOT releasing any ships." ) 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 _get_target_for_protection_mission(self): """Get a target for a PROTECT_REGION mission. 1) If primary target (system target of this mission) is under attack, move to primary target. 2) If neighbors of primary target have local enemy forces weaker than this fleet, may move to attack 3) If no neighboring fleets or strongest enemy force is too strong, move to defend primary target """ # TODO: Also check fleet rating vs planets in decision making below not only vs fleets universe = fo.getUniverse() primary_objective = self.target.id debug("Trying to find target for protection mission. Target: %s", self.target) immediate_threat = MilitaryAI.get_system_local_threat(primary_objective) if immediate_threat: debug("Immediate threat! Moving to primary mission target") return primary_objective else: debug("No immediate threats.") # Try to eliminate neighbouring fleets neighbors = universe.getImmediateNeighbors(primary_objective, fo.empireID()) threat_list = sorted(map( lambda x: (MilitaryAI.get_system_local_threat(x), x), neighbors ), reverse=True) if not threat_list: debug("No neighbors (?!). Moving to primary mission target") return primary_objective debug("%s", threat_list) top_threat, candidate_system = threat_list[0] if not top_threat: # TODO: Move into second ring but needs more careful evaluation # For now, consider staying at the current location if enemy # owns a planet here which we can bombard. current_system_id = self.fleet.get_current_system_id() if current_system_id in neighbors: system = universe.getSystem(current_system_id) if assertion_fails(system is not None): return primary_objective empire_id = fo.empireID() for planet_id in system.planetIDs: planet = universe.getPlanet(planet_id) if (planet and not planet.ownedBy(empire_id) and not planet.unowned): debug("Currently no neighboring threats. " "Staying for bombardment of planet %s", planet) self.clear_fleet_orders() self.set_target(MissionType.MILITARY, TargetSystem(current_system_id)) self.generate_fleet_orders() self.issue_fleet_orders() return INVALID_ID # TODO consider attacking neighboring, non-military fleets # - needs more careful evaluation against neighboring threats # empire_id = fo.empireID() # for sys_id in neighbors: # system = universe.getSystem(sys_id) # if assertion_fails(system is not None): # continue # local_fleets = system.fleetIDs # for fleet_id in local_fleets: # fleet = universe.getFleet(fleet_id) # if not fleet or fleet.ownedBy(empire_id): # continue # return sys_id debug("No neighboring threats. Moving to primary mission target") return primary_objective # TODO rate against threat in target system # TODO only engage if can reach in 1 turn or leaves sufficient defense behind fleet_rating = CombatRatingsAI.get_fleet_rating(self.fleet.id) debug("This fleet rating: %d. Enemy Rating: %d", fleet_rating, top_threat) safety_factor = get_aistate().character.military_safety_factor() if fleet_rating < safety_factor*top_threat: debug("Neighboring threat is too powerful. Moving to primary mission target") return primary_objective # do not engage! debug("Engaging neighboring threat: %d", candidate_system) return candidate_system
def issueAIFleetOrders(self): "issues AIFleetOrders which can be issued in system and moves to next one if is possible" # TODO: priority ordersCompleted = True print "Checking orders for fleet %d"%(self.getAITargetID()) #print "\t Full Orders are:" #for aiFleetOrder2 in self.getAIFleetOrders(): # print "\t\t %s"%aiFleetOrder2 for aiFleetOrder in self.getAIFleetOrders(): print " %s"%(aiFleetOrder) clearAll=False if aiFleetOrder.getAIFleetOrderType() in [EnumsAI.AIFleetOrderType.ORDER_COLONISE, EnumsAI.AIFleetOrderType.ORDER_OUTPOST]:#TODO: invasion? universe=fo.getUniverse() planet = universe.getPlanet(aiFleetOrder.getTargetAITarget().getTargetID()) if not planet: clearAll =True elif aiFleetOrder.getAIFleetOrderType() == EnumsAI.AIFleetOrderType.ORDER_COLONISE : if ( (planet.currentMeterValue(fo.meterType.population) >0) or not ( planet.unowned or planet.ownedBy(fo.empireID()) ) ) : clearAll =True elif not planet.unowned: clearAll =True if clearAll: print "Fleet %d had a target planet that is no longer valid for this mission; aborting."%(self.getAITargetID() ) self.clearAIFleetOrders() self.clearAITargets(([-1]+ self.getAIMissionTypes()[:1])[-1]) FleetUtilsAI.splitFleet(self.getAITargetID() ) return self.checkMergers(context=str(aiFleetOrder)) if aiFleetOrder.canIssueOrder(verbose=True): #print " " + str(aiFleetOrder) currently already printed in canIssueOrder() if aiFleetOrder.getAIFleetOrderType() == EnumsAI.AIFleetOrderType.ORDER_MOVE and ordersCompleted: #only move if all other orders completed aiFleetOrder.issueOrder() elif aiFleetOrder.getAIFleetOrderType() not in [ EnumsAI.AIFleetOrderType.ORDER_MOVE, EnumsAI.AIFleetOrderType.ORDER_DEFEND]: aiFleetOrder.issueOrder() if not aiFleetOrder.isExecutionCompleted(): ordersCompleted = False else: #check that we're not held up by a Big Monster if aiFleetOrder.getAIFleetOrderType() == EnumsAI.AIFleetOrderType.ORDER_MOVE: thisSysID = aiFleetOrder.getTargetAITarget().getTargetID() thisStatus = foAI.foAIstate.systemStatus.setdefault(thisSysID, {}) if ( thisStatus.get('monsterThreat', 0) > fo.currentTurn() * ProductionAI.curBestMilShipRating()/4.0 ) : if ( ( (self.getAIMissionTypes() + [-1] )[0] not in [ EnumsAI.AIFleetMissionType.FLEET_MISSION_ATTACK, EnumsAI.AIFleetMissionType.FLEET_MISSION_MILITARY, EnumsAI.AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE, ]) or ( aiFleetOrder != self.getAIFleetOrders()[-1] ) # if this move order is not this mil fleet's final destination, and blocked by Big Monster, release and hope for more effective reassignment ): print "Aborting mission due to being blocked by Big Monster at system %d , threat %d"%(thisSysID, foAI.foAIstate.systemStatus[thisSysID]['monsterThreat']) print "Full set of orders were:" for aiFleetOrder2 in self.getAIFleetOrders(): print "\t\t %s"%aiFleetOrder2 self.clearAIFleetOrders() self.clearAITargets(([-1]+ self.getAIMissionTypes()[:1])[-1]) return # moving to another system stops issuing all orders in system where fleet is # move order is also the last order in system if aiFleetOrder.getAIFleetOrderType() == EnumsAI.AIFleetOrderType.ORDER_MOVE: fleet = fo.getUniverse().getFleet( self.getAITargetID() ) if fleet.systemID != aiFleetOrder.getTargetAITarget().getTargetID(): break else: #went through entire order list if ordersCompleted: orders=self.getAIFleetOrders() lastOrder= orders and orders[-1] universe=fo.getUniverse() if orders and lastOrder.getAIFleetOrderType() == EnumsAI.AIFleetOrderType.ORDER_COLONISE: planet = universe.getPlanet(lastOrder.getTargetAITarget().getTargetID()) pop=planet.currentMeterValue(fo.meterType.population) if pop==0: print "Fleet %d has tentatively completed its colonize mission but will wait to confirm population."%(self.getAITargetID() ) print " Order details are %s"%lastOrder print " Order is valid: %s ; is Executed : %s ; is execution completed: %s "%(lastOrder.isValid(), lastOrder.isExecuted(), lastOrder.isExecutionCompleted()) if not lastOrder.isValid(): sourceT = lastOrder.getSourceAITarget() targT = lastOrder.getTargetAITarget() print " source target validity: %s ; target target validity: %s "%(sourceT.isValid() , targT.isValid()) if EnumsAI.AITargetType.TARGET_SHIP == sourceT: shipID = sourceT.getTargetID() ship = universe.getShip(shipID) if not ship: print "Ship id %d not a valid ship id"%(shipID) print " source target Ship (%d), species %s, can%s colonize"%( shipID, ship.speciesName, ["not", ""][ship.canColonize]) return # colonize order must not have completed yet clearAll=True if orders and lastOrder.getAIFleetOrderType() == EnumsAI.AIFleetOrderType.ORDER_MILITARY: # if (AIFleetMissionType.FLEET_MISSION_SECURE in self.getAIMissionTypes()) or # not doing this until decide a way to release from a SECURE mission if (lastOrder.getTargetAITarget().getTargetID() in list(set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs))): #consider a secure mission print "Fleet %d has completed initial stage of its mission to secure system %d, may release a portion of ships"%(self.getAITargetID() , lastOrder.getTargetAITarget().getTargetID()) clearAll=False fleetID=self.getAITargetID() fleet=universe.getFleet(fleetID) if fleet.systemID != -1: loc = fleet.systemID else: loc=fleet.nextSystemID if clearAll: print "Fleet %d has completed its mission; clearing all orders and targets."%(self.getAITargetID() ) print "Full set of orders were:" for aiFleetOrder2 in self.getAIFleetOrders(): print "\t\t %s"%aiFleetOrder2 self.clearAIFleetOrders() self.clearAITargets(([-1]+ self.getAIMissionTypes()[:1])[-1]) if foAI.foAIstate.getFleetRole(fleetID) in [ EnumsAI.AIFleetMissionType.FLEET_MISSION_MILITARY, EnumsAI.AIFleetMissionType.FLEET_MISSION_ATTACK, EnumsAI.AIFleetMissionType.FLEET_MISSION_DEFEND, EnumsAI.AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE ]: allocations = MilitaryAI.getMilitaryFleets(milFleetIDs=[fleetID], tryReset=False, round="Fleet %d Reassignment"%fleetID) if allocations: MilitaryAI.assignMilitaryFleetsToSystems(useFleetIDList=[fleetID], allocations=allocations) else: #TODO: evaluate releasing a smaller portion or none of the ships newFleets=FleetUtilsAI.splitFleet(self.getAITargetID() ) #at least first stage of current task is done; release extra ships for potential other deployments newMilFleets = [] for fleetID in newFleets: if foAI.foAIstate.getFleetRole(fleetID) in [ EnumsAI.AIFleetMissionType.FLEET_MISSION_MILITARY, EnumsAI.AIFleetMissionType.FLEET_MISSION_ATTACK, EnumsAI.AIFleetMissionType.FLEET_MISSION_DEFEND, EnumsAI.AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE ]: newMilFleets.append(fleetID) allocations = MilitaryAI.getMilitaryFleets(milFleetIDs=newMilFleets, tryReset=False, round="Fleet Reassignment %s"%newMilFleets) if allocations: MilitaryAI.assignMilitaryFleetsToSystems(useFleetIDList=newMilFleets, allocations=allocations)
def generateOrders(): global lastTurnTimestamp universe = fo.getUniverse() turnStartTime=time() #starting AI timer here, to be sure AI doesn't get blame for any lags in server being able to provide the Universe object empire = fo.getEmpire() planetID = PlanetUtilsAI.getCapital() planet=None if planetID is not None: planet = universe.getPlanet(planetID) print "***************************************************************************" print "***************************************************************************" print ("Generating Orders") print "EmpireID: " + str(empire.empireID) + " Name: " + empire.name+ "_"+str(empire.empireID-1) +"_pid:"+str(fo.playerID())+"_"+fo.playerName()+"_"+aggressions.get(foAIstate.aggression, "?") + " Turn: " + str(fo.currentTurn()) empireColor=empire.colour print "EmpireColors: %d %d %d %d"%(empireColor.r, empireColor.g, empireColor.b, empireColor.a) if planet: print "CapitalID: " + str(planetID) + " Name: " + planet.name + " Species: " + planet.speciesName else: print "CapitalID: None Currently Name: None Species: None " print "***************************************************************************" print "***************************************************************************" if fo.currentTurn() == 1: declareWarOnAll() # turn cleanup !!! this was formerly done at start of every turn -- not sure why splitNewFleets() #updateShipDesigns() #should not be needed anymore; #updateFleetsRoles() foAIstate.clean() #checks exploration border & clears roles/missions of missing fleets & updates fleet locs foAIstate.reportSystemThreats() # ...missions # ...demands/priorities print("Calling AI Modules") # call AI modules timer=[time()] try: PriorityAI.calculatePriorities() except: print "Error: exception triggered and caught: ", traceback.format_exc() # try traceback.print_exc() timer.append( time() ) try: ExplorationAI.assignScoutsToExploreSystems() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) try: ColonisationAI.assignColonyFleetsToColonise() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) try: InvasionAI.assignInvasionFleetsToInvade() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) try: MilitaryAI.assignMilitaryFleetsToSystems() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) try: FleetUtilsAI.generateAIFleetOrdersForAIFleetMissions() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) try: FleetUtilsAI.issueAIFleetOrdersForAIFleetMissions() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) try: ResearchAI.generateResearchOrders() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) try: ProductionAI.generateProductionOrders() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) try: ResourcesAI.generateResourcesOrders() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) try: foAIstate.afterTurnCleanup() except: print "Error: exception triggered and caught: ", traceback.format_exc() timer.append( time() ) times = [timer[i] - timer[i-1] for i in range(1, len(timer) ) ] turnEndTime=time() timeFmt = "%30s: %8d msec " print "AI Module Time Requirements:" for mod, modTime in zip(__timerEntries, times): print timeFmt%((30*' '+mod)[-30:], int(1000*modTime)) if __timerFile: __timerFile.write( __timerFileFmt%tuple( [ fo.currentTurn() ]+map(lambda x: int(1000*x), times )) +'\n') __timerFile.flush() if __timerBucketFile: __timerBucketFile.write( __timerBucketFileFmt%tuple( [ fo.currentTurn(), (turnStartTime-lastTurnTimestamp)*1000, (turnEndTime-turnStartTime)*1000 ]) +'\n') __timerBucketFile.flush() lastTurnTimestamp = time() try: fo.doneTurn() except: print "Error: exception triggered and caught: ", traceback.format_exc()
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 can_issue_order(self, verbose=False): """If FleetOrder can be issued now.""" # for some orders, may need to re-issue if invasion/outposting/colonization was interrupted if self.executed and self.order_type not in [AIFleetOrderType.ORDER_OUTPOST, AIFleetOrderType.ORDER_COLONISE, AIFleetOrderType.ORDER_INVADE]: return False if not self.is_valid(): return False universe = fo.getUniverse() ship_id = None fleet_id = self.fleet.target_id fleet = universe.getFleet(fleet_id) system_id = fleet.systemID target_id = self.target.target_id if verbose: sys1 = universe.getSystem(system_id) sys_name = sys1 and sys1.name or "unknown" main_fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id) main_mission_type = (main_fleet_mission.get_mission_types() + [-1])[0] print "** %s -- Mission Type %s (%s) , current loc sys %d - %s" % (self, AIFleetMissionTypeNames.name(main_mission_type), main_mission_type, system_id, sys_name) # # outpost # if AIFleetOrderType.ORDER_OUTPOST == self.order_type: # TODO: check for separate fleet holding outpost ships ship_id = FleetUtilsAI.get_ship_id_with_role(fleet_id, AIShipRoleType.SHIP_ROLE_CIVILIAN_OUTPOST) if ship_id is None: ship_id = FleetUtilsAI.get_ship_id_with_role(fleet_id, AIShipRoleType.SHIP_ROLE_BASE_OUTPOST) ship = universe.getShip(ship_id) planet = universe.getPlanet(self.target.target_id) return ship is not None and fleet.systemID == planet.systemID and ship.canColonize # # colonise # elif AIFleetOrderType.ORDER_COLONISE == self.order_type: # TODO: check for separate fleet holding colony ships if TargetType.TARGET_FLEET == self.fleet.target_type: ship_id = FleetUtilsAI.get_ship_id_with_role(fleet_id, AIShipRoleType.SHIP_ROLE_CIVILIAN_COLONISATION) if ship_id is None: ship_id = FleetUtilsAI.get_ship_id_with_role(fleet_id, AIShipRoleType.SHIP_ROLE_BASE_COLONISATION) ship = universe.getShip(ship_id) planet = universe.getPlanet(self.target.target_id) if ship and not ship.canColonize: print "Error: colonization fleet %d has no colony ship" % fleet_id return ship is not None and fleet.systemID == planet.systemID and ship.canColonize # # invade # elif AIFleetOrderType.ORDER_INVADE == self.order_type: # TODO: check for separate fleet holding invasion ships if TargetType.TARGET_FLEET == self.fleet.target_type: ship_id = FleetUtilsAI.get_ship_id_with_role(fleet_id, AIShipRoleType.SHIP_ROLE_MILITARY_INVASION, False) if ship_id is None: ship_id = FleetUtilsAI.get_ship_id_with_role(fleet_id, AIShipRoleType.SHIP_ROLE_BASE_INVASION) ship = universe.getShip(ship_id) planet = universe.getPlanet(self.target.target_id) return ship is not None and fleet.systemID == planet.systemID and ship.canInvade and not planet.currentMeterValue(fo.meterType.shield) # # military # elif AIFleetOrderType.ORDER_MILITARY == self.order_type: if TargetType.TARGET_FLEET == self.fleet.target_type: ship_id = FleetUtilsAI.get_ship_id_with_role(fleet_id, AIShipRoleType.SHIP_ROLE_MILITARY) ship = universe.getShip(ship_id) system = universe.getSystem(self.target.target_id) return ship is not None and fleet.systemID == system.systemID and ship.isArmed # # split fleet # elif AIFleetOrderType.ORDER_SPLIT_FLEET == self.order_type: return len(fleet.shipIDs) > 1 # # move -- have fleets will do a safety check, also check for potential military fleet mergers # elif AIFleetOrderType.ORDER_MOVE == self.order_type: # target_id = self.target.target_id # TODO: figure out better way to have invasions (& possibly colonizations) require visibility on target without needing visibility of all intermediate systems if False and main_mission_type not in [AIFleetMissionType.FLEET_MISSION_ATTACK, # TODO: consider this later AIFleetMissionType.FLEET_MISSION_MILITARY, AIFleetMissionType.FLEET_MISSION_SECURE, AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, AIFleetMissionType.FLEET_MISSION_EXPLORATION]: if not universe.getVisibility(target_id, foAI.foAIstate.empireID) >= fo.visibility.partial: #if not target_id in interior systems foAI.foAIstate.needsEmergencyExploration.append(fleet.systemID) return False system_id = fleet.systemID if system_id == target_id: return True # TODO: already there, but could consider retreating fleet_rating = foAI.foAIstate.get_rating(fleet_id).get('overall', 0) target_sys_status = foAI.foAIstate.systemStatus.get(target_id, {}) f_threat = target_sys_status.get('fleetThreat', 0) m_threat = target_sys_status.get('monsterThreat', 0) p_threat = target_sys_status.get('planetThreat', 0) threat = f_threat + m_threat + p_threat safety_factor = MilitaryAI.get_safety_factor() if fleet_rating >= safety_factor * threat: return True else: sys1 = universe.getSystem(system_id) sys1_name = sys1 and sys1.name or "unknown" targ1 = universe.getSystem(target_id) targ1_name = (targ1 and targ1.name) or "unknown" # following line was poor because AIstate.militaryFleetIDs only covers fleets without current missions # my_other_fleet_rating = sum([foAI.foAIstate.fleetStatus.get(fleet_id, {}).get('rating', 0) for fleet_id in foAI.foAIstate.militaryFleetIDs if ( foAI.foAIstate.fleetStatus.get(fleet_id, {}).get('sysID', -1) == thisSystemID ) ]) # myOtherFleetsRatings = [foAI.foAIstate.fleetStatus.get(fid, {}).get('rating', {}) for fid in foAI.foAIstate.systemStatus.get(target_id, {}).get('myfleets', [])] # my_other_fleet_rating = sum([foAI.foAIstate.fleetStatus.get(fid, {}).get('rating', 0) for fid in foAI.foAIstate.systemStatus.get( target_id, {}).get('myfleets', []) ]) my_other_fleet_rating = foAI.foAIstate.systemStatus.get(target_id, {}).get('myFleetRating', 0) # TODO: adjust calc for any departing fleets is_military = foAI.foAIstate.get_fleet_role(fleet_id) == AIFleetMissionType.FLEET_MISSION_MILITARY if ((my_other_fleet_rating > 3 * safety_factor * threat) or (is_military and my_other_fleet_rating + fleet_rating > safety_factor * threat) or (is_military and my_other_fleet_rating + fleet_rating > 0.8 * safety_factor * threat and fleet_rating > 0.2 * threat)): if verbose: print "\tAdvancing fleet %d (rating %d) at system %d (%s) into system %d (%s) with threat %d because of sufficient empire fleet strength already at destination" % (fleet_id, fleet_rating, system_id, sys1_name, target_id, targ1_name, threat) return True elif threat == p_threat and not fleet.aggressive and not my_other_fleet_rating and not target_sys_status.get('localEnemyFleetIDs', [-1]): if verbose: print ("\tAdvancing fleet %d (rating %d) at system %d (%s) into system %d (%s) with planet threat %d because nonaggressive" + " and no other fleets present to trigger combat") % (fleet_id, fleet_rating, system_id, sys1_name, target_id, targ1_name, threat) return True else: if verbose: print "\tHolding fleet %d (rating %d) at system %d (%s) before travelling to system %d (%s) with threat %d" % (fleet_id, fleet_rating, system_id, sys1_name, target_id, targ1_name, threat) return False else: # default returns true return True
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)): warn("Fleet %d has tentatively completed its " "colonize mission but will wait to confirm population." % self.fleet.id) 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 = CombatRatingsAI.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") 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 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 evaluate_invasion_planet(planet_id, secure_fleet_missions, verbose=True): """Return the invasion value (score, troops) of a planet.""" detail = [] building_values = {"BLD_IMPERIAL_PALACE": 1000, "BLD_CULTURE_ARCHIVES": 1000, "BLD_AUTO_HISTORY_ANALYSER": 100, "BLD_SHIPYARD_BASE": 100, "BLD_SHIPYARD_ORG_ORB_INC": 200, "BLD_SHIPYARD_ORG_XENO_FAC": 200, "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200, "BLD_SHIPYARD_CON_NANOROBO": 300, "BLD_SHIPYARD_CON_GEOINT": 400, "BLD_SHIPYARD_CON_ADV_ENGINE": 1000, "BLD_SHIPYARD_AST": 300, "BLD_SHIPYARD_AST_REF": 1000, "BLD_SHIPYARD_ENRG_SOLAR": 1500, "BLD_INDUSTRY_CENTER": 500, "BLD_GAS_GIANT_GEN": 200, "BLD_SOL_ORB_GEN": 800, "BLD_BLACK_HOLE_POW_GEN": 2000, "BLD_ENCLAVE_VOID": 500, "BLD_NEUTRONIUM_EXTRACTOR": 2000, "BLD_NEUTRONIUM_SYNTH": 2000, "BLD_NEUTRONIUM_FORGE": 1000, "BLD_CONC_CAMP": 100, "BLD_BIOTERROR_PROJECTOR": 1000, "BLD_SHIPYARD_ENRG_COMP": 3000, } # TODO: add more factors, as used for colonization universe = fo.getUniverse() empire_id = fo.empireID() max_jumps = 8 planet = universe.getPlanet(planet_id) if planet is None: # TODO: exclude planets with stealth higher than empireDetection print "invasion AI couldn't access any info for planet id %d" % planet_id return [0, 0] sys_partial_vis_turn = universe.getVisibilityTurnsMap(planet.systemID, empire_id).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(planet_id, empire_id).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: print "invasion AI couldn't get current info on planet id %d (was stealthed at last sighting)" % planet_id # TODO: track detection strength, order new scouting when it goes up return [0, 0] # last time we had partial vis of the system, the planet was stealthed to us species_name = planet.speciesName species = fo.getSpecies(species_name) if not species or AIDependencies.TAG_DESTROYED_ON_CONQUEST in species.tags: # this call iterates over this Empire's available species with which it could colonize after an invasion planet_eval = ColonisationAI.assign_colonisation_values([planet_id], MissionType.INVASION, None, detail) pop_val = max(0.75 * planet_eval.get(planet_id, [0])[0], ColonisationAI.evaluate_planet(planet_id, MissionType.OUTPOST, None, detail)) else: pop_val = ColonisationAI.evaluate_planet(planet_id, MissionType.INVASION, species_name, detail) bld_tally = 0 for bldType in [universe.getBuilding(bldg).buildingTypeName for bldg in planet.buildingIDs]: bval = building_values.get(bldType, 50) bld_tally += bval detail.append("%s: %d" % (bldType, bval)) tech_tally = 0 for unlocked_tech in AIDependencies.SPECIES_TECH_UNLOCKS.get(species_name, []): if not tech_is_complete(unlocked_tech): rp_cost = fo.getTech(unlocked_tech).researchCost(empire_id) tech_tally += rp_cost * 4 detail.append("%s: %d" % (unlocked_tech, rp_cost * 4)) p_sys_id = planet.systemID capitol_id = PlanetUtilsAI.get_capital() least_jumps_path = [] clear_path = True if capitol_id: homeworld = universe.getPlanet(capitol_id) if homeworld: home_system_id = homeworld.systemID eval_system_id = planet.systemID if (home_system_id != INVALID_ID) and (eval_system_id != INVALID_ID): least_jumps_path = list(universe.leastJumpsPath(home_system_id, eval_system_id, empire_id)) max_jumps = len(least_jumps_path) system_status = foAI.foAIstate.systemStatus.get(p_sys_id, {}) system_fleet_treat = system_status.get('fleetThreat', 1000) system_monster_threat = system_status.get('monsterThreat', 0) sys_total_threat = system_fleet_treat + system_monster_threat + system_status.get('planetThreat', 0) max_path_threat = system_fleet_treat mil_ship_rating = MilitaryAI.cur_best_mil_ship_rating() for path_sys_id in least_jumps_path: path_leg_status = foAI.foAIstate.systemStatus.get(path_sys_id, {}) path_leg_threat = path_leg_status.get('fleetThreat', 1000) + path_leg_status.get('monsterThreat', 0) if path_leg_threat > 0.5 * mil_ship_rating: clear_path = False if path_leg_threat > max_path_threat: max_path_threat = path_leg_threat pop = planet.currentMeterValue(fo.meterType.population) target_pop = planet.currentMeterValue(fo.meterType.targetPopulation) troops = planet.currentMeterValue(fo.meterType.troops) max_troops = planet.currentMeterValue(fo.meterType.maxTroops) # TODO: refactor troop determination into function for use in mid-mission updates and also consider defender techs max_troops += AIDependencies.TROOPS_PER_POP * (target_pop - pop) this_system = universe.getSystem(p_sys_id) secure_targets = [p_sys_id] + list(this_system.planetIDs) system_secured = False for mission in secure_fleet_missions: if system_secured: break secure_fleet_id = mission.fleet.id s_fleet = universe.getFleet(secure_fleet_id) if not s_fleet or s_fleet.systemID != p_sys_id: continue if mission.type == MissionType.SECURE: target_obj = mission.target.get_object() if target_obj is not None and target_obj.id in secure_targets: system_secured = True break system_secured = system_secured and system_status.get('myFleetRating', 0) if verbose: print ("Invasion eval of %s\n" " - maxShields: %.1f\n" " - sysFleetThreat: %.1f\n" " - sysMonsterThreat: %.1f") % ( planet, planet.currentMeterValue(fo.meterType.maxShield), system_fleet_treat, system_monster_threat) supply_val = 0 enemy_val = 0 if planet.owner != -1: # value in taking this away from an enemy enemy_val = 20 * (planet.currentMeterValue(fo.meterType.targetIndustry) + 2*planet.currentMeterValue(fo.meterType.targetResearch)) if p_sys_id in ColonisationAI.annexable_system_ids: # TODO: extend to rings supply_val = 100 elif p_sys_id in ColonisationAI.annexable_ring1: supply_val = 200 elif p_sys_id in ColonisationAI.annexable_ring2: supply_val = 300 elif p_sys_id in ColonisationAI.annexable_ring3: supply_val = 400 if max_path_threat > 0.5 * mil_ship_rating: if max_path_threat < 3 * mil_ship_rating: supply_val *= 0.5 else: supply_val *= 0.2 threat_factor = min(1, 0.2*MilitaryAI.get_tot_mil_rating()/(sys_total_threat+0.001))**2 # devalue invasions that would require too much military force design_id, _, locs = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_INVASION) if not locs or not universe.getPlanet(locs[0]): # We are in trouble anyway, so just calculate whatever approximation... build_time = 4 planned_troops = troops if system_secured else min(troops + max_jumps + build_time, max_troops) planned_troops += .01 # we must attack with more troops than there are defenders troop_cost = math.ceil((planned_troops+_TROOPS_SAFETY_MARGIN) / 6.0) * 20 * FleetUtilsAI.get_fleet_upkeep() else: loc = locs[0] species_here = universe.getPlanet(loc).speciesName design = fo.getShipDesign(design_id) cost_per_ship = design.productionCost(empire_id, loc) build_time = design.productionTime(empire_id, loc) troops_per_ship = CombatRatingsAI.weight_attack_troops(design.troopCapacity, CombatRatingsAI.get_species_troops_grade(species_here)) planned_troops = troops if system_secured else min(troops + max_jumps + build_time, max_troops) planned_troops += .01 # we must attack with more troops than there are defenders ships_needed = math.ceil((planned_troops+_TROOPS_SAFETY_MARGIN) / float(troops_per_ship)) troop_cost = ships_needed * cost_per_ship # fleet upkeep is already included in query from server # apply some bias to expensive operations normalized_cost = float(troop_cost) / max(fo.getEmpire().productionPoints, 1) normalized_cost = max(1, normalized_cost) cost_score = (normalized_cost**2 / 50.0) * troop_cost base_score = pop_val + supply_val + bld_tally + tech_tally + enemy_val - cost_score planet_score = retaliation_risk_factor(planet.owner) * threat_factor * max(0, base_score) if clear_path: planet_score *= 1.5 if verbose: print (' - planet score: %.2f\n' ' - troop score: %.2f\n' ' - projected troop cost: %.1f\n' ' - threat factor: %s\n' ' - planet detail: %s\n' ' - popval: %.1f\n' ' - supplyval: %.1f\n' ' - bldval: %s\n' ' - enemyval: %s') % (planet_score, planned_troops, troop_cost, threat_factor, detail, pop_val, supply_val, bld_tally, enemy_val) return [planet_score, planned_troops]
def evaluate_invasion_planet(planet_id, secure_fleet_missions, verbose=True): """Return the invasion value (score, troops) of a planet.""" universe = fo.getUniverse() empire_id = fo.empireID() detail = [] planet = universe.getPlanet(planet_id) if planet is None: debug("Invasion AI couldn't access any info for planet id %d" % planet_id) return [0, 0] system_id = planet.systemID # by using the following instead of simply relying on stealth meter reading, can (sometimes) plan ahead even if # planet is temporarily shrouded by an ion storm predicted_detectable = EspionageAI.colony_detectable_by_empire(planet_id, empire=fo.empireID(), default_result=False) if not predicted_detectable: if get_partial_visibility_turn(planet_id) < fo.currentTurn(): debug("InvasionAI predicts planet id %d to be stealthed" % planet_id) return [0, 0] else: debug("InvasionAI predicts planet id %d to be stealthed" % planet_id + ", but somehow have current visibity anyway, will still consider as target") # Check if the target planet was extra-stealthed somehow its system was last viewed # this test below may augment the tests above, but can be thrown off by temporary combat-related sighting system_last_seen = get_partial_visibility_turn(planet_id) planet_last_seen = get_partial_visibility_turn(system_id) if planet_last_seen < system_last_seen: # TODO: track detection strength, order new scouting when it goes up debug("Invasion AI considering planet id %d (stealthed at last view), still proceeding." % planet_id) # get a baseline evaluation of the planet as determined by ColonisationAI species_name = planet.speciesName species = fo.getSpecies(species_name) if not species or AIDependencies.TAG_DESTROYED_ON_CONQUEST in species.tags: # this call iterates over this Empire's available species with which it could colonize after an invasion planet_eval = ColonisationAI.assign_colonisation_values([planet_id], MissionType.INVASION, None, detail) colony_base_value = max(0.75 * planet_eval.get(planet_id, [0])[0], ColonisationAI.evaluate_planet(planet_id, MissionType.OUTPOST, None, detail)) else: colony_base_value = ColonisationAI.evaluate_planet(planet_id, MissionType.INVASION, species_name, detail) # Add extra score for all buildings on the planet building_values = {"BLD_IMPERIAL_PALACE": 1000, "BLD_CULTURE_ARCHIVES": 1000, "BLD_AUTO_HISTORY_ANALYSER": 100, "BLD_SHIPYARD_BASE": 100, "BLD_SHIPYARD_ORG_ORB_INC": 200, "BLD_SHIPYARD_ORG_XENO_FAC": 200, "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200, "BLD_SHIPYARD_CON_NANOROBO": 300, "BLD_SHIPYARD_CON_GEOINT": 400, "BLD_SHIPYARD_CON_ADV_ENGINE": 1000, "BLD_SHIPYARD_AST": 300, "BLD_SHIPYARD_AST_REF": 1000, "BLD_SHIPYARD_ENRG_SOLAR": 1500, "BLD_INDUSTRY_CENTER": 500, "BLD_GAS_GIANT_GEN": 200, "BLD_SOL_ORB_GEN": 800, "BLD_BLACK_HOLE_POW_GEN": 2000, "BLD_ENCLAVE_VOID": 500, "BLD_NEUTRONIUM_EXTRACTOR": 2000, "BLD_NEUTRONIUM_SYNTH": 2000, "BLD_NEUTRONIUM_FORGE": 1000, "BLD_CONC_CAMP": 100, "BLD_BIOTERROR_PROJECTOR": 1000, "BLD_SHIPYARD_ENRG_COMP": 3000, } bld_tally = 0 for bldType in [universe.getBuilding(bldg).buildingTypeName for bldg in planet.buildingIDs]: bval = building_values.get(bldType, 50) bld_tally += bval detail.append("%s: %d" % (bldType, bval)) # Add extra score for unlocked techs when we conquer the species tech_tally = 0 value_per_pp = 4 for unlocked_tech in AIDependencies.SPECIES_TECH_UNLOCKS.get(species_name, []): if not tech_is_complete(unlocked_tech): rp_cost = fo.getTech(unlocked_tech).researchCost(empire_id) tech_value = value_per_pp * rp_cost tech_tally += tech_value detail.append("%s: %d" % (unlocked_tech, tech_value)) max_jumps = 8 capitol_id = PlanetUtilsAI.get_capital() least_jumps_path = [] clear_path = True if capitol_id: homeworld = universe.getPlanet(capitol_id) if homeworld and homeworld.systemID != INVALID_ID and system_id != INVALID_ID: least_jumps_path = list(universe.leastJumpsPath(homeworld.systemID, system_id, empire_id)) max_jumps = len(least_jumps_path) aistate = get_aistate() system_status = aistate.systemStatus.get(system_id, {}) system_fleet_treat = system_status.get('fleetThreat', 1000) system_monster_threat = system_status.get('monsterThreat', 0) sys_total_threat = system_fleet_treat + system_monster_threat + system_status.get('planetThreat', 0) max_path_threat = system_fleet_treat mil_ship_rating = MilitaryAI.cur_best_mil_ship_rating() for path_sys_id in least_jumps_path: path_leg_status = aistate.systemStatus.get(path_sys_id, {}) path_leg_threat = path_leg_status.get('fleetThreat', 1000) + path_leg_status.get('monsterThreat', 0) if path_leg_threat > 0.5 * mil_ship_rating: clear_path = False if path_leg_threat > max_path_threat: max_path_threat = path_leg_threat pop = planet.currentMeterValue(fo.meterType.population) target_pop = planet.currentMeterValue(fo.meterType.targetPopulation) troops = planet.currentMeterValue(fo.meterType.troops) troop_regen = planet.currentMeterValue(fo.meterType.troops) - planet.initialMeterValue(fo.meterType.troops) max_troops = planet.currentMeterValue(fo.meterType.maxTroops) # TODO: refactor troop determination into function for use in mid-mission updates and also consider defender techs max_troops += AIDependencies.TROOPS_PER_POP * (target_pop - pop) this_system = universe.getSystem(system_id) secure_targets = [system_id] + list(this_system.planetIDs) system_secured = False for mission in secure_fleet_missions: if system_secured: break secure_fleet_id = mission.fleet.id s_fleet = universe.getFleet(secure_fleet_id) if not s_fleet or s_fleet.systemID != system_id: continue if mission.type in [MissionType.SECURE, MissionType.MILITARY]: target_obj = mission.target.get_object() if target_obj is not None and target_obj.id in secure_targets: system_secured = True break system_secured = system_secured and system_status.get('myFleetRating', 0) if verbose: debug("Invasion eval of %s\n" " - maxShields: %.1f\n" " - sysFleetThreat: %.1f\n" " - sysMonsterThreat: %.1f", planet, planet.currentMeterValue(fo.meterType.maxShield), system_fleet_treat, system_monster_threat) enemy_val = 0 if planet.owner != -1: # value in taking this away from an enemy enemy_val = 20 * (planet.currentMeterValue(fo.meterType.targetIndustry) + 2*planet.currentMeterValue(fo.meterType.targetResearch)) # devalue invasions that would require too much military force preferred_max_portion = MilitaryAI.get_preferred_max_military_portion_for_single_battle() total_max_mil_rating = MilitaryAI.get_concentrated_tot_mil_rating() threat_exponent = 2 # TODO: make this a character trait; higher aggression with a lower exponent threat_factor = min(1, preferred_max_portion * total_max_mil_rating/(sys_total_threat+0.001))**threat_exponent design_id, _, locs = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_INVASION) if not locs or not universe.getPlanet(locs[0]): # We are in trouble anyway, so just calculate whatever approximation... build_time = 4 planned_troops = troops if system_secured else min(troops + troop_regen*(max_jumps + build_time), max_troops) planned_troops += .01 # we must attack with more troops than there are defenders troop_cost = math.ceil((planned_troops+_TROOPS_SAFETY_MARGIN) / 6.0) * 20 * FleetUtilsAI.get_fleet_upkeep() else: loc = locs[0] species_here = universe.getPlanet(loc).speciesName design = fo.getShipDesign(design_id) cost_per_ship = design.productionCost(empire_id, loc) build_time = design.productionTime(empire_id, loc) troops_per_ship = CombatRatingsAI.weight_attack_troops(design.troopCapacity, CombatRatingsAI.get_species_troops_grade(species_here)) planned_troops = troops if system_secured else min(troops + troop_regen*(max_jumps + build_time), max_troops) planned_troops += .01 # we must attack with more troops than there are defenders ships_needed = math.ceil((planned_troops+_TROOPS_SAFETY_MARGIN) / float(troops_per_ship)) troop_cost = ships_needed * cost_per_ship # fleet upkeep is already included in query from server # apply some bias to expensive operations normalized_cost = float(troop_cost) / max(fo.getEmpire().productionPoints, 1) normalized_cost = max(1., normalized_cost) cost_score = (normalized_cost**2 / 50.0) * troop_cost base_score = colony_base_value + bld_tally + tech_tally + enemy_val - cost_score # If the AI does have enough total miltary to attack this target, and the target is more than minimally valuable, # don't let the threat_factor discount the adjusted value below MIN_INVASION_SCORE +1, so that if there are no # other targets the AI could still pursue this one. Otherwise, scoring pressure from # MilitaryAI.get_preferred_max_military_portion_for_single_battle might prevent the AI from attacking heavily # defended but still defeatable targets even if it has no softer targets available. if total_max_mil_rating > sys_total_threat and base_score > 2 * MIN_INVASION_SCORE: threat_factor = max(threat_factor, (MIN_INVASION_SCORE + 1)/base_score) planet_score = retaliation_risk_factor(planet.owner) * threat_factor * max(0, base_score) if clear_path: planet_score *= 1.5 if verbose: debug(' - planet score: %.2f\n' ' - planned troops: %.2f\n' ' - projected troop cost: %.1f\n' ' - threat factor: %s\n' ' - planet detail: %s\n' ' - popval: %.1f\n' ' - bldval: %s\n' ' - enemyval: %s', planet_score, planned_troops, troop_cost, threat_factor, detail, colony_base_value, bld_tally, enemy_val) debug(' - system secured: %s' % system_secured) return [planet_score, planned_troops]
def evaluate_invasion_planet(planet_id): """Return the invasion value (score, troops) of a planet.""" universe = fo.getUniverse() empire_id = fo.empireID() detail = [] planet = universe.getPlanet(planet_id) if planet is None: debug("Invasion AI couldn't access any info for planet id %d" % planet_id) return [0, 0] system_id = planet.systemID # by using the following instead of simply relying on stealth meter reading, # can (sometimes) plan ahead even if planet is temporarily shrouded by an ion storm predicted_detectable = EspionageAI.colony_detectable_by_empire( planet_id, empire=fo.empireID(), default_result=False) if not predicted_detectable: if get_partial_visibility_turn(planet_id) < fo.currentTurn(): debug("InvasionAI predicts planet id %d to be stealthed" % planet_id) return [0, 0] else: debug( "InvasionAI predicts planet id %d to be stealthed" % planet_id + ", but somehow have current visibility anyway, will still consider as target" ) # Check if the target planet was extra-stealthed somehow its system was last viewed # this test below may augment the tests above, # but can be thrown off by temporary combat-related sighting system_last_seen = get_partial_visibility_turn(planet_id) planet_last_seen = get_partial_visibility_turn(system_id) if planet_last_seen < system_last_seen: # TODO: track detection strength, order new scouting when it goes up debug( "Invasion AI considering planet id %d (stealthed at last view), still proceeding." % planet_id) # get a baseline evaluation of the planet as determined by ColonisationAI species_name = planet.speciesName species = fo.getSpecies(species_name) empire_research_list = tuple(element.tech for element in fo.getEmpire().researchQueue) if not species or AIDependencies.TAG_DESTROYED_ON_CONQUEST in species.tags: # this call iterates over this Empire's available species with which it could colonize after an invasion planet_eval = ColonisationAI.assign_colonisation_values( [planet_id], MissionType.INVASION, None, detail) colony_base_value = max( 0.75 * planet_eval.get(planet_id, [0])[0], calculate_planet_colonization_rating. calculate_planet_colonization_rating( planet_id=planet_id, mission_type=MissionType.OUTPOST, spec_name=None, detail=detail, empire_research_list=empire_research_list, ), ) else: colony_base_value = calculate_planet_colonization_rating.calculate_planet_colonization_rating( planet_id=planet_id, mission_type=MissionType.INVASION, spec_name=species_name, detail=detail, empire_research_list=empire_research_list, ) # Add extra score for all buildings on the planet building_values = { "BLD_IMPERIAL_PALACE": 1000, "BLD_CULTURE_ARCHIVES": 1000, "BLD_AUTO_HISTORY_ANALYSER": 100, "BLD_SHIPYARD_BASE": 100, "BLD_SHIPYARD_ORG_ORB_INC": 200, "BLD_SHIPYARD_ORG_XENO_FAC": 200, "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200, "BLD_SHIPYARD_CON_NANOROBO": 300, "BLD_SHIPYARD_CON_GEOINT": 400, "BLD_SHIPYARD_CON_ADV_ENGINE": 1000, "BLD_SHIPYARD_AST": 300, "BLD_SHIPYARD_AST_REF": 1000, "BLD_SHIPYARD_ENRG_SOLAR": 1500, "BLD_INDUSTRY_CENTER": 500, "BLD_GAS_GIANT_GEN": 200, "BLD_SOL_ORB_GEN": 800, "BLD_BLACK_HOLE_POW_GEN": 2000, "BLD_ENCLAVE_VOID": 500, "BLD_NEUTRONIUM_EXTRACTOR": 2000, "BLD_NEUTRONIUM_SYNTH": 2000, "BLD_NEUTRONIUM_FORGE": 1000, "BLD_CONC_CAMP": 100, "BLD_BIOTERROR_PROJECTOR": 1000, "BLD_SHIPYARD_ENRG_COMP": 3000, } bld_tally = 0 for bldType in [ universe.getBuilding(bldg).buildingTypeName for bldg in planet.buildingIDs ]: bval = building_values.get(bldType, 50) bld_tally += bval detail.append("%s: %d" % (bldType, bval)) # Add extra score for unlocked techs when we conquer the species tech_tally = 0 value_per_pp = 4 for unlocked_tech in AIDependencies.SPECIES_TECH_UNLOCKS.get( species_name, []): if not tech_is_complete(unlocked_tech): rp_cost = fo.getTech(unlocked_tech).researchCost(empire_id) tech_value = value_per_pp * rp_cost tech_tally += tech_value detail.append("%s: %d" % (unlocked_tech, tech_value)) least_jumps_path, max_jumps = _get_path_from_capital(planet) clear_path = True aistate = get_aistate() system_status = aistate.systemStatus.get(system_id, {}) system_fleet_treat = system_status.get("fleetThreat", 1000) system_monster_threat = system_status.get("monsterThreat", 0) sys_total_threat = system_fleet_treat + system_monster_threat + system_status.get( "planetThreat", 0) max_path_threat = system_fleet_treat mil_ship_rating = MilitaryAI.cur_best_mil_ship_rating() for path_sys_id in least_jumps_path: path_leg_status = aistate.systemStatus.get(path_sys_id, {}) path_leg_threat = path_leg_status.get( "fleetThreat", 1000) + path_leg_status.get("monsterThreat", 0) if path_leg_threat > 0.5 * mil_ship_rating: clear_path = False if path_leg_threat > max_path_threat: max_path_threat = path_leg_threat pop = planet.currentMeterValue(fo.meterType.population) target_pop = planet.currentMeterValue(fo.meterType.targetPopulation) troops = planet.currentMeterValue(fo.meterType.troops) troop_regen = planet.currentMeterValue( fo.meterType.troops) - planet.initialMeterValue(fo.meterType.troops) max_troops = planet.currentMeterValue(fo.meterType.maxTroops) # TODO: refactor troop determination into function for use in mid-mission updates and also consider defender techs max_troops += AIDependencies.TROOPS_PER_POP * (target_pop - pop) this_system = universe.getSystem(system_id) secure_targets = [system_id] + list(this_system.planetIDs) system_secured = False secure_fleets = get_aistate().get_fleet_missions_with_any_mission_types( [MissionType.SECURE, MissionType.MILITARY]) for mission in secure_fleets: secure_fleet_id = mission.fleet.id s_fleet = universe.getFleet(secure_fleet_id) if not s_fleet or s_fleet.systemID != system_id: continue if mission.type in [MissionType.SECURE, MissionType.MILITARY]: target_obj = mission.target.get_object() if target_obj is not None and target_obj.id in secure_targets: system_secured = True break system_secured = system_secured and system_status.get("myFleetRating", 0) debug( "Invasion eval of %s\n" " - maxShields: %.1f\n" " - sysFleetThreat: %.1f\n" " - sysMonsterThreat: %.1f", planet, planet.currentMeterValue(fo.meterType.maxShield), system_fleet_treat, system_monster_threat, ) enemy_val = 0 if planet.owner != -1: # value in taking this away from an enemy enemy_val = 20 * ( planet.currentMeterValue(fo.meterType.targetIndustry) + 2 * planet.currentMeterValue(fo.meterType.targetResearch)) # devalue invasions that would require too much military force preferred_max_portion = MilitaryAI.get_preferred_max_military_portion_for_single_battle( ) total_max_mil_rating = MilitaryAI.get_concentrated_tot_mil_rating() threat_exponent = 2 # TODO: make this a character trait; higher aggression with a lower exponent threat_factor = min( 1, preferred_max_portion * total_max_mil_rating / (sys_total_threat + 0.001))**threat_exponent design_id, _, locs = get_best_ship_info(PriorityType.PRODUCTION_INVASION) if not locs or not universe.getPlanet(locs[0]): # We are in trouble anyway, so just calculate whatever approximation... build_time = 4 planned_troops = troops if system_secured else min( troops + troop_regen * (max_jumps + build_time), max_troops) planned_troops += 0.01 # we must attack with more troops than there are defenders troop_cost = math.ceil((planned_troops + _TROOPS_SAFETY_MARGIN) / 6.0) * 20 * FleetUtilsAI.get_fleet_upkeep() else: loc = locs[0] species_here = universe.getPlanet(loc).speciesName design = fo.getShipDesign(design_id) cost_per_ship = design.productionCost(empire_id, loc) build_time = design.productionTime(empire_id, loc) troops_per_ship = CombatRatingsAI.weight_attack_troops( design.troopCapacity, get_species_tag_grade(species_here, Tags.ATTACKTROOPS)) planned_troops = troops if system_secured else min( troops + troop_regen * (max_jumps + build_time), max_troops) planned_troops += 0.01 # we must attack with more troops than there are defenders ships_needed = math.ceil( (planned_troops + _TROOPS_SAFETY_MARGIN) / float(troops_per_ship)) troop_cost = ships_needed * cost_per_ship # fleet upkeep is already included in query from server # apply some bias to expensive operations normalized_cost = float(troop_cost) / max(fo.getEmpire().productionPoints, 1) normalized_cost = max(1.0, normalized_cost) cost_score = (normalized_cost**2 / 50.0) * troop_cost base_score = colony_base_value + bld_tally + tech_tally + enemy_val - cost_score # If the AI does have enough total military to attack this target, and the target is more than minimally valuable, # don't let the threat_factor discount the adjusted value below MIN_INVASION_SCORE +1, so that if there are no # other targets the AI could still pursue this one. Otherwise, scoring pressure from # MilitaryAI.get_preferred_max_military_portion_for_single_battle might prevent the AI from attacking heavily # defended but still defeatable targets even if it has no softer targets available. if total_max_mil_rating > sys_total_threat and base_score > 2 * MIN_INVASION_SCORE: threat_factor = max(threat_factor, (MIN_INVASION_SCORE + 1) / base_score) planet_score = retaliation_risk_factor(planet.owner) * threat_factor * max( 0, base_score) if clear_path: planet_score *= 1.5 debug( " - planet score: %.2f\n" " - planned troops: %.2f\n" " - projected troop cost: %.1f\n" " - threat factor: %s\n" " - planet detail: %s\n" " - popval: %.1f\n" " - bldval: %s\n" " - enemyval: %s", planet_score, planned_troops, troop_cost, threat_factor, detail, colony_base_value, bld_tally, enemy_val, ) debug(" - system secured: %s" % system_secured) return [planet_score, planned_troops]
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 print print "Checking orders for fleet %s (on turn %d), with mission type %s" % ( self.fleet.get_object(), fo.currentTurn(), self.type or 'No mission') 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 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 print "Checking order: %s" % fleet_order self.check_mergers(context=str(fleet_order)) if fleet_order.can_issue_order(verbose=False): if isinstance(fleet_order, OrderMove) and order_completed: # only move if all other orders completed print "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): print "Issuing fleet order %s" % fleet_order fleet_order.issue_order() else: print "NOT issuing (even though can_issue) fleet order %s" % fleet_order print "Order issued: %s" % fleet_order.order_issued 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 print "CAN'T issue fleet order %s" % fleet_order if isinstance(fleet_order, OrderMove): this_system_id = fleet_order.target.id this_status = foAI.foAIstate.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]): print "Aborting mission due to being blocked by Big Monster at system %d, threat %d" % ( this_system_id, foAI.foAIstate.systemStatus[this_system_id]['monsterThreat']) print "Full set of orders were:" for this_order in self.orders: print " - %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: print "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)): warn("Fleet %d has tentatively completed its " "colonize mission but will wait to confirm population." % self.fleet.id) print " Order details are %s" % last_order print " 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 print " 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" print ("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 fleet_id = self.fleet.id if clear_all: if orders: print "Fleet %d has completed its mission; clearing all orders and targets." % self.fleet.id print "Full set of orders were:" for this_order in orders: print "\t\t %s" % this_order self.clear_fleet_orders() self.clear_target() if foAI.foAIstate.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 print "No Current Orders" else: # TODO: evaluate releasing a smaller portion or none of the ships system_status = foAI.foAIstate.systemStatus.setdefault(last_sys_target, {}) new_fleets = [] threat_present = system_status.get('totalThreat', 0) + system_status.get('neighborThreat', 0) > 0 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): threat_present = True break if not threat_present: print "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) else: print "Threat remains in target system; NOT releasing any ships." new_military_fleets = [] for fleet_id in new_fleets: if foAI.foAIstate.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 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 print "--------------" print "Checking orders for fleet %d (on turn %d)" % (self.target_id, fo.currentTurn()) print "\t Full Orders are:" for this_orders in self.orders: print "\t\t %s" % this_orders print "/t/t------" if AIFleetMissionType.FLEET_MISSION_INVASION in self.get_mission_types(): self._check_retarget_invasion() for fleet_order in self.orders: print " checking Order: %s" % fleet_order order_type = fleet_order.get_fleet_order_type() if order_type in [AIFleetOrderType.ORDER_COLONISE, AIFleetOrderType.ORDER_OUTPOST, AIFleetOrderType.ORDER_INVADE]: # TODO: invasion? if self._check_abort_mission(fleet_order): return self.check_mergers(context=str(fleet_order)) if fleet_order.can_issue_order(verbose=True): if order_type == AIFleetOrderType.ORDER_MOVE and order_completed: # only move if all other orders completed fleet_order.issue_order() elif order_type not in [AIFleetOrderType.ORDER_MOVE, AIFleetOrderType.ORDER_DEFEND]: fleet_order.issue_order() if not fleet_order.is_execution_completed(): order_completed = False else: # check that we're not held up by a Big Monster if order_type == AIFleetOrderType.ORDER_MOVE: this_system_id = fleet_order.get_target_target().target_id this_status = foAI.foAIstate.systemStatus.setdefault(this_system_id, {}) if this_status.get('monsterThreat', 0) > fo.currentTurn() * ProductionAI.curBestMilShipRating()/4.0: first_mission = self.get_mission_types()[0] if self.get_mission_types() else AIFleetMissionType.FLEET_MISSION_INVALID if (first_mission not in (AIFleetMissionType.FLEET_MISSION_ATTACK, AIFleetMissionType.FLEET_MISSION_MILITARY, AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, AIFleetMissionType.FLEET_MISSION_SECURE, ) or fleet_order != self.orders[-1] # if this move order is not this mil fleet's final destination, and blocked by Big Monster, release and hope for more effective reassignment ): print "Aborting mission due to being blocked by Big Monster at system %d, threat %d"%(this_system_id, foAI.foAIstate.systemStatus[this_system_id]['monsterThreat']) print "Full set of orders were:" for this_orders in self.orders: print "\t\t %s" % this_orders self.clear_fleet_orders() self.clear_targets(([-1] + self.get_mission_types()[:1])[-1]) return # moving to another system stops issuing all orders in system where fleet is # move order is also the last order in system if order_type == AIFleetOrderType.ORDER_MOVE: fleet = fo.getUniverse().getFleet(self.target_id) if fleet.systemID != fleet_order.get_target_target().target_id: break else: # went through entire order list if order_completed: orders = self.orders last_order = orders[-1] if orders else None universe = fo.getUniverse() if last_order and last_order.get_fleet_order_type() == AIFleetOrderType.ORDER_COLONISE: planet = universe.getPlanet(last_order.get_target_target().target_id) sys_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.systemID, fo.empireID())).get(fo.visibility.partial, -9999) planet_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.id, fo.empireID())).get(fo.visibility.partial, -9999) if planet_partial_vis_turn == sys_partial_vis_turn and not planet.currentMeterValue(fo.meterType.population): print "Potential Error: Fleet %d has tentatively completed its colonize mission but will wait to confirm population." % self.target_id print " Order details are %s" % last_order print " Order is valid: %s ; is Executed : %s; is execution completed: %s " % (last_order.is_valid(), last_order.isExecuted(), last_order.isExecutionCompleted()) if not last_order.is_valid(): source_target = last_order.get_source_target() target_target = last_order.get_target_target() print " source target validity: %s; target target validity: %s " % (source_target.valid, target_target.valid) if EnumsAI.AITargetType.TARGET_SHIP == source_target.target_type: ship_id = source_target.target_id ship = universe.getShip(ship_id) if not ship: print "Ship id %d not a valid ship id" % ship_id print " source target Ship (%d), species %s, can%s colonize" % (ship_id, ship.speciesName, ["not", ""][ship.canColonize]) return # colonize order must not have completed yet clearAll = True last_sys_target = -1 if last_order and last_order.get_fleet_order_type() == AIFleetOrderType.ORDER_MILITARY: last_sys_target = last_order.get_target_target().target_id # if (AIFleetMissionType.FLEET_MISSION_SECURE in self.get_mission_types()) or # not doing this until decide a way to release from a SECURE mission secure_targets = set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs) 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" elif last_sys_target in AIstate.blockadeTargetedSystemIDs: secure_type = "Blockade" else: secure_type = "Unidentified" print "Fleet %d has completed initial stage of its mission to secure system %d (targeted for %s), may release a portion of ships" % (self.target_id, last_sys_target, secure_type) clearAll = False fleet_id = self.target_id if clearAll: if orders: print "Fleet %d has completed its mission; clearing all orders and targets." % self.target_id print "Full set of orders were:" for this_orders in orders: print "\t\t %s" % this_orders self.clear_fleet_orders() self.clear_targets(([-1] + self.get_mission_types()[:1])[-1]) if foAI.foAIstate.get_fleet_role(fleet_id) in (AIFleetMissionType.FLEET_MISSION_MILITARY, AIFleetMissionType.FLEET_MISSION_ATTACK, AIFleetMissionType.FLEET_MISSION_DEFEND, AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, AIFleetMissionType.FLEET_MISSION_SECURE): allocations = MilitaryAI.get_military_fleets(milFleetIDs=[fleet_id], tryReset=False, thisround="Fleet %d Reassignment" % fleet_id) if allocations: MilitaryAI.assign_military_fleets_to_systems(useFleetIDList=[fleet_id], allocations=allocations) else: # no orders print "No Current Orders" else: #TODO: evaluate releasing a smaller portion or none of the ships system_status = foAI.foAIstate.systemStatus.setdefault(last_sys_target, {}) new_fleets = [] threat_present = (system_status.get('totalThreat', 0) != 0) or (system_status.get('neighborThreat', 0) != 0) 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: threat_present = True break if not threat_present: print "No current threat in target system; releasing a portion of ships." new_fleets = FleetUtilsAI.split_fleet(self.target_id) # at least first stage of current task is done; release extra ships for potential other deployments else: print "Threat remains in target system; NOT releasing any ships." new_military_fleets = [] for fleet_id in new_fleets: if foAI.foAIstate.get_fleet_role(fleet_id) in (AIFleetMissionType.FLEET_MISSION_MILITARY, AIFleetMissionType.FLEET_MISSION_ATTACK, AIFleetMissionType.FLEET_MISSION_DEFEND, AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, AIFleetMissionType.FLEET_MISSION_SECURE): new_military_fleets.append(fleet_id) allocations = [] if new_military_fleets: allocations = MilitaryAI.get_military_fleets(milFleetIDs=new_military_fleets, tryReset=False, thisround="Fleet Reassignment %s" % new_military_fleets) if allocations: MilitaryAI.assign_military_fleets_to_systems(useFleetIDList=new_military_fleets, allocations=allocations)