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 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 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 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, 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]