def get_military_fleets(mil_fleets_ids=None, try_reset=True, thisround="Main"): """Get armed military fleets.""" global _military_allocations universe = fo.getUniverse() empire_id = fo.empireID() home_system_id = PlanetUtilsAI.get_capital_sys_id() all_military_fleet_ids = (mil_fleets_ids if mil_fleets_ids is not None else FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.MILITARY)) if try_reset and (fo.currentTurn() + empire_id) % 30 == 0 and thisround == "Main": try_again(all_military_fleet_ids, try_reset=False, thisround=thisround + " Reset") return mil_fleets_ids = list(FleetUtilsAI.extract_fleet_ids_without_mission_types(all_military_fleet_ids)) mil_needing_repair_ids, mil_fleets_ids = avail_mil_needing_repair(mil_fleets_ids, split_ships=True) avail_mil_rating = sum(map(CombatRatingsAI.get_fleet_rating, mil_fleets_ids)) if not mil_fleets_ids: if "Main" in thisround: _military_allocations = [] return [] # for each system, get total rating of fleets assigned to it already_assigned_rating = {} already_assigned_rating_vs_planets = {} systems_status = foAI.foAIstate.systemStatus enemy_sup_factor = {} # enemy supply for sys_id in universe.systemIDs: already_assigned_rating[sys_id] = 0 already_assigned_rating_vs_planets[sys_id] = 0 enemy_sup_factor[sys_id] = min(2, len(systems_status.get(sys_id, {}).get('enemies_nearly_supplied', []))) for fleet_id in [fid for fid in all_military_fleet_ids if fid not in mil_fleets_ids]: ai_fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id) if not ai_fleet_mission.target: # shouldn't really be possible continue last_sys = ai_fleet_mission.target.get_system().id # will count this fleet as assigned to last system in target list # TODO last_sys or target sys? this_rating = CombatRatingsAI.get_fleet_rating(fleet_id) this_rating_vs_planets = CombatRatingsAI.get_fleet_rating_against_planets(fleet_id) already_assigned_rating[last_sys] = CombatRatingsAI.combine_ratings( already_assigned_rating.get(last_sys, 0), this_rating) already_assigned_rating_vs_planets[last_sys] = CombatRatingsAI.combine_ratings( already_assigned_rating_vs_planets.get(last_sys, 0), this_rating_vs_planets) for sys_id in universe.systemIDs: my_defense_rating = systems_status.get(sys_id, {}).get('mydefenses', {}).get('overall', 0) already_assigned_rating[sys_id] = CombatRatingsAI.combine_ratings(my_defense_rating, already_assigned_rating[sys_id]) if _verbose_mil_reporting and already_assigned_rating[sys_id]: print "\t System %s already assigned rating %.1f" % ( universe.getSystem(sys_id), already_assigned_rating[sys_id]) # get systems to defend capital_id = PlanetUtilsAI.get_capital() if capital_id is not None: capital_planet = universe.getPlanet(capital_id) else: capital_planet = None # TODO: if no owned planets try to capture one! if capital_planet: capital_sys_id = capital_planet.systemID else: # should be rare, but so as to not break code below, pick a randomish mil-centroid system capital_sys_id = None # unless we can find one to use system_dict = {} for fleet_id in all_military_fleet_ids: status = foAI.foAIstate.fleetStatus.get(fleet_id, None) if status is not None: system_id = status['sysID'] if not list(universe.getSystem(system_id).planetIDs): continue system_dict[system_id] = system_dict.get(system_id, 0) + status.get('rating', 0) ranked_systems = sorted([(val, sys_id) for sys_id, val in system_dict.items()]) if ranked_systems: capital_sys_id = ranked_systems[-1][-1] else: try: capital_sys_id = foAI.foAIstate.fleetStatus.items()[0][1]['sysID'] except: pass num_targets = max(10, PriorityAI.allotted_outpost_targets) top_target_planets = ([pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allotted_invasion_targets()] if pscore > MIN_INVASION_SCORE] + [pid for pid, (pscore, spec) in foAI.foAIstate.colonisableOutpostIDs.items()[:num_targets] if pscore > MIN_INVASION_SCORE] + [pid for pid, (pscore, spec) in foAI.foAIstate.colonisablePlanetIDs.items()[:num_targets] if pscore > MIN_INVASION_SCORE]) top_target_planets.extend(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) base_col_target_systems = PlanetUtilsAI.get_systems(top_target_planets) top_target_systems = [] for sys_id in AIstate.invasionTargetedSystemIDs + base_col_target_systems: if sys_id not in top_target_systems: if foAI.foAIstate.systemStatus[sys_id]['totalThreat'] > get_tot_mil_rating(): continue top_target_systems.append(sys_id) # doing this rather than set, to preserve order try: # capital defense allocation_helper = AllocationHelper(already_assigned_rating, already_assigned_rating_vs_planets, avail_mil_rating, try_reset) if capital_sys_id is not None: CapitalDefenseAllocator(capital_sys_id, allocation_helper).allocate() # defend other planets empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) empire_occupied_system_ids = list(set(PlanetUtilsAI.get_systems(empire_planet_ids)) - {capital_sys_id}) for sys_id in empire_occupied_system_ids: PlanetDefenseAllocator(sys_id, allocation_helper).allocate() # attack / protect high priority targets for sys_id in top_target_systems: TopTargetAllocator(sys_id, allocation_helper).allocate() # enemy planets other_targeted_system_ids = [sys_id for sys_id in set(PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if sys_id not in top_target_systems] for sys_id in other_targeted_system_ids: TargetAllocator(sys_id, allocation_helper).allocate() # colony / outpost targets other_targeted_system_ids = [sys_id for sys_id in list(set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if sys_id not in top_target_systems] for sys_id in other_targeted_system_ids: OutpostTargetAllocator(sys_id, allocation_helper).allocate() # TODO blockade enemy systems # interior systems targetable_ids = set(state.get_systems_by_supply_tier(0)) current_mil_systems = [sid for sid, _, _, _, _ in allocation_helper.allocations] interior_targets1 = targetable_ids.difference(current_mil_systems) interior_targets = [sid for sid in interior_targets1 if ( allocation_helper.threat_bias + systems_status.get(sid, {}).get('totalThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid])] for sys_id in interior_targets: InteriorTargetsAllocator(sys_id, allocation_helper).allocate() # TODO Exploration targets # border protections visible_system_ids = foAI.foAIstate.visInteriorSystemIDs | foAI.foAIstate.visBorderSystemIDs accessible_system_ids = ([sys_id for sys_id in visible_system_ids if universe.systemsConnected(sys_id, home_system_id, empire_id)] if home_system_id != INVALID_ID else []) current_mil_systems = [sid for sid, alloc, rvp, take_any, _ in allocation_helper.allocations if alloc > 0] border_targets1 = [sid for sid in accessible_system_ids if sid not in current_mil_systems] border_targets = [sid for sid in border_targets1 if ( allocation_helper.threat_bias + systems_status.get(sid, {}).get('fleetThreat', 0) + systems_status.get(sid, {}).get( 'planetThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid])] for sys_id in border_targets: BorderSecurityAllocator(sys_id, allocation_helper).allocate() except ReleaseMilitaryException: try_again(all_military_fleet_ids) return new_allocations = [] remaining_mil_rating = avail_mil_rating # for top categories assign max_alloc right away as available for cat in ['capitol', 'occupied', 'topTargets']: for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(cat, []): if remaining_mil_rating <= 0: break this_alloc = min(remaining_mil_rating, max_alloc) new_allocations.append((sid, this_alloc, alloc, rvp, take_any)) remaining_mil_rating -= this_alloc base_allocs = set() # for lower priority categories, first assign base_alloc around to all, then top up as available for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']: for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(cat, []): if remaining_mil_rating <= 0: break base_allocs.add(sid) remaining_mil_rating -= alloc for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']: for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(cat, []): if sid not in base_allocs: break if remaining_mil_rating <= 0: new_allocations.append((sid, alloc, alloc, rvp, take_any)) else: new_rating = min(remaining_mil_rating + alloc, max_alloc) new_allocations.append((sid, new_rating, alloc, rvp, take_any)) remaining_mil_rating -= (new_rating - alloc) if "Main" in thisround: _military_allocations = new_allocations if _verbose_mil_reporting or "Main" in thisround: print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % (thisround, dict([(sid, alloc) for sid, alloc, _, _, _ in new_allocations])) print "(Apparently) remaining military rating: %.1f" % remaining_mil_rating return new_allocations
def 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, 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 get_military_fleets(mil_fleets_ids=None, try_reset=True, thisround="Main"): """Get armed military fleets.""" global _military_allocations universe = fo.getUniverse() empire_id = fo.empireID() home_system_id = PlanetUtilsAI.get_capital_sys_id() all_military_fleet_ids = (mil_fleets_ids if mil_fleets_ids is not None else FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.MILITARY)) if try_reset and (fo.currentTurn() + empire_id) % 30 == 0 and thisround == "Main": try_again(all_military_fleet_ids, try_reset=False, thisround=thisround + " Reset") return mil_fleets_ids = list(FleetUtilsAI.extract_fleet_ids_without_mission_types(all_military_fleet_ids)) mil_needing_repair_ids, mil_fleets_ids = avail_mil_needing_repair(mil_fleets_ids, split_ships=True) avail_mil_rating = combine_ratings_list(map(CombatRatingsAI.get_fleet_rating, mil_fleets_ids)) if not mil_fleets_ids: if "Main" in thisround: _military_allocations = [] return [] # for each system, get total rating of fleets assigned to it already_assigned_rating = {} already_assigned_rating_vs_planets = {} aistate = get_aistate() systems_status = aistate.systemStatus enemy_sup_factor = {} # enemy supply for sys_id in universe.systemIDs: already_assigned_rating[sys_id] = 0 already_assigned_rating_vs_planets[sys_id] = 0 enemy_sup_factor[sys_id] = min(2, len(systems_status.get(sys_id, {}).get('enemies_nearly_supplied', []))) for fleet_id in [fid for fid in all_military_fleet_ids if fid not in mil_fleets_ids]: ai_fleet_mission = aistate.get_fleet_mission(fleet_id) if not ai_fleet_mission.target: # shouldn't really be possible continue last_sys = ai_fleet_mission.target.get_system().id # will count this fleet as assigned to last system in target list # TODO last_sys or target sys? this_rating = CombatRatingsAI.get_fleet_rating(fleet_id) this_rating_vs_planets = CombatRatingsAI.get_fleet_rating_against_planets(fleet_id) already_assigned_rating[last_sys] = CombatRatingsAI.combine_ratings( already_assigned_rating.get(last_sys, 0), this_rating) already_assigned_rating_vs_planets[last_sys] = CombatRatingsAI.combine_ratings( already_assigned_rating_vs_planets.get(last_sys, 0), this_rating_vs_planets) for sys_id in universe.systemIDs: my_defense_rating = systems_status.get(sys_id, {}).get('mydefenses', {}).get('overall', 0) already_assigned_rating[sys_id] = CombatRatingsAI.combine_ratings(my_defense_rating, already_assigned_rating[sys_id]) if _verbose_mil_reporting and already_assigned_rating[sys_id]: print "\t System %s already assigned rating %.1f" % ( universe.getSystem(sys_id), already_assigned_rating[sys_id]) # get systems to defend capital_id = PlanetUtilsAI.get_capital() if capital_id is not None: capital_planet = universe.getPlanet(capital_id) else: capital_planet = None # TODO: if no owned planets try to capture one! if capital_planet: capital_sys_id = capital_planet.systemID else: # should be rare, but so as to not break code below, pick a randomish mil-centroid system capital_sys_id = None # unless we can find one to use system_dict = {} for fleet_id in all_military_fleet_ids: status = aistate.fleetStatus.get(fleet_id, None) if status is not None: system_id = status['sysID'] if not list(universe.getSystem(system_id).planetIDs): continue system_dict[system_id] = system_dict.get(system_id, 0) + status.get('rating', 0) ranked_systems = sorted([(val, sys_id) for sys_id, val in system_dict.items()]) if ranked_systems: capital_sys_id = ranked_systems[-1][-1] else: try: capital_sys_id = aistate.fleetStatus.items()[0][1]['sysID'] except: pass num_targets = max(10, PriorityAI.allotted_outpost_targets) top_target_planets = ([pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allotted_invasion_targets()] if pscore > InvasionAI.MIN_INVASION_SCORE] + [pid for pid, (pscore, spec) in aistate.colonisableOutpostIDs.items()[:num_targets] if pscore > InvasionAI.MIN_INVASION_SCORE] + [pid for pid, (pscore, spec) in aistate.colonisablePlanetIDs.items()[:num_targets] if pscore > InvasionAI.MIN_INVASION_SCORE]) top_target_planets.extend(aistate.qualifyingTroopBaseTargets.keys()) base_col_target_systems = PlanetUtilsAI.get_systems(top_target_planets) top_target_systems = [] for sys_id in AIstate.invasionTargetedSystemIDs + base_col_target_systems: if sys_id not in top_target_systems: if aistate.systemStatus[sys_id]['totalThreat'] > get_tot_mil_rating(): continue top_target_systems.append(sys_id) # doing this rather than set, to preserve order try: # capital defense allocation_helper = AllocationHelper(already_assigned_rating, already_assigned_rating_vs_planets, avail_mil_rating, try_reset) if capital_sys_id is not None: CapitalDefenseAllocator(capital_sys_id, allocation_helper).allocate() # defend other planets empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) empire_occupied_system_ids = list(set(PlanetUtilsAI.get_systems(empire_planet_ids)) - {capital_sys_id}) for sys_id in empire_occupied_system_ids: PlanetDefenseAllocator(sys_id, allocation_helper).allocate() # attack / protect high priority targets for sys_id in top_target_systems: TopTargetAllocator(sys_id, allocation_helper).allocate() # enemy planets other_targeted_system_ids = [sys_id for sys_id in set(PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if sys_id not in top_target_systems] for sys_id in other_targeted_system_ids: TargetAllocator(sys_id, allocation_helper).allocate() # colony / outpost targets other_targeted_system_ids = [sys_id for sys_id in list(set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if sys_id not in top_target_systems] for sys_id in other_targeted_system_ids: OutpostTargetAllocator(sys_id, allocation_helper).allocate() # TODO blockade enemy systems # interior systems targetable_ids = set(state.get_systems_by_supply_tier(0)) current_mil_systems = [sid for sid, _, _, _, _ in allocation_helper.allocations] interior_targets1 = targetable_ids.difference(current_mil_systems) interior_targets = [sid for sid in interior_targets1 if ( allocation_helper.threat_bias + systems_status.get(sid, {}).get('totalThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid])] for sys_id in interior_targets: InteriorTargetsAllocator(sys_id, allocation_helper).allocate() # TODO Exploration targets # border protections visible_system_ids = aistate.visInteriorSystemIDs | aistate.visBorderSystemIDs accessible_system_ids = ([sys_id for sys_id in visible_system_ids if universe.systemsConnected(sys_id, home_system_id, empire_id)] if home_system_id != INVALID_ID else []) current_mil_systems = [sid for sid, alloc, rvp, take_any, _ in allocation_helper.allocations if alloc > 0] border_targets1 = [sid for sid in accessible_system_ids if sid not in current_mil_systems] border_targets = [sid for sid in border_targets1 if ( allocation_helper.threat_bias + systems_status.get(sid, {}).get('fleetThreat', 0) + systems_status.get(sid, {}).get( 'planetThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid])] for sys_id in border_targets: BorderSecurityAllocator(sys_id, allocation_helper).allocate() except ReleaseMilitaryException: try_again(all_military_fleet_ids) return new_allocations = [] remaining_mil_rating = avail_mil_rating # for top categories assign max_alloc right away as available for cat in ['capitol', 'occupied', 'topTargets']: for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(cat, []): if remaining_mil_rating <= 0: break this_alloc = min(remaining_mil_rating, max_alloc) new_allocations.append((sid, this_alloc, alloc, rvp, take_any)) remaining_mil_rating = rating_difference(remaining_mil_rating, this_alloc) base_allocs = set() # for lower priority categories, first assign base_alloc around to all, then top up as available for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']: for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(cat, []): if remaining_mil_rating <= 0: break alloc = min(remaining_mil_rating, alloc) base_allocs.add(sid) remaining_mil_rating = rating_difference(remaining_mil_rating, alloc) for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']: for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(cat, []): if sid not in base_allocs: break if remaining_mil_rating <= 0: new_allocations.append((sid, alloc, alloc, rvp, take_any)) else: local_max_avail = combine_ratings(remaining_mil_rating, alloc) new_rating = min(local_max_avail, max_alloc) new_allocations.append((sid, new_rating, alloc, rvp, take_any)) remaining_mil_rating = rating_difference(local_max_avail, new_rating) if "Main" in thisround: _military_allocations = new_allocations if _verbose_mil_reporting or "Main" in thisround: print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % (thisround, dict([(sid, alloc) for sid, alloc, _, _, _ in new_allocations])) print "(Apparently) remaining military rating: %.1f" % remaining_mil_rating return new_allocations