def startNewGame(aggression_input=fo.aggression.aggressive): # pylint: disable=invalid-name """Called by client when a new game is started (but not when a game is loaded). Should clear any pre-existing state and set up whatever is needed for AI to generate orders.""" empire = fo.getEmpire() if empire is None: fatal("This client has no empire. Ignoring new game start message.") return if empire.eliminated: info( "This empire has been eliminated. Ignoring new game start message." ) return # initialize AIstate debug("Initializing AI state...") create_new_aistate(aggression_input) aistate = get_aistate() aggression_trait = aistate.character.get_trait(Aggression) debug("New game started, AI Aggression level %d (%s)" % (aggression_trait.key, get_trait_name_aggression(aistate.character))) aistate.session_start_cleanup() debug("Initialization of AI state complete!") debug("Trying to rename our homeworld...") planet_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() if planet_id is not None and planet_id != INVALID_ID: planet = universe.getPlanet(planet_id) new_name = " ".join([ random.choice(possible_capitals(aistate.character)).strip(), planet.name ]) debug(" Renaming to %s..." % new_name) res = fo.issueRenameOrder(planet_id, new_name) debug(" Result: %d; Planet is now named %s" % (res, planet.name)) _pre_game_start(empire.empireID, aistate)
def startNewGame(aggression_input=fo.aggression.aggressive): # pylint: disable=invalid-name """Called by client when a new game is started (but not when a game is loaded). Should clear any pre-existing state and set up whatever is needed for AI to generate orders.""" empire = fo.getEmpire() if empire.eliminated: print "This empire has been eliminated. Ignoring new game start message." return turn_timer.start("Server Processing") # initialize AIstate global foAIstate print "Initializing foAIstate..." foAIstate = AIstate.AIstate(aggression_input) aggression_trait = foAIstate.character.get_trait(Aggression) print "New game started, AI Aggression level %d (%s)" % ( aggression_trait.key, get_trait_name_aggression(foAIstate.character)) foAIstate.session_start_cleanup() print "Initialization of foAIstate complete!" print "Trying to rename our homeworld..." planet_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() if planet_id is not None and planet_id != INVALID_ID: planet = universe.getPlanet(planet_id) new_name = " ".join([random.choice(possible_capitals(foAIstate.character)).strip(), planet.name]) print " Renaming to %s..." % new_name res = fo.issueRenameOrder(planet_id, new_name) print " Result: %d; Planet is now named %s" % (res, planet.name) diplomatic_corp_configs = {fo.aggression.beginner: DiplomaticCorp.BeginnerDiplomaticCorp, fo.aggression.maniacal: DiplomaticCorp.ManiacalDiplomaticCorp} global diplomatic_corp diplomatic_corp = diplomatic_corp_configs.get(aggression_trait.key, DiplomaticCorp.DiplomaticCorp)() TechsListsAI.test_tech_integrity()
def startNewGame(aggression=fo.aggression.aggressive): # pylint: disable=invalid-name """Called by client when a new game is started (but not when a game is loaded). Should clear any pre-existing state and set up whatever is needed for AI to generate orders.""" empire = fo.getEmpire() if empire.eliminated: print "This empire has been eliminated. Ignoring new game start message." return turn_timer.start("Server Processing") print "New game started, AI Aggression level %d (%s)" % (aggression, UserString(_aggression_names[aggression])) # initialize AIstate global foAIstate print "Initializing foAIstate..." foAIstate = AIstate.AIstate(aggression) foAIstate.session_start_cleanup() print "Initialization of foAIstate complete!" print "Trying to rename our homeworld..." planet_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() if planet_id is not None and planet_id != -1: planet = universe.getPlanet(planet_id) new_name = " ".join([random.choice(_capitals.get(aggression, []) or [" "]).strip(), planet.name]) print " Renaming to %s..." % new_name res = fo.issueRenameOrder(planet_id, new_name) print " Result: %d; Planet is now named %s" % (res, planet.name) diplomatic_corp_configs = {fo.aggression.beginner: DiplomaticCorp.BeginnerDiplomaticCorp, fo.aggression.maniacal: DiplomaticCorp.ManiacalDiplomaticCorp} global diplomatic_corp diplomatic_corp = diplomatic_corp_configs.get(aggression, DiplomaticCorp.DiplomaticCorp)() TechsListsAI.test_tech_integrity()
def startNewGame(aggression=fo.aggression.aggressive): # pylint: disable=invalid-name """Called by client when a new game is started (but not when a game is loaded). Should clear any pre-existing state and set up whatever is needed for AI to generate orders.""" empire = fo.getEmpire() if empire.eliminated: print "This empire has been eliminated. Ignoring new game start message." return turn_timer.start("Server Processing") print "New game started, AI Aggression level %d" % aggression # initialize AIstate global foAIstate foAIstate = AIstate.AIstate(aggression=aggression) foAIstate.session_start_cleanup() print "Initialized foAIstate class" planet_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() if planet_id is not None and planet_id != -1: planet = universe.getPlanet(planet_id) new_name = " ".join([random.choice(_capitals.get(aggression, []) or [" "]).strip(), planet.name]) print "Capitol City Names are: ", _capitals print "This Capitol New name is ", new_name res = fo.issueRenameOrder(planet_id, new_name) print "Capitol Rename attempt result: %d; planet now named %s" % (res, planet.name) diplomatic_corp_configs = {fo.aggression.beginner: DiplomaticCorp.BeginnerDiplomaticCorp, fo.aggression.maniacal: DiplomaticCorp.ManiacalDiplomaticCorp} global diplomatic_corp diplomatic_corp = diplomatic_corp_configs.get(aggression, DiplomaticCorp.DiplomaticCorp)()
def inspect_ai_interface(): capital_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() fleets_int_vector = universe.fleetIDs fleet = universe.getFleet(list(fleets_int_vector)[0]) ship = universe.getShip(list(universe.shipIDs)[0]) design = fo.getShipDesign(ship.designID) empire = fo.getEmpire() tech = fo.getTech('SHP_WEAPON_2_1') tech_spec = list(tech.unlockedItems)[0] part_id = list(empire.availableShipParts)[0] part_type = fo.getPartType(part_id) prod_queue = empire.productionQueue fo.issueEnqueueShipProductionOrder( list(empire.availableShipDesigns)[0], capital_id) research_queue = empire.researchQueue fo.issueEnqueueTechOrder('SHP_WEAPON_1_2', -1) planet = universe.getPlanet(capital_id) building = list(planet.buildingIDs)[0] inspect( fo, universe, fleet, planet, universe.getSystem(planet.systemID), ship, empire, design, tech, tech_spec, fo.getFieldType('FLD_ION_STORM'), fo.getBuildingType('BLD_SHIPYARD_BASE'), fo.getGalaxySetupData(), fo.getHullType('SH_XENTRONIUM'), fo.getPartType('SR_WEAPON_1_1'), fo.getSpecial('MODERATE_TECH_NATIVES_SPECIAL'), fo.getSpecies('SP_ABADDONI'), fo.getTech('SHP_WEAPON_4_1'), fo.diplomaticMessage(1, 2, fo.diplomaticMessageType.acceptProposal), fleets_int_vector, part_type, prod_queue, prod_queue.allocatedPP, prod_queue[0], research_queue, research_queue[0], empire.getSitRep(0), universe.getBuilding(building), ) exit(1) # exit game to main menu no need to play anymore.
def inspect_FreeOrionAIInterface(): capital_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() fleets_int_vector = universe.fleetIDs fleet = universe.getFleet(list(fleets_int_vector)[0]) ship = universe.getShip(list(universe.shipIDs)[0]) design = fo.getShipDesign(ship.designID) empire = fo.getEmpire() tech = fo.getTech('SHP_WEAPON_2_1') tech_spec = list(tech.unlockedItems)[0] part_id = list(empire.availableShipParts)[0] part_type = fo.getPartType(part_id) prod_queue = empire.productionQueue fo.issueEnqueueShipProductionOrder(list(empire.availableShipDesigns)[0], capital_id) research_queue = empire.researchQueue fo.issueEnqueueTechOrder('SHP_WEAPON_1_2', -1) planet = universe.getPlanet(capital_id) building = list(planet.buildingIDs)[0] inspect( fo, universe, fleet, planet, universe.getSystem(planet.systemID), ship, empire, design, tech, tech_spec, fo.getFieldType('FLD_ION_STORM'), fo.getBuildingType('BLD_SHIPYARD_BASE'), fo.getGalaxySetupData(), fo.getHullType('SH_XENTRONIUM'), fo.getPartType('SR_WEAPON_1_1'), fo.getSpecial('MODERATE_TECH_NATIVES_SPECIAL'), fo.getSpecies('SP_ABADDONI'), fo.getTech('SHP_WEAPON_4_1'), fo.diplomaticMessage(1, 2, fo.diplomaticMessageType.acceptProposal), fleets_int_vector, part_type, prod_queue, prod_queue.allocatedPP, prod_queue[0], research_queue, research_queue[0], empire.getSitRep(0), universe.getBuilding(building), ) exit(1) # exit game to main menu no need to play anymore.
def _print_empire_capital(): planet_id = PlanetUtilsAI.get_capital() if planet_id is not None and planet_id != INVALID_ID: planet = fo.getUniverse().getPlanet(planet_id) stats.capital(planet_id, planet.name, planet.speciesName) else: stats.capital(None, None, None) debug( "***************************************************************************" ) debug( "***************************************************************************" )
def startNewGame(aggression_input=fo.aggression.aggressive): # pylint: disable=invalid-name """Called by client when a new game is started (but not when a game is loaded). Should clear any pre-existing state and set up whatever is needed for AI to generate orders.""" empire = fo.getEmpire() if empire is None: print "This client has no empire. Ignoring new game start message." return if empire.eliminated: info( "This empire has been eliminated. Ignoring new game start message." ) return turn_timer.start("Server Processing") # initialize AIstate global foAIstate debug("Initializing foAIstate...") foAIstate = AIstate.AIstate(aggression_input) aggression_trait = foAIstate.character.get_trait(Aggression) debug( "New game started, AI Aggression level %d (%s)" % (aggression_trait.key, get_trait_name_aggression(foAIstate.character))) foAIstate.session_start_cleanup() debug("Initialization of foAIstate complete!") debug("Trying to rename our homeworld...") planet_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() if planet_id is not None and planet_id != INVALID_ID: planet = universe.getPlanet(planet_id) new_name = " ".join([ random.choice(possible_capitals(foAIstate.character)).strip(), planet.name ]) debug(" Renaming to %s..." % new_name) res = fo.issueRenameOrder(planet_id, new_name) debug(" Result: %d; Planet is now named %s" % (res, planet.name)) diplomatic_corp_configs = { fo.aggression.beginner: DiplomaticCorp.BeginnerDiplomaticCorp, fo.aggression.maniacal: DiplomaticCorp.ManiacalDiplomaticCorp } global diplomatic_corp diplomatic_corp = diplomatic_corp_configs.get( aggression_trait.key, DiplomaticCorp.DiplomaticCorp)() TechsListsAI.test_tech_integrity()
def _get_path_from_capital( planet: "fo.planet") -> Tuple[Sequence[SystemId], int]: """ Return chain of system from the planet to capital and length. If there is no path, return empty sequence and default distance. """ universe = fo.getUniverse() capital_id = PlanetUtilsAI.get_capital() if capital_id != INVALID_ID: homeworld = universe.getPlanet(capital_id) if homeworld and homeworld.systemID != INVALID_ID and planet.systemID != INVALID_ID: least_jumps_path = list( universe.leastJumpsPath(homeworld.systemID, planet.systemID, fo.empireID())) max_jumps = len(least_jumps_path) return least_jumps_path, max_jumps else: return [], 8
def startNewGame(aggression=fo.aggression.aggressive): # pylint: disable=invalid-name """Called by client when a new game is started (but not when a game is loaded). Should clear any pre-existing state and set up whatever is needed for AI to generate orders.""" turn_timer.start("Server Processing") print "New game started, AI Aggression level %d" % aggression # initialize AIstate global foAIstate foAIstate = AIstate.AIstate(aggression=aggression) foAIstate.session_start_cleanup() print "Initialized foAIstate class" planet_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() if planet_id is not None and planet_id != -1: planet = universe.getPlanet(planet_id) new_name = random.choice(_capitols.get(aggression, "").split('\n')).strip() + " " + planet.name print "Capitol City Names are: ", _capitols print "This Capitol New name is ", new_name res = fo.issueRenameOrder(planet_id, new_name) print "Capitol Rename attempt result: %d; planet now named %s" % (res, planet.name)
def startNewGame(aggression=fo.aggression.aggressive): # pylint: disable=invalid-name """Called by client when a new game is started (but not when a game is loaded). Should clear any pre-existing state and set up whatever is needed for AI to generate orders.""" turn_timer.start("Server Processing") print "New game started, AI Aggression level %d" % aggression # initialize AIstate global foAIstate foAIstate = AIstate.AIstate(aggression=aggression) foAIstate.session_start_cleanup() print "Initialized foAIstate class" planet_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() if planet_id is not None and planet_id != -1: planet = universe.getPlanet(planet_id) new_name = random.choice(_capitols.get( aggression, "").split('\n')).strip() + " " + planet.name print "Capitol City Names are: ", _capitols print "This Capitol New name is ", new_name res = fo.issueRenameOrder(planet_id, new_name) print "Capitol Rename attempt result: %d; planet now named %s" % ( res, planet.name)
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 get_invasion_fleets(): invasion_timer.start("gathering initial info") all_invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) AIstate.invasionFleetIDs = FleetUtilsAI.extract_fleet_ids_without_mission_types(all_invasion_fleet_ids) # get suppliable planets universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID capital_id = PlanetUtilsAI.get_capital() homeworld=None if capital_id: homeworld = universe.getPlanet(capital_id) if homeworld: home_system_id = homeworld.systemID else: home_system_id = -1 fleet_suppliable_system_ids = empire.fleetSupplyableSystemIDs fleet_suppliable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(fleet_suppliable_system_ids) prime_invadable_system_ids = set(ColonisationAI.annexable_system_ids) prime_invadable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(prime_invadable_system_ids) visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate. visBorderSystemIDs.keys() if home_system_id != -1: accessible_system_ids = [sys_id for sys_id in visible_system_ids if (sys_id != -1) and universe.systemsConnected(sys_id, home_system_id, empire_id)] else: print "Invasion Warning: this empire has no identifiable homeworld, will therefor treat all visible planets as accessible." accessible_system_ids = visible_system_ids # TODO: check if any troop ships still owned, use their system as home system acessible_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(accessible_system_ids) print "Accessible Systems: ", ", ".join(PlanetUtilsAI.sys_name_ids(accessible_system_ids)) print all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids(acessible_planet_ids) # need these for unpopulated outposts all_populated_planets = PlanetUtilsAI.get_populated_planet_ids(acessible_planet_ids) # need this for natives print "All Visible and accessible Populated PlanetIDs (including this empire's): ", ", ".join(PlanetUtilsAI.planet_name_ids(all_populated_planets)) print print "Prime Invadable Target Systems: ", ", ".join(PlanetUtilsAI.sys_name_ids(prime_invadable_system_ids)) print empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) invadable_planet_ids = set(prime_invadable_planet_ids).intersection(set(all_owned_planet_ids).union(all_populated_planets) - set(empire_owned_planet_ids)) print "Prime Invadable PlanetIDs: ", ", ".join(PlanetUtilsAI.planet_name_ids(invadable_planet_ids)) print print "Current Invasion Targeted SystemIDs: ", ", ".join(PlanetUtilsAI.sys_name_ids(AIstate.invasionTargetedSystemIDs)) invasion_targeted_planet_ids = get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) invasion_targeted_planet_ids.extend(get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_ORBITAL_INVASION)) all_invasion_targeted_system_ids = set(PlanetUtilsAI.get_systems(invasion_targeted_planet_ids)) print "Current Invasion Targeted PlanetIDs: ", ", ".join(PlanetUtilsAI.planet_name_ids(invasion_targeted_planet_ids)) invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) if not invasion_fleet_ids: print "Available Invasion Fleets: 0" else: print "Invasion FleetIDs: %s" % FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) num_invasion_fleets = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(invasion_fleet_ids)) print "Invasion Fleets Without Missions: %s" % num_invasion_fleets invasion_timer.start("planning troop base production") # only do base invasions if aggression is typical or above reserved_troop_base_targets = [] if foAI.foAIstate.aggression > fo.aggression.typical: available_pp = {} for el in empire.planetsWithAvailablePP: # keys are sets of ints; data is doubles avail_pp = el.data() for pid in el.key(): available_pp[pid] = avail_pp for pid in invadable_planet_ids: # TODO: reorganize planet = universe.getPlanet(pid) if not planet: continue sys_id = planet.systemID sys_partial_vis_turn = universe.getVisibilityTurnsMap(planet.systemID, empire_id).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(pid, empire_id).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: continue for pid2 in ColonisationAI.empire_species_systems.get(sys_id, {}).get('pids', []): if available_pp.get(pid2, 0) < 2: # TODO: improve troop base PP sufficiency determination break planet2 = universe.getPlanet(pid2) if not planet2: continue if pid not in foAI.foAIstate.qualifyingTroopBaseTargets and planet2.speciesName in ColonisationAI.empire_ship_builders: foAI.foAIstate.qualifyingTroopBaseTargets.setdefault(pid, [pid2, -1]) break for pid in list(foAI.foAIstate.qualifyingTroopBaseTargets): planet = universe.getPlanet(pid) # TODO: also check that still have a colony in this system that can make troops if planet and planet.owner == empire_id: del foAI.foAIstate.qualifyingTroopBaseTargets[pid] secure_ai_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types([EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE]) for pid in (set(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) - set(invasion_targeted_planet_ids)): # TODO: consider overriding standard invasion mission planet = universe.getPlanet(pid) if foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] != -1: reserved_troop_base_targets.append(pid) if planet: all_invasion_targeted_system_ids.add(planet.systemID) continue # already building for here sys_id = planet.systemID this_sys_status = foAI.foAIstate.systemStatus.get(sys_id, {}) if (planet.currentMeterValue(fo.meterType.shield) > 0 and this_sys_status.get('myFleetRating', 0) < 0.8 * this_sys_status.get('totalThreat', 0)): continue loc = foAI.foAIstate.qualifyingTroopBaseTargets[pid][0] this_score, p_troops = evaluate_invasion_planet(pid, empire, secure_ai_fleet_missions, False) best_ship, col_design, build_choices = ProductionAI.getBestShipInfo(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_ORBITAL_INVASION, loc) if not best_ship: continue n_bases = math.ceil((p_troops+1) / 2) # TODO: reconsider this +1 safety factor retval = fo.issueEnqueueShipProductionOrder(best_ship, loc) print "Enqueueing %d Troop Bases at %s for %s" % (n_bases, PlanetUtilsAI.planet_name_ids([loc]), PlanetUtilsAI.planet_name_ids([pid])) if retval != 0: all_invasion_targeted_system_ids.add(planet.systemID) reserved_troop_base_targets.append(pid) foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] = loc fo.issueChangeProductionQuantityOrder(empire.productionQueue.size - 1, 1, int(n_bases)) fo.issueRequeueProductionOrder(empire.productionQueue.size - 1, 0) invasion_timer.start("evaluating target planets") # TODO: check if any invasion_targeted_planet_ids need more troops assigned evaluated_planet_ids = list(set(invadable_planet_ids) - set(invasion_targeted_planet_ids) - set(reserved_troop_base_targets)) print "Evaluating potential invasions, PlanetIDs: %s" % evaluated_planet_ids evaluated_planets = assign_invasion_values(evaluated_planet_ids, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, fleet_suppliable_planet_ids, empire) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, (pscore, ptroops) in evaluated_planets.items()] sorted_planets.sort(key=lambda x: x[1], reverse=True) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, pscore, ptroops in sorted_planets] print if sorted_planets: print "Invadable planets:\n%-6s | %-6s | %-16s | %-16s | Troops" % ('ID', 'Score', 'Name', 'Race') for pid, pscore, ptroops in sorted_planets: planet = universe.getPlanet(pid) if planet: print "%6d | %6d | %16s | %16s | %d" % (pid, pscore, planet.name, planet.speciesName, ptroops) else: print "%6d | %6d | Error: invalid planet ID" % (pid, pscore) else: print "No Invadable planets identified" sorted_planets = filter(lambda x: x[1] > 0, sorted_planets) # export opponent planets for other AI modules AIstate.opponentPlanetIDs = [pid for pid, _, _ in sorted_planets] AIstate.invasionTargets = sorted_planets # export invasion targeted systems for other AI modules AIstate.invasionTargetedSystemIDs = list(all_invasion_targeted_system_ids) invasion_timer.stop(section_name="evaluating %d target planets" % (len(evaluated_planet_ids))) invasion_timer.end()
def 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 generateOrders(): # pylint: disable=invalid-name """Called once per turn to tell the Python AI to generate and issue orders to control its empire. at end of this function, fo.doneTurn() should be called to indicate to the client that orders are finished and can be sent to the server for processing.""" empire = fo.getEmpire() if empire is None: print "This client has no empire. Doing nothing to generate orders." try: # early abort if no empire. no need to do meter calculations # on last-seen gamestate if nothing can be ordered anyway... # # note that doneTurn() is issued on behalf of the client network # id, not the empire id, so not having a correct empire id does # not invalidate doneTurn() fo.doneTurn() except Exception as e: print_error(e) return if empire.eliminated: print "This empire has been eliminated. Aborting order generation" try: # early abort if already eliminated. no need to do meter calculations # on last-seen gamestate if nothing can be ordered anyway... fo.doneTurn() except Exception as e: print_error(e) return # This code block is required for correct AI work. print "Meter / Resource Pool updating..." fo.initMeterEstimatesDiscrepancies() fo.updateMeterEstimates(False) fo.updateResourcePools() turn = fo.currentTurn() turn_uid = foAIstate.set_turn_uid() print "\n\n\n", "=" * 20, print "Starting turn %s (%s) of game: %s" % (turn, turn_uid, foAIstate.uid), print "=" * 20, "\n" turn_timer.start("AI planning") # set the random seed (based on galaxy seed, empire name and current turn) # for game-reload consistency. random_seed = str(fo.getGalaxySetupData().seed) + "%05d%s" % (turn, fo.getEmpire().name) random.seed(random_seed) universe = fo.getUniverse() empire = fo.getEmpire() planet_id = PlanetUtilsAI.get_capital() planet = None if planet_id is not None: planet = universe.getPlanet(planet_id) aggression_name = get_trait_name_aggression(foAIstate.character) print "***************************************************************************" print "******* Log info for AI progress chart script. Do not modify. **********" print ("Generating Orders") print ("EmpireID: {empire.empireID}" " Name: {empire.name}_{empire.empireID}_pid:{p_id}_{p_name}RIdx_{res_idx}_{aggression}" " Turn: {turn}").format(empire=empire, p_id=fo.playerID(), p_name=fo.playerName(), res_idx=ResearchAI.get_research_index(), turn=turn, aggression=aggression_name.capitalize()) print "EmpireColors: {0.colour.r} {0.colour.g} {0.colour.b} {0.colour.a}".format(empire) if planet: print "CapitalID: " + str(planet_id) + " Name: " + planet.name + " Species: " + planet.speciesName else: print "CapitalID: None Currently Name: None Species: None " print "***************************************************************************" print "***************************************************************************" if turn == 1: declare_war_on_all() human_player = fo.empirePlayerID(1) greet = diplomatic_corp.get_first_turn_greet_message() fo.sendChatMessage(human_player, '%s (%s): [[%s]]' % (empire.name, get_trait_name_aggression(foAIstate.character), greet)) # turn cleanup !!! this was formerly done at start of every turn -- not sure why foAIstate.split_new_fleets() foAIstate.refresh() # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats foAIstate.report_system_threats() print("Calling AI Modules") # call AI modules action_list = [ColonisationAI.survey_universe, ProductionAI.find_best_designs_this_turn, PriorityAI.calculate_priorities, ExplorationAI.assign_scouts_to_explore_systems, ColonisationAI.assign_colony_fleets_to_colonise, InvasionAI.assign_invasion_fleets_to_invade, MilitaryAI.assign_military_fleets_to_systems, FleetUtilsAI.generate_fleet_orders_for_fleet_missions, FleetUtilsAI.issue_fleet_orders_for_fleet_missions, ResearchAI.generate_research_orders, ProductionAI.generate_production_orders, ResourcesAI.generate_resources_orders, ] for action in action_list: try: main_timer.start(action.__name__) action() main_timer.stop() except Exception as e: print_error(e, location=action.__name__) main_timer.stop_print_and_clear() turn_timer.stop_print_and_clear() turn_timer.start("Server_Processing") try: fo.doneTurn() except Exception as e: print_error(e) # TODO move it to cycle above if using_statprof: try: statprof.stop() statprof.display() statprof.start() except: pass
def get_military_fleets(milFleetIDs=None, tryReset=True, thisround="Main"): """get armed military fleets""" global MilitaryAllocations, totMilRating, num_milships universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID capitalID = PlanetUtilsAI.get_capital() if capitalID is None: homeworld=None else: homeworld = universe.getPlanet(capitalID) if homeworld: homeSystemID = homeworld.systemID else: homeSystemID=-1 if milFleetIDs is not None: allMilitaryFleetIDs = milFleetIDs else: allMilitaryFleetIDs = FleetUtilsAI.get_empire_fleet_ids_by_role(AIFleetMissionType.FLEET_MISSION_MILITARY ) if tryReset and ((fo.currentTurn()+empireID) % 30 ==0) and thisround=="Main": try_again(allMilitaryFleetIDs, try_reset=False, thisround = thisround+" Reset") num_milships = 0 for fid in allMilitaryFleetIDs: num_milships += foAI.foAIstate.fleetStatus.get(fid, {}).get('nships', 0) thisTotMilRating = sum( map(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0), allMilitaryFleetIDs ) ) if "Main" in thisround: totMilRating = thisTotMilRating print "==================================================" print "%s Round Total Military Rating: %d"%(thisround, totMilRating) print "---------------------------------" foAI.foAIstate.militaryRating=totMilRating milFleetIDs = list( FleetUtilsAI.extract_fleet_ids_without_mission_types(allMilitaryFleetIDs)) mil_needing_repair_ids, milFleetIDs = avail_mil_needing_repair(milFleetIDs, split_ships=True) availMilRating = sum( map(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0), milFleetIDs ) ) if "Main" in thisround: print "==================================================" print "%s Round Available Military Rating: %d"%(thisround, availMilRating) print "---------------------------------" remainingMilRating = availMilRating allocations = [] allocationGroups={} if not milFleetIDs: if "Main" in thisround: MilitaryAllocations = [] return [] #for each system, get total rating of fleets assigned to it alreadyAssignedRating={} assignedAttack={} assignedHP={} for sysID in universe.systemIDs: assignedAttack[sysID]=0 assignedHP[sysID]=0 for fleetID in [fid for fid in allMilitaryFleetIDs if fid not in milFleetIDs]: aiFleetMission = foAI.foAIstate.get_fleet_mission(fleetID) sysTargets= [] for aiFleetMissionType in aiFleetMission.get_mission_types(): aiTargets = aiFleetMission.get_targets(aiFleetMissionType) for aiTarget in aiTargets: sysTargets.extend(aiTarget.get_required_system_ai_targets()) if not sysTargets: #shouldn't really be possible continue lastSys = sysTargets[-1].target_id # will count this fleet as assigned to last system in target list assignedAttack[lastSys] += foAI.foAIstate.get_rating(fleetID).get('attack', 0) assignedHP[lastSys] += foAI.foAIstate.get_rating(fleetID).get('health', 0) for sysID in universe.systemIDs: mydefenses = foAI.foAIstate.systemStatus.get(sysID, {}).get( 'mydefenses', {} ) mypattack = mydefenses.get('attack', 0) myphealth = mydefenses.get('health', 0) alreadyAssignedRating[sysID] = ( assignedAttack[sysID] + mypattack ) * ( assignedHP[sysID] + myphealth ) # get systems to defend capitalID = PlanetUtilsAI.get_capital() if capitalID is not None: capitalPlanet = universe.getPlanet(capitalID) else: capitalPlanet=None #TODO: if no owned planets try to capture one! if capitalPlanet: capitalSysID = capitalPlanet.systemID else: # should be rare, but so as to not break code below, pick a randomish mil-centroid system capitalSysID=None #unless we can find one to use systemDict = {} for fleetID in allMilitaryFleetIDs: status = foAI.foAIstate.fleetStatus.get(fleetID, None) if status is not None: sysID = status['sysID'] if len( list( universe.getSystem(sysID).planetIDs ) ) ==0: continue systemDict[sysID] = systemDict.get( sysID, 0) + status.get('rating', {}).get('overall', 0) rankedSystems = sorted( [(val, sysID) for sysID, val in systemDict.items() ] ) if rankedSystems: capitalSysID = rankedSystems[-1][-1] else: try: capitalSysID = foAI.foAIstate.fleetStatus.items()[0][1]['sysID'] except: pass if False: if fo.currentTurn() < 20: threatBias = 0 elif fo.currentTurn() < 40: threatBias = 10 elif fo.currentTurn() < 60: threatBias = 80 elif fo.currentTurn() < 80: threatBias = 200 else: threatBias = 400 else: threatBias = 0 safetyFactor = [ 4.0, 3.0, 2.0, 1.5, 1.2, 1.0 ][foAI.foAIstate.aggression] topTargetPlanets = [pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allottedInvasionTargets] if pscore > 20] + [pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs[:10] if pscore > 20] topTargetPlanets.extend( foAI.foAIstate.qualifyingTroopBaseTargets.keys() ) topTargetSystems = [] for sysID in AIstate.invasionTargetedSystemIDs + PlanetUtilsAI.get_systems( topTargetPlanets ): if sysID not in topTargetSystems: topTargetSystems.append(sysID) #doing this rather than set, to preserve order # allocation format: ( sysID, newAllocation, takeAny, maxMultiplier ) #================================ #--------Capital Threat ---------- capitalThreat = safetyFactor*(2* threatBias +sum( [ foAI.foAIstate.systemStatus[capitalSysID][thrtKey] for thrtKey in ['totalThreat', 'neighborThreat']] )) neededRating = rating_needed(1.4*capitalThreat, alreadyAssignedRating[capitalSysID]) newAlloc=0 if tryReset: if neededRating > 0.5*availMilRating: try_again(allMilitaryFleetIDs) return if neededRating > 0: newAlloc = min(remainingMilRating, neededRating ) allocations.append( ( capitalSysID, newAlloc, True, 1.6*capitalThreat) ) allocationGroups.setdefault('capitol', []).append( ( capitalSysID, newAlloc, True, 2*capitalThreat) ) remainingMilRating -= newAlloc if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "Empire Capital System: (%d) %s -- threat : %d, military allocation: existing: %d ; new: %d"%(capitalSysID, universe.getSystem(capitalSysID).name , capitalThreat, alreadyAssignedRating[capitalSysID], newAlloc) print "-----------------" #================================ #--------Empire Occupied Systems ---------- empirePlanetIDs = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) empireOccupiedSystemIDs = list(set(PlanetUtilsAI.get_systems(empirePlanetIDs)) - {capitalSysID}) if "Main" in thisround: if verboseMilReporting: print "Empire-Occupied Systems: %s"%( [ "| %d %s |"%(eoSysID, universe.getSystem(eoSysID).name) for eoSysID in empireOccupiedSystemIDs ] ) print "-----------------" newAlloc=0 if len( empireOccupiedSystemIDs ) > 0: ocSysTotThreat = [ ( oSID, threatBias +safetyFactor*sum( [ foAI.foAIstate.systemStatus.get(oSID, {}).get(thrtKey, 0) for thrtKey in ['totalThreat', 'neighborThreat']] )) for oSID in empireOccupiedSystemIDs ] totocSysThreat = sum( [thrt for sid, thrt in ocSysTotThreat] ) totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in ocSysTotThreat] ) allocationFactor = min( 1.5, remainingMilRating /max(0.01, ( totocSysThreat -totCurAlloc) )) ocSysAlloc = 0 for sid, thrt in ocSysTotThreat: curAlloc=alreadyAssignedRating[sid] neededRating = rating_needed( 1.3*thrt, curAlloc) if (neededRating > 0.8* remainingMilRating) and tryReset: try_again(allMilitaryFleetIDs) return thisAlloc=0 if ( neededRating>0 ) and remainingMilRating > 0: thisAlloc = max(0, min( neededRating, 0.5*availMilRating, remainingMilRating)) newAlloc+=thisAlloc allocations.append( (sid, thisAlloc, True, 1.6*thrt) ) allocationGroups.setdefault('occupied', []).append( (sid, thisAlloc, True, 2*thrt) ) remainingMilRating -= thisAlloc ocSysAlloc += thisAlloc if "Main" in thisround or thisAlloc >0: if verboseMilReporting: print "Provincial Occupied system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc) if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "Provincial Empire-Occupied Sytems under total threat: %d -- total mil allocation: existing %d ; new: %d"%(totocSysThreat, totCurAlloc, ocSysAlloc ) print "-----------------" #================================ #--------Top Targeted Systems ---------- #TODO: do native invasions highest priority otherTargetedSystemIDs = topTargetSystems if "Main" in thisround: if verboseMilReporting: print "Top Colony and Invasion Targeted Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in otherTargetedSystemIDs ] ) print "-----------------" # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety newAlloc=0 if len( otherTargetedSystemIDs ) > 0: otSysAlloc = 0 otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)+0.5*foAI.foAIstate.systemStatus.get(oSID, {}).get('neightborThreat', 0) ) ) for oSID in otherTargetedSystemIDs ] tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] ) totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] ) for sid, thrt in otSysThreat: curAlloc=alreadyAssignedRating[sid] neededRating = rating_needed( 1.4*thrt, curAlloc) thisAlloc=0 #only record more allocation for this invasion if we already started or have enough rating available takeAny= alreadyAssignedRating[sid] > 0 if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny): thisAlloc = max(0, min( neededRating, remainingMilRating)) maxAlloc = safetyFactor*3*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0)) newAlloc+=thisAlloc allocations.append( (sid, thisAlloc, takeAny , maxAlloc) ) allocationGroups.setdefault('topTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) ) remainingMilRating -= thisAlloc otSysAlloc += thisAlloc if "Main" in thisround or thisAlloc >0: if verboseMilReporting: print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc) if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "-----------------" print "Top Colony and Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc ) print "-----------------" #================================ #--------Targeted Systems ---------- #TODO: do native invasions highest priority otherTargetedSystemIDs = [sysID for sysID in set( PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if sysID not in topTargetSystems] if "Main" in thisround: if verboseMilReporting: print "Other Invasion Targeted Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in otherTargetedSystemIDs ] ) print "-----------------" # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety newAlloc=0 if len( otherTargetedSystemIDs ) > 0: otSysAlloc = 0 otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)+0.5*foAI.foAIstate.systemStatus.get(oSID, {}).get('neighborThreat', 0) ) ) for oSID in otherTargetedSystemIDs ] tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] ) totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] ) for sid, thrt in otSysThreat: curAlloc=alreadyAssignedRating[sid] neededRating = rating_needed( 1.4*thrt, curAlloc) thisAlloc=0 #only record more allocation for this invasion if we already started or have enough rating available takeAny= alreadyAssignedRating[sid] > 0 if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny): thisAlloc = max(0, min( neededRating, remainingMilRating)) newAlloc+=thisAlloc maxAlloc = safetyFactor*3*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0)) allocations.append( (sid, thisAlloc, takeAny , maxAlloc) ) allocationGroups.setdefault('otherTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) ) remainingMilRating -= thisAlloc otSysAlloc += thisAlloc if "Main" in thisround or thisAlloc >0: if verboseMilReporting: print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc) if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "-----------------" print "Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc ) print "-----------------" otherTargetedSystemIDs = [sysID for sysID in list(set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if sysID not in topTargetSystems] if "Main" in thisround: if verboseMilReporting: print "Other Targeted Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in otherTargetedSystemIDs ] ) print "-----------------" if len( otherTargetedSystemIDs ) > 0: otSysAlloc = 0 otSysThreat = [ ( oSID, safetyFactor*(threatBias +foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)+0.5*foAI.foAIstate.systemStatus.get(oSID, {}).get('neighborThreat', 0) ) ) for oSID in otherTargetedSystemIDs ] tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] ) totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] ) newAlloc=0 for sid, thrt in otSysThreat: curAlloc=alreadyAssignedRating[sid] neededRating = rating_needed( 1.2*thrt, curAlloc) thisAlloc=0 #only record more allocation for this invasion if we already started or have enough rating available takeAny= alreadyAssignedRating[sid] > 0 if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny): thisAlloc = max(0, min( neededRating, remainingMilRating)) newAlloc+=thisAlloc maxAlloc = safetyFactor*3*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0)) allocations.append( (sid, thisAlloc, takeAny , maxAlloc) ) allocationGroups.setdefault('otherTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) ) remainingMilRating -= thisAlloc otSysAlloc += thisAlloc if "Main" in thisround or thisAlloc >0: if verboseMilReporting: print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc) if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "-----------------" print "Other Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc ) print "-----------------" otherTargetedSystemIDs = [] #targetableIDs = ColonisationAI.annexableSystemIDs.union( empire.fleetSupplyableSystemIDs ) targetableIDs = set(ColonisationAI.systems_by_supply_tier.get(0, []) + ColonisationAI.systems_by_supply_tier.get(1, [])) for sysID in AIstate.opponentSystemIDs: if sysID in targetableIDs: otherTargetedSystemIDs.append(sysID) else: for nID in universe.getImmediateNeighbors(sysID, empireID): if nID in targetableIDs: otherTargetedSystemIDs.append(sysID) break if "Main" in thisround: if verboseMilReporting: print "Blockade Targeted Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in otherTargetedSystemIDs ] ) print "-----------------" if len( otherTargetedSystemIDs ) > 0: otSysAlloc = 0 otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)+ 0.5*foAI.foAIstate.systemStatus.get(oSID, {}).get('neighborThreat', 0) ) ) for oSID in otherTargetedSystemIDs ] tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] ) totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] ) newAlloc=0 for sid, thrt in otSysThreat: curAlloc=alreadyAssignedRating[sid] neededRating = rating_needed( 1.2*thrt, curAlloc) thisAlloc=0 #only record more allocation for this invasion if we already started or have enough rating available takeAny= alreadyAssignedRating[sid] > 0 if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny): thisAlloc = max(0, min( neededRating, remainingMilRating)) newAlloc+=thisAlloc maxAlloc = safetyFactor*2*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0)) allocations.append( (sid, thisAlloc, takeAny , maxAlloc) ) allocationGroups.setdefault('otherTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) ) remainingMilRating -= thisAlloc otSysAlloc += thisAlloc if "Main" in thisround or thisAlloc >0: if verboseMilReporting: print "Blockade Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc) if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "-----------------" print "Blockade Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc ) print "-----------------" currentMilSystems = [sid for sid, alloc, takeAny, mm in allocations ] interiorIDs = list( foAI.foAIstate.expInteriorSystemIDs) interiorTargets1 = (targetableIDs.union(interiorIDs)).difference( currentMilSystems ) interiorTargets = [sid for sid in interiorTargets1 if ( threatBias + foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0) >0.8*alreadyAssignedRating[sid] ) ] if "Main" in thisround: if verboseMilReporting: print print "Other Empire-Proximal Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in interiorTargets1 ] ) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety newAlloc=0 if len(interiorTargets) >0: otSysAlloc = 0 otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)) ) for oSID in interiorTargets ] tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] ) totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] ) for sid, thrt in otSysThreat: curAlloc=alreadyAssignedRating[sid] neededRating = rating_needed( 1.2*thrt, curAlloc) thisAlloc=0 #only record more allocation for this invasion if we already started or have enough rating available takeAny= alreadyAssignedRating[sid] > 0 if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny): thisAlloc = max(0, min( neededRating, remainingMilRating)) newAlloc+=thisAlloc maxAlloc = safetyFactor*2*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0)) allocations.append( (sid, thisAlloc, takeAny , maxAlloc) ) allocationGroups.setdefault('otherTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) ) remainingMilRating -= thisAlloc otSysAlloc += thisAlloc if "Main" in thisround or thisAlloc >0: if verboseMilReporting: print "Other interior system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc) if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "-----------------" print "Other Interior Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc ) print "-----------------" elif "Main" in thisround: if verboseMilReporting: print "-----------------" print "No Other Interior Systems with fleet threat " print "-----------------" monsterDens=[] #exploTargetIDs, _ = ExplorationAI.get_current_exploration_info(verbose=False) exploTargetIDs=[] if "Main" in thisround: if verboseMilReporting: print print "Exploration-targeted Systems: %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in exploTargetIDs ] ) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety newAlloc=0 if len(exploTargetIDs) > 0: otSysAlloc = 0 otSysThreat = [ ( oSID, safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(oSID, {}).get('monsterThreat', 0)+ foAI.foAIstate.systemStatus.get(oSID, {}).get('planetThreat', 0) )) for oSID in exploTargetIDs ] tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] ) totCurAlloc = sum( [0.8*alreadyAssignedRating[sid] for sid, thrt in otSysThreat] ) if availMilRating <1125: maxMilRating = availMilRating else: maxMilRating = 0.5*availMilRating for sid, thrt in otSysThreat: curAlloc=alreadyAssignedRating[sid] neededRating = rating_needed( 1.2*thrt, curAlloc) thisAlloc=0 #only record more allocation for this invasion if we already started or have enough rating available takeAny= False if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny): thisAlloc = max(0, min( neededRating, remainingMilRating)) newAlloc+=thisAlloc maxAlloc = safetyFactor*2*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0)) allocations.append( (sid, thisAlloc, takeAny , maxAlloc) ) allocationGroups.setdefault('exploreTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) ) remainingMilRating -= thisAlloc otSysAlloc += thisAlloc if "Main" in thisround or thisAlloc >0: if verboseMilReporting: print "Exploration-targeted %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc) if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "-----------------" print "Exploration-targeted s under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc ) print "-----------------" visibleSystemIDs = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate. visBorderSystemIDs.keys() accessibleSystemIDs = [sysID for sysID in visibleSystemIDs if universe.systemsConnected(sysID, homeSystemID, empireID) ] currentMilSystems = [sid for sid, alloc, takeAny, multiplier in allocations if alloc > 0 ] borderTargets1 = [sid for sid in accessibleSystemIDs if ( sid not in currentMilSystems) ] borderTargets = [sid for sid in borderTargets1 if (threatBias +foAI.foAIstate.systemStatus.get(sid, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(sid, {}).get('planetThreat', 0) > 0.8*alreadyAssignedRating[sid]) ] if "Main" in thisround: if verboseMilReporting: print print "Empire-Accessible Systems not yet allocated military: %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID) and universe.getSystem(sysID).name) for sysID in borderTargets1 ] ) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety newAlloc=0 if len(borderTargets) > 0: otSysAlloc = 0 otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(oSID, {}).get('monsterThreat', 0)+ foAI.foAIstate.systemStatus.get(oSID, {}).get('planetThreat', 0)) ) for oSID in borderTargets ] tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] ) totCurAlloc = sum( [0.8*alreadyAssignedRating[sid] for sid, thrt in otSysThreat] ) for sid, thrt in otSysThreat: curAlloc=alreadyAssignedRating[sid] neededRating = rating_needed( 1.2*thrt, curAlloc) thisAlloc=0 #only record more allocation for this invasion if we already started or have enough rating available takeAny= False if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny): thisAlloc = max(0, min( neededRating, remainingMilRating)) newAlloc+=thisAlloc maxAlloc = safetyFactor*2*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0)) allocations.append( (sid, thisAlloc, takeAny , maxAlloc) ) allocationGroups.setdefault('accessibleTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) ) remainingMilRating -= thisAlloc otSysAlloc += thisAlloc if "Main" in thisround or thisAlloc >0: if verboseMilReporting: print "Other Empire-Accessible system %4d ( %10s ) has local biased threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc) if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "-----------------" print "Other Empire-Accessible Systems under total biased threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc ) print "-----------------" elif "Main" in thisround: if verboseMilReporting: print "-----------------" print "No Other Empire-Accessible Systems with biased local threat " print "-----------------" #monster den treatment probably unnecessary now if "Main" in thisround: if verboseMilReporting: print print "Big-Monster Dens: %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in monsterDens ] ) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety newAlloc=0 if len(monsterDens) > 0: otSysAlloc = 0 otSysThreat = [ ( oSID, safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('fleetThreat', 0)+foAI.foAIstate.systemStatus.get(oSID, {}).get('monsterThreat', 0) + foAI.foAIstate.systemStatus.get(oSID, {}).get('planetThreat', 0) ) ) for oSID in monsterDens ] tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] ) totCurAlloc = sum( [0.8*alreadyAssignedRating[sid] for sid, thrt in otSysThreat] ) for sid, thrt in otSysThreat: curAlloc=0.8*alreadyAssignedRating[sid] thisAlloc=0 if (thrt > curAlloc) and remainingMilRating > 2* thrt: thisAlloc = int(0.99999 + (thrt-curAlloc)*1.5) newAlloc+=thisAlloc allocations.append( (sid, thisAlloc, False, 5) ) remainingMilRating -= thisAlloc otSysAlloc += thisAlloc if "Main" in thisround or thisAlloc >0: if verboseMilReporting: print "Monster Den %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc) if "Main" in thisround or newAlloc >0: if verboseMilReporting: print "-----------------" if remainingMilRating <=6: newAllocations = [ (sid, alc, alc, ta) for (sid, alc, ta, mm) in allocations ] else: #oldAllocations = dict( [ (entry[0], entry ) for entry in allocations ] ) try: totAlloc = sum( [alloc for sid, alloc, takeAny, maxAlloc in allocations ] ) except: print "error unpacking sid, alloc, takeAny, maxAlloc from ", allocations factor =(2.0* remainingMilRating ) / ( totAlloc + 0.1) #print "Remaining military strength allocation %d will be allocated as %.1f %% surplus allocation to top current priorities"%(remainingMilRating, 100*factor) print "%s Round Remaining military strength allocation %d will be allocated as surplus allocation to top current priorities"%(thisround, remainingMilRating) newAllocations = [] for cat in ['capitol', 'topTargets', 'otherTargets', 'accessibleTargets', 'occupied', 'exploreTargets']: for sid, alloc, takeAny, maxAlloc in allocationGroups.get(cat, []): if remainingMilRating <= 0 : newAllocations.append( ( sid, alloc, alloc, takeAny ) ) else: newRating = min(remainingMilRating+alloc, max(alloc, rating_needed( maxAlloc, alreadyAssignedRating[sid]) ) ) newAllocations.append( ( sid, newRating, alloc, takeAny ) ) remainingMilRating -= ( newRating - alloc ) if "Main" in thisround: MilitaryAllocations = newAllocations minMilAllocations.clear() minMilAllocations.update( [ (sid, alloc) for sid, alloc, takeAny, mm in allocations ] ) if verboseMilReporting or "Main" in thisround: print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------"%(thisround, dict( [ (sid, alloc) for sid, alloc, minalloc, takeAny in newAllocations ] ) ) # export military systems for other AI modules if "Main" in thisround: AIstate.militarySystemIDs = list( set([sid for sid, alloc, minalloc, takeAny in newAllocations]).union( [sid for sid in alreadyAssignedRating if alreadyAssignedRating[sid]>0 ] )) else: AIstate.militarySystemIDs = list( set([sid for sid, alloc, minalloc, takeAny in newAllocations]).union( AIstate.militarySystemIDs) ) return newAllocations
def get_military_fleets(mil_fleets_ids=None, try_reset=True, thisround="Main"): """Get armed military fleets.""" global military_allocations, totMilRating, num_milships universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID capital_id = PlanetUtilsAI.get_capital() if capital_id is None: homeworld = None else: homeworld = universe.getPlanet(capital_id) if homeworld: home_system_id = homeworld.systemID else: home_system_id = -1 if mil_fleets_ids is not None: all_military_fleet_ids = mil_fleets_ids else: all_military_fleet_ids = 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") num_milships = 0 for fid in all_military_fleet_ids: num_milships += foAI.foAIstate.fleetStatus.get(fid, {}).get('nships', 0) enemy_rating = foAI.foAIstate.empire_standard_enemy_rating 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(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0), mil_fleets_ids)) if "Main" in thisround: print "==================================================" print "%s Round Available Military Rating: %d" % (thisround, avail_mil_rating) print "---------------------------------" remaining_mil_rating = avail_mil_rating allocations = [] allocation_groups = {} 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 = {} assigned_attack = {} assigned_hp = {} systems_status = foAI.foAIstate.systemStatus enemy_sup_factor = {} # enemy supply for sys_id in universe.systemIDs: assigned_attack[sys_id] = 0 assigned_hp[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 assigned_attack[last_sys] += foAI.foAIstate.get_rating(fleet_id).get('attack', 0) assigned_hp[last_sys] += foAI.foAIstate.get_rating(fleet_id).get('health', 0) for sys_id in universe.systemIDs: mydefenses = systems_status.get(sys_id, {}).get('mydefenses', {}) mypattack = mydefenses.get('attack', 0) myphealth = mydefenses.get('health', 0) already_assigned_rating[sys_id] = (assigned_attack[sys_id] + mypattack) * (assigned_hp[sys_id] + myphealth) if verbose_mil_reporting and already_assigned_rating[sys_id]: print "\t System %s already assigned rating %.1f" % ( universe.getSystem(sys_id), already_assigned_rating[sys_id]) # get systems to defend capital_id = PlanetUtilsAI.get_capital() if capital_id is not None: capital_planet = universe.getPlanet(capital_id) else: capital_planet = None # TODO: if no owned planets try to capture one! if capital_planet: capital_sys_id = capital_planet.systemID else: # should be rare, but so as to not break code below, pick a randomish mil-centroid system capital_sys_id = None # unless we can find one to use system_dict = {} for fleet_id in all_military_fleet_ids: status = foAI.foAIstate.fleetStatus.get(fleet_id, None) if status is not None: sys_id = status['sysID'] if not list(universe.getSystem(sys_id).planetIDs): continue system_dict[sys_id] = system_dict.get(sys_id, 0) + status.get('rating', {}).get('overall', 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 if False: if fo.currentTurn() < 20: threat_bias = 0 elif fo.currentTurn() < 40: threat_bias = 10 elif fo.currentTurn() < 60: threat_bias = 80 elif fo.currentTurn() < 80: threat_bias = 200 else: threat_bias = 400 else: threat_bias = 0 safety_factor = get_safety_factor() num_targets = max(10, PriorityAI.allotted_outpost_targets) top_target_planets = ([pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allottedInvasionTargets] if pscore > 20] + [pid for pid, (pscore, spec) in foAI.foAIstate.colonisableOutpostIDs.items()[:num_targets] if pscore > 20] + [pid for pid, (pscore, spec) in foAI.foAIstate.colonisablePlanetIDs.items()[:num_targets] if pscore > 20]) top_target_planets.extend(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) base_col_target_systems = PlanetUtilsAI.get_systems(top_target_planets) top_target_systems = [] for sys_id in AIstate.invasionTargetedSystemIDs + base_col_target_systems: if sys_id not in top_target_systems: if foAI.foAIstate.systemStatus[sys_id]['totalThreat'] > totMilRating: continue top_target_systems.append(sys_id) # doing this rather than set, to preserve order if verbose_mil_reporting: print "----------------------------" # allocation format: ( sysID, newAllocation, takeAny, maxMultiplier ) # ================================ # --------Capital Threat ---------- if capital_sys_id is not None: capital_sys_status = systems_status[capital_sys_id] capital_threat = safety_factor*(2 * threat_bias + combine_ratings_list([capital_sys_status[thrt_key] for thrt_key in ['totalThreat', 'neighborThreat']])) capital_threat += max(0, enemy_sup_factor[sys_id]*enemy_rating - capital_sys_status.get('my_neighbor_rating', 0)) local_support = combine_ratings(already_assigned_rating[capital_sys_id], capital_sys_status['my_neighbor_rating']) base_needed_rating = rating_needed(capital_sys_status['regional_threat'], local_support) needed_rating = max(base_needed_rating, rating_needed(1.4 * capital_threat, already_assigned_rating[capital_sys_id])) max_alloc = max(rating_needed(1.5 * capital_sys_status['regional_threat'], already_assigned_rating[capital_sys_id]), rating_needed(2 * capital_threat, already_assigned_rating[capital_sys_id])) new_alloc = 0 if try_reset: if needed_rating > 0.5*avail_mil_rating: try_again(all_military_fleet_ids) return if needed_rating > 0: new_alloc = min(remaining_mil_rating, needed_rating) allocations.append((capital_sys_id, new_alloc, True, max_alloc)) allocation_groups.setdefault('capitol', []).append((capital_sys_id, new_alloc, True, max_alloc)) if verbose_mil_reporting: report_format = ("\tAt Capital system %s, local threat %.1f, regional threat %.1f, local support %.1f, " "base_needed_rating %.1f, needed_rating %.1f, new allocation %.1f") print report_format % (universe.getSystem(capital_sys_id), capital_threat, capital_sys_status['regional_threat'], local_support, base_needed_rating, needed_rating, new_alloc) remaining_mil_rating -= new_alloc if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "Empire Capital System: (%d) %s -- threat : %d, military allocation: existing: %d ; new: %d" % (capital_sys_id, universe.getSystem(capital_sys_id).name, capital_threat, already_assigned_rating[capital_sys_id], new_alloc) print "-----------------" # ================================ # --------Empire Occupied Systems ---------- 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}) if "Main" in thisround: if verbose_mil_reporting: print "Empire-Occupied Systems: %s" % (["| %d %s |" % (eo_sys_id, universe.getSystem(eo_sys_id).name) for eo_sys_id in empire_occupied_system_ids]) print "-----------------" new_alloc = 0 if empire_occupied_system_ids: oc_sys_tot_threat_v1 = [(o_s_id, threat_bias + safety_factor*combine_ratings_list( [systems_status.get(o_s_id, {}).get(thrt_key, 0) for thrt_key in ['totalThreat', 'neighborThreat']])) for o_s_id in empire_occupied_system_ids] tot_oc_sys_threat = sum([thrt for _, thrt in oc_sys_tot_threat_v1]) tot_cur_alloc = sum([already_assigned_rating[sid] for sid, _ in oc_sys_tot_threat_v1]) # intentionally after tallies, but perhaps should be before oc_sys_tot_threat = [] threat_details = {} for sys_id, sys_threat in oc_sys_tot_threat_v1: j2_threat = systems_status.get(sys_id, {}).get('jump2_threat', 0) local_defenses = combine_ratings_list([systems_status.get(sys_id, {}).get('my_neighbor_rating', 0), already_assigned_rating[sys_id]]) threat_details[sys_id] = (sys_threat, enemy_sup_factor[sys_id] * 0.5 * enemy_rating, j2_threat, local_defenses) oc_sys_tot_threat.append((sys_id, sys_threat + max(0, enemy_sup_factor[sys_id] * 0.5 * enemy_rating + j2_threat - local_defenses ))) oc_sys_alloc = 0 for sid, thrt in oc_sys_tot_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.3 * thrt, cur_alloc) max_alloc = rating_needed(2*thrt, cur_alloc) if (needed_rating > 0.8 * remaining_mil_rating) and try_reset: try_again(all_military_fleet_ids) return this_alloc = 0 if needed_rating > 0 and remaining_mil_rating > 0: this_alloc = max(0, min(needed_rating, 0.5 * avail_mil_rating, remaining_mil_rating)) new_alloc += this_alloc allocations.append((sid, this_alloc, True, max_alloc)) allocation_groups.setdefault('occupied', []).append((sid, this_alloc, True, max_alloc)) remaining_mil_rating -= this_alloc oc_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Provincial Occupied system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) print "\t base threat was %.1f, supply_threat %.1f, jump2_threat %.1f, and local defenses %.1f" % ( threat_details[sid]) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "Provincial Empire-Occupied Sytems under total threat: %d -- total mil allocation: existing %d ; new: %d" % (tot_oc_sys_threat, tot_cur_alloc, oc_sys_alloc) print "-----------------" # ================================ # --------Top Targeted Systems ---------- # TODO: do native invasions highest priority other_targeted_system_ids = top_target_systems if "Main" in thisround: if verbose_mil_reporting: print "Top Colony and Invasion Targeted Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids]) print "-----------------" new_alloc = 0 if other_targeted_system_ids: ot_sys_alloc = 0 ot_sys_threat = [(o_s_id, threat_bias + safety_factor * combine_ratings_list([ systems_status.get(o_s_id, {}).get('totalThreat', 0), 0.75 * systems_status.get(o_s_id, {}).get('neighborThreat', 0), 0.5 * systems_status.get(o_s_id, {}).get('jump2_threat', 0)])) for o_s_id in other_targeted_system_ids] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) # intentionally after tallies, but perhaps should be before ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.5 * enemy_rating) for sys_id, sys_threat in ot_sys_threat] for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.4*thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) max_alloc = rating_needed(3 * thrt, cur_alloc) new_alloc += this_alloc allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('topTargets', []).append((sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Top Colony and Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" # ================================ # --------Targeted Systems ---------- # TODO: do native invasions highest priority other_targeted_system_ids = [sys_id for sys_id in set(PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if sys_id not in top_target_systems] if "Main" in thisround: if verbose_mil_reporting: print "Other Invasion Targeted Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids]) print "-----------------" # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if other_targeted_system_ids: ot_sys_alloc = 0 ot_sys_threat = [(o_s_id, threat_bias + safety_factor*combine_ratings_list([ systems_status.get(o_s_id, {}).get('totalThreat', 0), 0.75*systems_status.get(o_s_id, {}).get('neighborThreat', 0), 0.5*systems_status.get(o_s_id, {}).get('jump2_threat', 0)])) for o_s_id in other_targeted_system_ids] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) # intentionally after tallies, but perhaps should be before ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.5 * enemy_rating) for sys_id, sys_threat in ot_sys_threat] for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.4 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = rating_needed(2 * thrt, cur_alloc) allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('otherTargets', []).append((sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" other_targeted_system_ids = [sys_id for sys_id in list(set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if sys_id not in top_target_systems] if "Main" in thisround: if verbose_mil_reporting: print "Other Targeted Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids]) print "-----------------" if other_targeted_system_ids: ot_sys_alloc = 0 ot_sys_threat = [(o_s_id, threat_bias + safety_factor*combine_ratings_list([ systems_status.get(o_s_id, {}).get('totalThreat', 0), 0.75*systems_status.get(o_s_id, {}).get('neighborThreat', 0), 0.5*systems_status.get(o_s_id, {}).get('jump2_threat', 0)])) for o_s_id in other_targeted_system_ids] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) # intentionally after tallies, but perhaps should be before ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.5 * enemy_rating) for sys_id, sys_threat in ot_sys_threat] tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) new_alloc = 0 for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.4 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = rating_needed(3*thrt, cur_alloc) allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('otherTargets', []).append((sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Other Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" other_targeted_system_ids = [] # targetable_ids = ColonisationAI.annexableSystemIDs.union(empire.fleetSupplyableSystemIDs) targetable_ids = set(ColonisationAI.systems_by_supply_tier.get(0, []) + ColonisationAI.systems_by_supply_tier.get(1, [])) for sys_id in AIstate.opponentSystemIDs: if sys_id in targetable_ids: other_targeted_system_ids.append(sys_id) else: for nID in universe.getImmediateNeighbors(sys_id, empire_id): if nID in targetable_ids: other_targeted_system_ids.append(sys_id) break if "Main" in thisround: if verbose_mil_reporting: print "Blockade Targeted Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids]) print "-----------------" if other_targeted_system_ids: ot_sys_alloc = 0 ot_sys_threat = [(o_s_id, threat_bias + safety_factor*combine_ratings_list([ systems_status.get(o_s_id, {}).get('totalThreat', 0), 0.75*systems_status.get(o_s_id, {}).get('neighborThreat', 0), 0.5*systems_status.get(o_s_id, {}).get('jump2_threat', 0)])) for o_s_id in other_targeted_system_ids] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) # intentionally after tallies, but perhaps should be before # this supply threat calc intentionally uses a lower multiplier 0.25 ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.25 * enemy_rating) for sys_id, sys_threat in ot_sys_threat] new_alloc = 0 for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.4*thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = 1.5 * this_alloc allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('otherTargets', []).append((sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Blockade Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Blockade Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" current_mil_systems = [sid for sid, alloc, take_any, mm in allocations] interior_targets1 = targetable_ids.difference(current_mil_systems) interior_targets = [sid for sid in interior_targets1 if ( threat_bias + systems_status.get(sid, {}).get('totalThreat', 0) > 0.8 * already_assigned_rating[sid])] if "Main" in thisround: if verbose_mil_reporting: print print "Other Empire-Proximal Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in interior_targets1]) print "-----------------" # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if interior_targets: ot_sys_alloc = 0 ot_sys_threat = [(o_s_id, threat_bias + safety_factor*(systems_status.get(o_s_id, {}).get('totalThreat', 0))) for o_s_id in interior_targets] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) # do not add enemy supply threat here for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.5 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = 3 * this_alloc allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('otherTargets', []).append((sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Other interior system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Other Interior Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" elif "Main" in thisround: if verbose_mil_reporting: print "-----------------" print "No Other Interior Systems with fleet threat " print "-----------------" monster_dens = [] # explo_target_ids, _ = ExplorationAI.get_current_exploration_info(verbose=False) explo_target_ids = [] if "Main" in thisround: if verbose_mil_reporting: print print "Exploration-targeted Systems: %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in explo_target_ids]) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if explo_target_ids: ot_sys_alloc = 0 ot_sys_threat = [(o_s_id, safety_factor*combine_ratings(systems_status.get(o_s_id, {}).get('fleetThreat', 0), systems_status.get(o_s_id, {}).get('monsterThreat', 0) + systems_status.get(o_s_id, {}).get('planetThreat', 0))) for o_s_id in explo_target_ids] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum([0.8*already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) # intentionally after tallies, but perhaps should be before # this supply threat calc intentionally uses a lower multiplier 0.25 ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.25 * enemy_rating) for sys_id, sys_threat in ot_sys_threat] for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.4 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = False if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = 2 * this_alloc allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('exploreTargets', []).append((sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Exploration-targeted %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Exploration-targeted s under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate. visBorderSystemIDs.keys() accessible_system_ids = [sys_id for sys_id in visible_system_ids if universe.systemsConnected(sys_id, home_system_id, empire_id)] current_mil_systems = [sid for sid, alloc, take_any, multiplier in 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 (threat_bias + systems_status.get(sid, {}).get('fleetThreat', 0) + systems_status.get(sid, {}).get('planetThreat', 0) > 0.8*already_assigned_rating[sid])] if "Main" in thisround: if verbose_mil_reporting: print print "Empire-Accessible Systems not yet allocated military: %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id) and universe.getSystem(sys_id).name) for sys_id in border_targets1]) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if border_targets: ot_sys_alloc = 0 ot_sys_threat = [(o_s_id, threat_bias + safety_factor*combine_ratings(systems_status.get(o_s_id, {}).get('fleetThreat', 0), systems_status.get(o_s_id, {}).get('monsterThreat', 0) + systems_status.get(o_s_id, {}).get('planetThreat', 0))) for o_s_id in border_targets] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum([0.8*already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) # do not add enemy supply threat here for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.2 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = False if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = safety_factor*2*max(systems_status.get(sid, {}).get('totalThreat', 0), systems_status.get(sid, {}).get('neighborThreat', 0)) allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('accessibleTargets', []).append((sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Other Empire-Accessible system %4d ( %10s ) has local biased threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Other Empire-Accessible Systems under total biased threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" elif "Main" in thisround: if verbose_mil_reporting: print "-----------------" print "No Other Empire-Accessible Systems with biased local threat " print "-----------------" # monster den treatment probably unnecessary now if "Main" in thisround: if verbose_mil_reporting: print print "Big-Monster Dens: %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in monster_dens]) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if monster_dens: ot_sys_alloc = 0 ot_sys_threat = [(o_s_id, safety_factor * combine_ratings_list([systems_status.get(o_s_id, {}).get('fleetThreat', 0), systems_status.get(o_s_id, {}).get('monsterThreat', 0), systems_status.get(o_s_id, {}).get('planetThreat', 0)])) for o_s_id in monster_dens] for sid, thrt in ot_sys_threat: cur_alloc = 0.8 * already_assigned_rating[sid] this_alloc = 0 if (thrt > cur_alloc) and remaining_mil_rating > 2 * thrt: this_alloc = int(0.99999 + (thrt - cur_alloc) * 1.5) new_alloc += this_alloc allocations.append((sid, this_alloc, False, 5)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Monster Den %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" 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, take_any, max_alloc in allocation_groups.get(cat, []): if remaining_mil_rating <= 0: break this_alloc = min(remaining_mil_rating, max_alloc) new_allocations.append((sid, this_alloc, alloc, 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, take_any, max_alloc in allocation_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, take_any, max_alloc in allocation_groups.get(cat, []): if sid not in base_allocs: break if remaining_mil_rating <= 0: new_allocations.append((sid, alloc, alloc, take_any)) else: new_rating = min(remaining_mil_rating + alloc, max_alloc) new_allocations.append((sid, new_rating, alloc, take_any)) remaining_mil_rating -= (new_rating - alloc) if "Main" in thisround: military_allocations = new_allocations minMilAllocations.clear() minMilAllocations.update([(sid, alloc) for sid, alloc, take_any, mm in allocations]) if verbose_mil_reporting or "Main" in thisround: print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % (thisround, dict([(sid, alloc) for sid, alloc, minalloc, take_any in new_allocations])) print "(Apparently) remaining military rating: %.1f" % remaining_mil_rating # export military systems for other AI modules if "Main" in thisround: AIstate.militarySystemIDs = list(set([sid for sid, alloc, minalloc, take_any in new_allocations]).union([sid for sid in already_assigned_rating if already_assigned_rating[sid] > 0])) else: AIstate.militarySystemIDs = list(set([sid for sid, alloc, minalloc, take_any in new_allocations]).union(AIstate.militarySystemIDs)) return new_allocations
def calculateMilitaryPriority(): """calculates the demand for military ships by military targeted systems""" global unmetThreat universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID capitalID = PlanetUtilsAI.get_capital() if capitalID is not None and capitalID != -1: homeworld = universe.getPlanet(capitalID) else: return 0 # no capitol (not even a capitol-in-the-making), means can't produce any ships have_l1_weaps = ( tech_is_complete("SHP_WEAPON_1_4") or (tech_is_complete("SHP_WEAPON_1_3") and tech_is_complete("SHP_MIL_ROBO_CONT")) or tech_is_complete("SHP_WEAPON_2_1") or tech_is_complete("SHP_WEAPON_4_1") ) have_l2_weaps = tech_is_complete("SHP_WEAPON_2_3") or tech_is_complete("SHP_WEAPON_4_1") enemies_sighted = foAI.foAIstate.misc.get("enemies_sighted", {}) allottedInvasionTargets = 1 + int(fo.currentTurn() / 25) targetPlanetIDs = ( [pid for pid, pscore, trp in AIstate.invasionTargets[:allottedInvasionTargets]] + [pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs.items()[:allottedColonyTargets]] + [pid for pid, pscore in foAI.foAIstate.colonisableOutpostIDs.items()[:allottedColonyTargets]] ) mySystems = set(AIstate.popCtrSystemIDs).union(AIstate.outpostSystemIDs) targetSystems = set(PlanetUtilsAI.get_systems(targetPlanetIDs)) curShipRating = ProductionAI.cur_best_military_design_rating() cSRR = curShipRating ** 0.5 defense_ships_needed = 0 currentTurn = fo.currentTurn() ships_needed = 0 defense_ships_needed = 0 ships_needed_allocation = [] for sysID in mySystems.union(targetSystems): status = foAI.foAIstate.systemStatus.get(sysID, {}) myRating = status.get("myFleetRating", 0) my_defenses = status.get("mydefenses", {}).get("overall", 0) baseMonsterThreat = status.get("monsterThreat", 0) # scale monster threat so that in early - mid game big monsters don't over-drive military production monsterThreat = baseMonsterThreat if currentTurn > 200: pass elif currentTurn > 100: if baseMonsterThreat >= 2000: monsterThreat = 2000 + (currentTurn / 100.0 - 1) * (baseMonsterThreat - 2000) elif currentTurn > 30: if baseMonsterThreat >= 2000: monsterThreat = 0 else: if baseMonsterThreat > 200: monsterThreat = 0 if sysID in mySystems: threatRoot = ( status.get("fleetThreat", 0) ** 0.5 + 0.8 * status.get("max_neighbor_threat", 0) ** 0.5 + 0.2 * status.get("neighborThreat", 0) ** 0.5 + monsterThreat ** 0.5 + status.get("planetThreat", 0) ** 0.5 ) else: threatRoot = ( status.get("fleetThreat", 0) ** 0.5 + monsterThreat ** 0.5 + status.get("planetThreat", 0) ** 0.5 ) ships_needed_here = math.ceil( (max(0, (threatRoot - (myRating ** 0.5 + my_defenses ** 0.5))) ** 2) / curShipRating ) ships_needed += ships_needed_here ships_needed_allocation.append((sysID, ships_needed_here)) if sysID in mySystems: defense_ships_needed += ships_needed_here part1 = min(1 * fo.currentTurn(), 40) part2 = max(0, int(75 * ships_needed)) militaryPriority = part1 + part2 if not have_l1_weaps: militaryPriority /= 2.0 elif not (have_l2_weaps and enemies_sighted): militaryPriority /= 1.5 fmt_string = "Calculating Military Priority: min(t,40) + max(0,75 * ships_needed) \n\t Priority: %d \t ships_needed: %d \t defense_ships_needed: %d \t curShipRating: %.0f \t l1_weaps: %s \t enemies_sighted: %s" print fmt_string % ( militaryPriority, ships_needed, defense_ships_needed, curShipRating, have_l1_weaps, enemies_sighted, ) print "Source of milship demand: ", ships_needed_allocation if foAI.foAIstate.aggression < fo.aggression.typical: militaryPriority *= (1.0 + foAI.foAIstate.aggression) / (1.0 + fo.aggression.typical) return max(militaryPriority, 0)
def get_invasion_fleets(): """get invasion fleets""" invasion_timer.start("gathering initial info") all_invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) AIstate.invasionFleetIDs = FleetUtilsAI.extract_fleet_ids_without_mission_types(all_invasion_fleet_ids) # get supplyable planets universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID capital_id = PlanetUtilsAI.get_capital() homeworld=None if capital_id: homeworld = universe.getPlanet(capital_id) if homeworld: home_system_id = homeworld.systemID else: home_system_id = -1 fleet_supplyable_system_ids = empire.fleetSupplyableSystemIDs fleet_supplyable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(fleet_supplyable_system_ids) prime_invadable_system_ids = set(ColonisationAI.annexableSystemIDs) prime_invadable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(prime_invadable_system_ids) visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate. visBorderSystemIDs.keys() if home_system_id != -1: accessible_system_ids = [sys_id for sys_id in visible_system_ids if (sys_id != -1 ) and universe.systemsConnected(sys_id, home_system_id, empire_id) ] else: print "Invasion Warning: this empire has no identifiable homeworld, will therefor treat all visible planets as accessible." accessible_system_ids = visible_system_ids #TODO: check if any troop ships still owned, use their system as home system acessible_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(accessible_system_ids) print "Accessible Systems: ", ", ".join(PlanetUtilsAI.sys_name_ids(accessible_system_ids)) print #all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids(exploredPlanetIDs) all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids(acessible_planet_ids)#need these for unpopulated outposts # print "All Owned and Populated PlanetIDs: " + str(all_owned_planet_ids) all_populated_planets = PlanetUtilsAI.get_populated_planet_ids(acessible_planet_ids)#need this for natives print "All Visible and accessible Populated PlanetIDs (including this empire's): ", ", ".join(PlanetUtilsAI.planet_name_ids(all_populated_planets )) print print "Prime Invadable Target Systems: ", ", ".join(PlanetUtilsAI.sys_name_ids(prime_invadable_system_ids)) print empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) # print "Empire Owned PlanetIDs: " + str(empire_owned_planet_ids) invadable_planet_ids = set(prime_invadable_planet_ids).intersection(set(all_owned_planet_ids).union(all_populated_planets ) - set(empire_owned_planet_ids)) print "Prime Invadable PlanetIDs: ", ", ".join(PlanetUtilsAI.planet_name_ids(invadable_planet_ids)) print print "Current Invasion Targeted SystemIDs: ", ", ".join(PlanetUtilsAI.sys_name_ids(AIstate.invasionTargetedSystemIDs)) invasion_targeted_planet_ids = get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, empire_id) invasion_targeted_planet_ids.extend( get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_ORBITAL_INVASION, empire_id)) all_invasion_targeted_system_ids = set(PlanetUtilsAI.get_systems(invasion_targeted_planet_ids)) print "Current Invasion Targeted PlanetIDs: ", ", ".join(PlanetUtilsAI.planet_name_ids(invasion_targeted_planet_ids)) invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) if not invasion_fleet_ids: print "Available Invasion Fleets: 0" else: print "Invasion FleetIDs: " + str(FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION)) num_invasion_fleets = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(invasion_fleet_ids)) print "Invasion Fleets Without Missions: " + str(num_invasion_fleets) invasion_timer.start("planning troop base production") # only do base invasions if aggression is typical or above reserved_troop_base_targets = [] if foAI.foAIstate.aggression > fo.aggression.typical: available_pp = {} for el in empire.planetsWithAvailablePP: #keys are sets of ints; data is doubles avail_pp = el.data() for pid in el.key(): available_pp[pid] = avail_pp if len (invadable_planet_ids) > 0: #print "Evaluating Troop Bases (SpaceInvaders) for %s"%(invadable_planet_ids) pass for pid in invadable_planet_ids: #TODO: reorganize planet = universe.getPlanet(pid) if not planet: continue sys_id = planet.systemID sys_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.systemID, empire_id)).get(fo.visibility.partial, -9999) planet_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(pid, empire_id)).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: #print "rejecting %s due to stealth"%planet.name continue for pid2 in ColonisationAI.empireSpeciesSystems.get(sys_id, {}).get('pids', []): if available_pp.get(pid2, 0) < 2: #TODO: improve troop base PP sufficiency determination #print "rejecting %s due to insufficient avail PP"%planet.name break planet2 = universe.getPlanet(pid2) if not planet2: continue if (pid not in foAI.foAIstate.qualifyingTroopBaseTargets) and (planet2.speciesName in ColonisationAI.empireShipBuilders): #print "Adding %s to Troop Bases (SpaceInvaders) potential target list, from %s"%(planet.name, planet2.name) foAI.foAIstate.qualifyingTroopBaseTargets.setdefault(pid, [pid2, -1]) break for pid in list(foAI.foAIstate.qualifyingTroopBaseTargets): planet = universe.getPlanet(pid) #TODO: also check that still have a colony in this system that can make troops if planet and planet.owner == empire_id: del foAI.foAIstate.qualifyingTroopBaseTargets[pid] secureAIFleetMissions = foAI.foAIstate.get_fleet_missions_with_any_mission_types([EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE]) #print "considering possible troop bases at %s" % (foAI.foAIstate.qualifyingTroopBaseTargets.keys()) for pid in (set(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) - set(invasion_targeted_planet_ids)): #TODO: consider overriding standard invasion mission planet = universe.getPlanet(pid) if foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] != -1: reserved_troop_base_targets.append(pid) if planet: all_invasion_targeted_system_ids.add( planet.systemID ) continue #already building for here sys_id = planet.systemID this_sys_status = foAI.foAIstate.systemStatus.get( sys_id, {} ) if ((planet.currentMeterValue(fo.meterType.shield) > 0) and (this_sys_status.get('myFleetRating', 0) < (0.8 * this_sys_status.get('totalThreat', 0)))): continue loc = foAI.foAIstate.qualifyingTroopBaseTargets[pid][0] this_score, p_troops = evaluate_invasion_planet(pid, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, fleet_supplyable_planet_ids, empire, secureAIFleetMissions, False) bestShip, colDesign, buildChoices = ProductionAI.getBestShipInfo(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_ORBITAL_INVASION, loc) if not bestShip: #print "Error: no troop base can be built at ", PlanetUtilsAI.planet_name_ids([loc]) continue #print "selecting ", PlanetUtilsAI.planet_name_ids([loc]), " to build Orbital troop bases" n_bases = math.ceil((p_troops+1) / 2)#TODO: reconsider this +1 safety factor retval = fo.issueEnqueueShipProductionOrder(bestShip, loc) print "Enqueueing %d Troop Bases at %s for %s"%( n_bases, PlanetUtilsAI.planet_name_ids([loc]), PlanetUtilsAI.planet_name_ids([pid])) if retval !=0: all_invasion_targeted_system_ids.add( planet.systemID ) reserved_troop_base_targets.append(pid) foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] = loc fo.issueChangeProductionQuantityOrder(empire.productionQueue.size -1, 1, int(n_bases)) res=fo.issueRequeueProductionOrder(empire.productionQueue.size -1, 0) invasion_timer.start("evaluating target planets") #TODO: check if any invasion_targeted_planet_ids need more troops assigned evaluatedPlanetIDs = list(set(invadable_planet_ids) - set(invasion_targeted_planet_ids) - set(reserved_troop_base_targets) ) print "Evaluating potential invasions, PlanetIDs: " + str(evaluatedPlanetIDs) evaluatedPlanets = assign_invasion_values(evaluatedPlanetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, fleet_supplyable_planet_ids, empire) sortedPlanets = [(pid, pscore, ptroops) for (pid, (pscore, ptroops)) in evaluatedPlanets.items() ] sortedPlanets.sort(lambda x, y: cmp(x[1], y[1]), reverse=True) sortedPlanets = [(pid, pscore%10000, ptroops) for (pid, pscore, ptroops) in sortedPlanets ] print if sortedPlanets: print "Invadable planets\nIDs, ID | Score | Name | Race | Troops" for pid, pscore, ptroops in sortedPlanets: planet = universe.getPlanet(pid) if planet: print "%6d | %6d | %16s | %16s | %d"%(pid, pscore, planet.name, planet.speciesName, ptroops) else: print "%6d | %6d | Error: invalid planet ID"%(pid, pscore) else: print "No Invadable planets identified" sortedPlanets = [(pid, pscore, ptroops) for (pid, pscore, ptroops) in sortedPlanets if pscore > 0] # export opponent planets for other AI modules AIstate.opponentPlanetIDs = [pid for pid, pscore, trp in sortedPlanets] AIstate.invasionTargets = sortedPlanets # export invasion targeted systems for other AI modules AIstate.invasionTargetedSystemIDs = list(all_invasion_targeted_system_ids) invasion_timer.stop(section_name="evaluating %d target planets" % (len(evaluatedPlanetIDs))) invasion_timer.end()
def _calculate_planet_colonization_rating( planet_id: PlanetId, mission_type: MissionType, species_name: SpeciesName, detail: list, empire_research_list: Sequence, ) -> float: empire = fo.getEmpire() retval = 0 character = get_aistate().character discount_multiplier = character.preferred_discount_multiplier([30.0, 40.0]) species = fo.getSpecies(species_name) species_foci = [] and species and list(species.foci) tag_list = list(species.tags) if species else [] pilot_val = pilot_rating = 0 if species and species.canProduceShips: pilot_val = pilot_rating = rate_piloting_tag(species_name) if pilot_val > best_pilot_rating(): pilot_val *= 2 if pilot_val > 2: retval += discount_multiplier * 5 * pilot_val detail.append("Pilot Val %.1f" % (discount_multiplier * 5 * pilot_val)) if empire.productionPoints < 100: backup_factor = 0.0 else: backup_factor = min(1.0, (empire.productionPoints / 200.0)**2) universe = fo.getUniverse() capital_id = PlanetUtilsAI.get_capital() homeworld = universe.getPlanet(capital_id) planet = universe.getPlanet(planet_id) prospective_invasion_targets = [ pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allotted_invasion_targets()] if pscore > InvasionAI.MIN_INVASION_SCORE ] if species_name != planet.speciesName and planet.speciesName and mission_type != MissionType.INVASION: return 0 this_sysid = planet.systemID if homeworld: home_system_id = homeworld.systemID eval_system_id = this_sysid if home_system_id != INVALID_ID and eval_system_id != INVALID_ID: least_jumps = universe.jumpDistance(home_system_id, eval_system_id) if least_jumps == -1: # indicates no known path return 0.0 if planet is None: vis_map = universe.getVisibilityTurnsMap(planet_id, empire.empireID) debug("Planet %d object not available; visMap: %s" % (planet_id, vis_map)) return 0 # only count existing presence if not target planet # TODO: consider neighboring sytems for smaller contribution, and bigger contributions for # local colonies versus local outposts locally_owned_planets = [ lpid for lpid in get_owned_planets_in_system(this_sysid) if lpid != planet_id ] planets_with_species = get_inhabited_planets() locally_owned_pop_ctrs = [ lpid for lpid in locally_owned_planets if lpid in planets_with_species ] # triple count pop_ctrs existing_presence = len( locally_owned_planets) + 2 * len(locally_owned_pop_ctrs) system = universe.getSystem(this_sysid) sys_supply = get_system_supply(this_sysid) planet_supply = AIDependencies.supply_by_size.get(planet.size, 0) bld_types = set( universe.getBuilding(bldg).buildingTypeName for bldg in planet.buildingIDs).intersection( AIDependencies.building_supply) planet_supply += sum( AIDependencies.building_supply[bld_type].get(int(psize), 0) for psize in [-1, planet.size] for bld_type in bld_types) supply_specials = set(planet.specials).union(system.specials).intersection( AIDependencies.SUPPLY_MOD_SPECIALS) planet_supply += sum( AIDependencies.SUPPLY_MOD_SPECIALS[_special].get(int(psize), 0) for _special in supply_specials for psize in [-1, planet.size]) ind_tag_mod = AIDependencies.SPECIES_INDUSTRY_MODIFIER.get( get_species_tag_grade(species_name, Tags.INDUSTRY), 1.0) res_tag_mod = AIDependencies.SPECIES_RESEARCH_MODIFIER.get( get_species_tag_grade(species_name, Tags.RESEARCH), 1.0) if species: supply_tag_mod = AIDependencies.SPECIES_SUPPLY_MODIFIER.get( get_species_tag_grade(species_name, Tags.SUPPLY), 1) else: supply_tag_mod = 0 # determine the potential supply provided by owning this planet, and if the planet is currently populated by # the evaluated species, then save this supply value in a cache. # The system supply value can be negative (indicates the respective number of starlane jumps to the closest supplied # system). So if this total planet supply value is non-negative, then the planet would be supply connected (to at # least a portion of the existing empire) if the planet becomes owned by the AI. planet_supply += supply_tag_mod planet_supply = max(planet_supply, 0) # planets can't have negative supply if planet.speciesName == species_name: update_planet_supply(planet_id, planet_supply + sys_supply) threat_factor = _determine_colony_threat_factor(planet_id, species_name, existing_presence) sys_partial_vis_turn = get_partial_visibility_turn(this_sysid) planet_partial_vis_turn = get_partial_visibility_turn(planet_id) if planet_partial_vis_turn < sys_partial_vis_turn: # last time we had partial vis of the system, the planet was stealthed to us # print "Colonization AI couldn't get current info on planet id %d (was stealthed at last sighting)" % planet_id return 0 # TODO: track detection strength, order new scouting when it goes up star_bonus = 0 colony_star_bonus = 0 research_bonus = 0 growth_val = 0 fixed_ind = 0 fixed_res = 0 if system: already_got_this_one = this_sysid in get_owned_planets() # TODO: Should probably consider pilot rating also for Phototropic species if "PHOTOTROPHIC" not in tag_list and pilot_rating >= best_pilot_rating( ): if system.starType == fo.starType.red and tech_is_complete( "LRN_STELLAR_TOMOGRAPHY"): star_bonus += 40 * discount_multiplier # can be used for artif'l black hole and solar hull detail.append( "Red Star for Art Black Hole for solar hull %.1f" % (40 * discount_multiplier)) elif system.starType == fo.starType.blackHole and tech_is_complete( "SHP_FRC_ENRG_COMP"): star_bonus += 100 * discount_multiplier # can be used for solar hull detail.append("Black Hole for solar hull %.1f" % (100 * discount_multiplier)) if tech_is_complete("PRO_SOL_ORB_GEN" ) or "PRO_SOL_ORB_GEN" in empire_research_list[:5]: if system.starType in [fo.starType.blue, fo.starType.white]: if not has_claimed_star(fo.starType.blue, fo.starType.white): star_bonus += 20 * discount_multiplier detail.append("PRO_SOL_ORB_GEN BW %.1f" % (20 * discount_multiplier)) elif not already_got_this_one: # still has extra value as an alternate location for solar generators star_bonus += 10 * discount_multiplier * backup_factor detail.append("PRO_SOL_ORB_GEN BW Backup Location %.1f" % (10 * discount_multiplier * backup_factor)) elif fo.currentTurn() > 100: # lock up this whole system pass # starBonus += 5 # TODO: how much? # detail.append("PRO_SOL_ORB_GEN BW LockingDownSystem %.1f"%5) if system.starType in [fo.starType.yellow, fo.starType.orange]: if not has_claimed_star(fo.starType.blue, fo.starType.white, fo.starType.yellow, fo.starType.orange): star_bonus += 10 * discount_multiplier detail.append("PRO_SOL_ORB_GEN YO %.1f" % (10 * discount_multiplier)) else: pass # starBonus +=2 # still has extra value as an alternate location for solar generators # detail.append("PRO_SOL_ORB_GEN YO Backup %.1f" % 2) if system.starType in [fo.starType.blackHole ] and fo.currentTurn() > 100: if not already_got_this_one: # whether have tech yet or not, assign some base value star_bonus += 10 * discount_multiplier * backup_factor detail.append("Black Hole %.1f" % (10 * discount_multiplier * backup_factor)) else: star_bonus += 5 * discount_multiplier * backup_factor detail.append("Black Hole Backup %.1f" % (5 * discount_multiplier * backup_factor)) if tech_is_complete(AIDependencies.PRO_SOL_ORB_GEN ): # start valuing as soon as PRO_SOL_ORB_GEN done if system.starType == fo.starType.blackHole: # pretty rare planets, good for generator this_val = 0.5 * max(population_with_industry_focus(), 20) * discount_multiplier if not has_claimed_star(fo.starType.blackHole): star_bonus += this_val detail.append("PRO_SINGULAR_GEN %.1f" % this_val) elif not is_system_star_claimed(system): # still has extra value as an alternate location for generators & for blocking enemies generators star_bonus += this_val * backup_factor detail.append("PRO_SINGULAR_GEN Backup %.1f" % (this_val * backup_factor)) elif system.starType == fo.starType.red and not has_claimed_star( fo.starType.blackHole): rfactor = (1.0 + count_claimed_stars(fo.starType.red))**-2 star_bonus += 40 * discount_multiplier * backup_factor * rfactor # can be used for artif'l black hole detail.append( "Red Star for Art Black Hole %.1f" % (40 * discount_multiplier * backup_factor * rfactor)) if tech_is_complete( "PRO_NEUTRONIUM_EXTRACTION" ) or "PRO_NEUTRONIUM_EXTRACTION" in empire_research_list[:8]: if system.starType in [fo.starType.neutron]: if not has_claimed_star(fo.starType.neutron): star_bonus += 80 * discount_multiplier # pretty rare planets, good for armor detail.append("PRO_NEUTRONIUM_EXTRACTION %.1f" % (80 * discount_multiplier)) else: # still has extra value as an alternate location for generators & for bnlocking enemies generators star_bonus += 20 * discount_multiplier * backup_factor detail.append("PRO_NEUTRONIUM_EXTRACTION Backup %.1f" % (20 * discount_multiplier * backup_factor)) if tech_is_complete( "SHP_ENRG_BOUND_MAN" ) or "SHP_ENRG_BOUND_MAN" in empire_research_list[:6]: # TODO: base this on pilot val, and also consider red stars if system.starType in [fo.starType.blackHole, fo.starType.blue]: init_val = 100 * discount_multiplier * (pilot_val or 1) if not has_claimed_star(fo.starType.blackHole, fo.starType.blue): colony_star_bonus += init_val # pretty rare planets, good for energy shipyards detail.append("SHP_ENRG_BOUND_MAN %.1f" % init_val) elif not is_system_star_claimed(system): # still has extra value as an alternate location for energy shipyard colony_star_bonus += 0.5 * init_val * backup_factor detail.append("SHP_ENRG_BOUND_MAN Backup %.1f" % (0.5 * init_val * backup_factor)) retval += star_bonus planet_specials = list(planet.specials) if "ECCENTRIC_ORBIT_SPECIAL" in planet.specials: fixed_res += discount_multiplier * 6 detail.append("ECCENTRIC_ORBIT_SPECIAL %.1f" % (discount_multiplier * 6)) if mission_type == MissionType.OUTPOST or ( mission_type == MissionType.INVASION and not species_name): if "ANCIENT_RUINS_SPECIAL" in planet.specials: # TODO: add value for depleted ancient ruins retval += discount_multiplier * 30 detail.append("Undepleted Ruins %.1f" % discount_multiplier * 30) for special in planet_specials: if "_NEST_" in special: nest_val = get_nest_rating( special, 5.0 ) * discount_multiplier # get an outpost on the nest quick retval += nest_val detail.append("%s %.1f" % (special, nest_val)) elif special == "FORTRESS_SPECIAL": fort_val = 10 * discount_multiplier retval += fort_val detail.append("%s %.1f" % (special, fort_val)) elif special == "HONEYCOMB_SPECIAL": honey_val = 0.3 * (AIDependencies.HONEYCOMB_IND_MULTIPLIER * AIDependencies.INDUSTRY_PER_POP * population_with_industry_focus() * discount_multiplier) retval += honey_val detail.append("%s %.1f" % (special, honey_val)) if planet.size == fo.planetSize.asteroids: ast_val = 0 if system: for pid in system.planetIDs: other_planet = universe.getPlanet(pid) if other_planet.size == fo.planetSize.asteroids: if pid == planet_id: continue elif pid < planet_id and planet.unowned: ast_val = 0 break elif other_planet.speciesName: if other_planet.owner == empire.empireID: ownership_factor = 1.0 elif pid in prospective_invasion_targets: ownership_factor = character.secondary_valuation_factor_for_invasion_targets( ) else: ownership_factor = 0.0 ast_val += ownership_factor * _base_asteroid_mining_val( ) * discount_multiplier retval += ast_val if ast_val > 0: detail.append("AsteroidMining %.1f" % ast_val) ast_val = 0 if tech_is_complete("SHP_ASTEROID_HULLS"): per_ast = 20 elif tech_is_complete("CON_ORBITAL_CON"): per_ast = 5 else: per_ast = 0.1 if system: for pid in system.planetIDs: other_planet = universe.getPlanet(pid) if other_planet.size == fo.planetSize.asteroids: if pid == planet_id: continue elif pid < planet_id and planet.unowned: ast_val = 0 break elif other_planet.speciesName: other_species = fo.getSpecies(other_planet.speciesName) if other_species and other_species.canProduceShips: if other_planet.owner == empire.empireID: ownership_factor = 1.0 elif pid in prospective_invasion_targets: ownership_factor = character.secondary_valuation_factor_for_invasion_targets( ) else: ownership_factor = 0.0 ast_val += ownership_factor * per_ast * discount_multiplier retval += ast_val if ast_val > 0: detail.append("AsteroidShipBuilding %.1f" % ast_val) # We will assume that if any GG in the system is populated, they all will wind up populated; cannot then hope # to build a GGG on a non-populated GG populated_gg_factor = 1.0 per_gg = 0 if planet.size == fo.planetSize.gasGiant: # TODO: Given current industry calc approach, consider bringing this max val down to actual max val of 10 if tech_is_complete("PRO_ORBITAL_GEN"): per_gg = 20 elif tech_is_complete("CON_ORBITAL_CON"): per_gg = 10 if species_name: populated_gg_factor = 0.5 else: per_gg = 5 if system: gg_list = [] orb_gen_val = 0 gg_detail = [] for pid in system.planetIDs: other_planet = universe.getPlanet(pid) if other_planet.size == fo.planetSize.gasGiant: gg_list.append(pid) if other_planet.speciesName: populated_gg_factor = 0.5 if (pid != planet_id and other_planet.owner == empire.empireID and FocusType.FOCUS_INDUSTRY in list(other_planet.availableFoci) + [other_planet.focus]): orb_gen_val += per_gg * discount_multiplier # Note, this reported value may not take into account a later adjustment from a populated gg gg_detail.append("GGG for %s %.1f" % (other_planet.name, discount_multiplier * per_gg * populated_gg_factor)) if planet_id in sorted(gg_list)[:1]: retval += orb_gen_val * populated_gg_factor detail.extend(gg_detail) else: detail.append("Won't GGG") if existing_presence: detail.append("preexisting system colony") retval = (retval + existing_presence * _get_defense_value(species_name)) * 1.5 # Fixme - sys_supply is always <= 0 leading to incorrect supply bonus score supply_val = 0 if sys_supply < 0: if sys_supply + planet_supply >= 0: supply_val += 30 * (planet_supply - max(-3, sys_supply)) else: retval += 30 * (planet_supply + sys_supply) # a penalty elif planet_supply > sys_supply and ( sys_supply < 2): # TODO: check min neighbor supply supply_val += 25 * (planet_supply - sys_supply) detail.append("sys_supply: %d, planet_supply: %d, supply_val: %.0f" % (sys_supply, planet_supply, supply_val)) retval += supply_val if threat_factor < 1.0: threat_factor = _revise_threat_factor(threat_factor, retval, this_sysid, MINIMUM_COLONY_SCORE) retval *= threat_factor detail.append("threat reducing value by %3d %%" % (100 * (1 - threat_factor))) return int(retval) else: # colonization mission if not species: return 0 supply_val = 0 if "ANCIENT_RUINS_SPECIAL" in planet.specials: retval += discount_multiplier * 50 detail.append("Undepleted Ruins %.1f" % discount_multiplier * 50) if "HONEYCOMB_SPECIAL" in planet.specials: honey_val = (AIDependencies.HONEYCOMB_IND_MULTIPLIER * AIDependencies.INDUSTRY_PER_POP * population_with_industry_focus() * discount_multiplier) if FocusType.FOCUS_INDUSTRY not in species_foci: honey_val *= -0.3 # discourage settlement by colonizers not able to use Industry Focus retval += honey_val detail.append("%s %.1f" % ("HONEYCOMB_SPECIAL", honey_val)) # Fixme - sys_supply is always <= 0 leading to incorrect supply bonus score if sys_supply <= 0: if sys_supply + planet_supply >= 0: supply_val = 40 * (planet_supply - max(-3, sys_supply)) else: supply_val = 200 * (planet_supply + sys_supply) # a penalty if species_name == "SP_SLY": # Sly are essentially stuck with lousy supply, so don't penalize for that supply_val = 0 elif planet_supply > sys_supply == 1: # TODO: check min neighbor supply supply_val = 20 * (planet_supply - sys_supply) detail.append("sys_supply: %d, planet_supply: %d, supply_val: %.0f" % (sys_supply, planet_supply, supply_val)) # if AITags != "": # print "Species %s has AITags %s"%(specName, AITags) retval += fixed_res retval += colony_star_bonus asteroid_bonus = 0 gas_giant_bonus = 0 flat_industry = 0 mining_bonus = 0 per_ggg = 10 asteroid_factor = 0.0 gg_factor = 0.0 ast_shipyard_name = "" if system and FocusType.FOCUS_INDUSTRY in species.foci: for pid in system.planetIDs: if pid == planet_id: continue p2 = universe.getPlanet(pid) if p2: if p2.size == fo.planetSize.asteroids: this_factor = 0.0 if p2.owner == empire.empireID: this_factor = 1.0 elif p2.unowned: this_factor = 0.5 elif pid in prospective_invasion_targets: this_factor = character.secondary_valuation_factor_for_invasion_targets( ) if this_factor > asteroid_factor: asteroid_factor = this_factor ast_shipyard_name = p2.name if p2.size == fo.planetSize.gasGiant: if p2.owner == empire.empireID: gg_factor = max(gg_factor, 1.0) elif p2.unowned: gg_factor = max(gg_factor, 0.5) elif pid in prospective_invasion_targets: gg_factor = max( gg_factor, character. secondary_valuation_factor_for_invasion_targets( )) if asteroid_factor > 0.0: if tech_is_complete( "PRO_MICROGRAV_MAN" ) or "PRO_MICROGRAV_MAN" in empire_research_list[:10]: flat_industry += 2 * asteroid_factor # will go into detailed industry projection detail.append("Asteroid mining ~ %.1f" % (5 * asteroid_factor * discount_multiplier)) if tech_is_complete( "SHP_ASTEROID_HULLS" ) or "SHP_ASTEROID_HULLS" in empire_research_list[:11]: if species and species.canProduceShips: asteroid_bonus = 30 * discount_multiplier * pilot_val detail.append("Asteroid ShipBuilding from %s %.1f" % (ast_shipyard_name, discount_multiplier * 30 * pilot_val)) if gg_factor > 0.0: if tech_is_complete( "PRO_ORBITAL_GEN" ) or "PRO_ORBITAL_GEN" in empire_research_list[:5]: flat_industry += per_ggg * gg_factor # will go into detailed industry projection detail.append("GGG ~ %.1f" % (per_ggg * gg_factor * discount_multiplier)) # calculate the maximum population of the species on that planet. if planet.speciesName not in AIDependencies.SPECIES_FIXED_POPULATION: max_pop_size = calc_max_pop(planet, species, detail) else: max_pop_size = AIDependencies.SPECIES_FIXED_POPULATION[ planet.speciesName] detail.append("Fixed max population of %.2f" % max_pop_size) if max_pop_size <= 0: detail.append( "Non-positive population projection for species '%s', so no colonization value" % (species and species.name)) return 0 for special in [ "MINERALS_SPECIAL", "CRYSTALS_SPECIAL", "ELERIUM_SPECIAL" ]: if special in planet_specials: mining_bonus += 1 has_blackhole = has_claimed_star(fo.starType.blackHole) ind_tech_map_flat = AIDependencies.INDUSTRY_EFFECTS_FLAT_NOT_MODIFIED_BY_SPECIES ind_tech_map_before_species_mod = AIDependencies.INDUSTRY_EFFECTS_PER_POP_MODIFIED_BY_SPECIES ind_tech_map_after_species_mod = AIDependencies.INDUSTRY_EFFECTS_PER_POP_NOT_MODIFIED_BY_SPECIES ind_mult = 1 for tech in ind_tech_map_before_species_mod: if tech_is_complete(tech) and ( tech != AIDependencies.PRO_SINGULAR_GEN or has_blackhole): ind_mult += ind_tech_map_before_species_mod[tech] ind_mult = ind_mult * max( ind_tag_mod, 0.5 * (ind_tag_mod + res_tag_mod)) # TODO: report an actual calc for research value for tech in ind_tech_map_after_species_mod: if tech_is_complete(tech) and ( tech != AIDependencies.PRO_SINGULAR_GEN or has_blackhole): ind_mult += ind_tech_map_after_species_mod[tech] max_ind_factor = 0 for tech in ind_tech_map_flat: if tech_is_complete(tech): fixed_ind += discount_multiplier * ind_tech_map_flat[tech] if FocusType.FOCUS_INDUSTRY in species.foci: if "TIDAL_LOCK_SPECIAL" in planet.specials: ind_mult += 1 max_ind_factor += AIDependencies.INDUSTRY_PER_POP * mining_bonus max_ind_factor += AIDependencies.INDUSTRY_PER_POP * ind_mult cur_pop = 1.0 # assume an initial colonization value if planet.speciesName != "": cur_pop = planet.currentMeterValue(fo.meterType.population) elif tech_is_complete("GRO_LIFECYCLE_MAN"): cur_pop = 3.0 cur_industry = planet.currentMeterValue(fo.meterType.industry) ind_val = _project_ind_val(cur_pop, max_pop_size, cur_industry, max_ind_factor, flat_industry, discount_multiplier) detail.append("ind_val %.1f" % ind_val) # used to give preference to closest worlds for special in [ spec for spec in planet_specials if spec in AIDependencies.metabolismBoosts ]: # TODO: also consider potential future benefit re currently unpopulated planets gbonus = (discount_multiplier * AIDependencies.INDUSTRY_PER_POP * ind_mult * empire_metabolisms.get( AIDependencies.metabolismBoosts[special], 0) ) # due to growth applicability to other planets growth_val += gbonus detail.append("Bonus for %s: %.1f" % (special, gbonus)) if FocusType.FOCUS_RESEARCH in species.foci: research_bonus += discount_multiplier * 2 * AIDependencies.RESEARCH_PER_POP * max_pop_size if "ANCIENT_RUINS_SPECIAL" in planet.specials or "ANCIENT_RUINS_DEPLETED_SPECIAL" in planet.specials: research_bonus += discount_multiplier * 2 * AIDependencies.RESEARCH_PER_POP * max_pop_size * 5 detail.append("Ruins Research") if "TEMPORAL_ANOMALY_SPECIAL" in planet.specials: research_bonus += discount_multiplier * 2 * AIDependencies.RESEARCH_PER_POP * max_pop_size * 25 detail.append("Temporal Anomaly Research") if AIDependencies.COMPUTRONIUM_SPECIAL in planet.specials: comp_bonus = (0.5 * AIDependencies.TECH_COST_MULTIPLIER * AIDependencies.RESEARCH_PER_POP * AIDependencies.COMPUTRONIUM_RES_MULTIPLIER * population_with_research_focus() * discount_multiplier) if have_computronium(): comp_bonus *= backup_factor research_bonus += comp_bonus detail.append(AIDependencies.COMPUTRONIUM_SPECIAL) retval += (max(ind_val + asteroid_bonus + gas_giant_bonus, research_bonus, growth_val) + fixed_ind + fixed_res + supply_val) if existing_presence: detail.append("preexisting system colony") retval = (retval + existing_presence * _get_defense_value(species_name)) * 2 if threat_factor < 1.0: threat_factor = _revise_threat_factor(threat_factor, retval, this_sysid, MINIMUM_COLONY_SCORE) retval *= threat_factor detail.append("threat reducing value by %3d %%" % (100 * (1 - threat_factor))) return retval
def inspect_ai_interface(): capital_id = PlanetUtilsAI.get_capital() universe = fo.getUniverse() fleets_int_vector = universe.fleetIDs fleet = universe.getFleet(list(fleets_int_vector)[0]) ship = universe.getShip(list(universe.shipIDs)[0]) design = fo.getShipDesign(ship.designID) empire = fo.getEmpire() tech = fo.getTech('SHP_WEAPON_2_1') tech_spec = list(tech.unlockedItems)[0] part_id = list(empire.availableShipParts)[0] part_type = fo.getPartType(part_id) prod_queue = empire.productionQueue fo.issueEnqueueShipProductionOrder(list(empire.availableShipDesigns)[0], capital_id) research_queue = empire.researchQueue fo.issueEnqueueTechOrder('SHP_WEAPON_1_2', -1) planet = universe.getPlanet(capital_id) building = list(planet.buildingIDs)[0] color = empire.colour part_meters = ship.partMeters meter = planet.getMeter(fo.meterType.population) inspect( fo, instances=[ meter, part_meters, color, universe, fleet, planet, universe.getSystem(planet.systemID), ship, empire, design, tech, tech_spec, fo.getFieldType('FLD_ION_STORM'), fo.getBuildingType('BLD_SHIPYARD_BASE'), fo.getGalaxySetupData(), fo.getHullType('SH_XENTRONIUM'), fo.getPartType('SR_WEAPON_1_1'), fo.getSpecial('MODERATE_TECH_NATIVES_SPECIAL'), fo.getSpecies('SP_ABADDONI'), fo.getTech('SHP_WEAPON_4_1'), fo.diplomaticMessage(1, 2, fo.diplomaticMessageType.acceptPeaceProposal), fleets_int_vector, part_type, prod_queue, prod_queue.allocatedPP, prod_queue[0], research_queue, research_queue[0], empire.getSitRep(0), universe.getBuilding(building) ], classes_to_ignore=( 'IntSet', 'StringSet', 'IntIntMap', 'ShipSlotVec', 'VisibilityIntMap', 'IntDblMap', 'IntBoolMap', 'ItemSpecVec', 'PairIntInt_IntMap', 'IntSetSet', 'StringVec', 'IntPairVec', 'IntFltMap', 'MeterTypeStringPair', 'MeterTypeMeterMap', 'universeObject', # this item cannot be get from generate orders 'diplomaticStatusUpdate', ), path='AI' ) exit(1) # exit game to main menu no need to play anymore.
def generateOrders(): # pylint: disable=invalid-name """Called once per turn to tell the Python AI to generate and issue orders to control its empire. at end of this function, fo.doneTurn() should be called to indicate to the client that orders are finished and can be sent to the server for processing.""" turn = fo.currentTurn() turn_uid = foAIstate.set_turn_uid() print "Start turn %s (%s) of game: %s" % (turn, turn_uid, foAIstate.uid) turn_timer.start("AI planning") universe = fo.getUniverse() empire = fo.getEmpire() planet_id = PlanetUtilsAI.get_capital() # set the random seed (based on galaxy seed, empire ID and current turn) # for game-reload consistency random_seed = str(fo.getGalaxySetupData().seed) + "%03d%05d" % (fo.empireID(), turn) random.seed(random_seed) planet = None if planet_id is not None: planet = universe.getPlanet(planet_id) aggression_name = fo.aggression.values[foAIstate.aggression].name print "***************************************************************************" print "********** String for chart. Do not modify. ***************************" print ("Generating Orders") print ("EmpireID: {empire.empireID}" " Name: {empire.name}_{empire.empireID}_pid:{p_id}_{p_name}RIdx_{res_idx}_{aggression}" " Turn: {turn}").format(empire=empire, p_id=fo.playerID(), p_name=fo.playerName(), res_idx=ResearchAI.get_research_index(), turn=turn, aggression=aggression_name.capitalize()) print "EmpireColors: {0.colour.r} {0.colour.g} {0.colour.b} {0.colour.a}".format(empire) if planet: print "CapitalID: " + str(planet_id) + " Name: " + planet.name + " Species: " + planet.speciesName else: print "CapitalID: None Currently Name: None Species: None " print "***************************************************************************" print "***************************************************************************" if turn == 1: declare_war_on_all() human_player = fo.empirePlayerID(1) fo.sendChatMessage(human_player, '%s Empire (%s):\n"Ave, Human, morituri te salutant!"' % (empire.name, aggression_name)) # turn cleanup !!! this was formerly done at start of every turn -- not sure why foAIstate.split_new_fleets() foAIstate.refresh() # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats foAIstate.report_system_threats() # ...missions # ...demands/priorities print("Calling AI Modules") # call AI modules action_list = [ColonisationAI.survey_universe, ProductionAI.find_best_designs_this_turn, PriorityAI.calculate_priorities, ExplorationAI.assign_scouts_to_explore_systems, ColonisationAI.assign_colony_fleets_to_colonise, InvasionAI.assign_invasion_fleets_to_invade, MilitaryAI.assign_military_fleets_to_systems, FleetUtilsAI.generate_fleet_orders_for_fleet_missions, FleetUtilsAI.issue_fleet_orders_for_fleet_missions, ResearchAI.generate_research_orders, ProductionAI.generateProductionOrders, ResourcesAI.generate_resources_orders, foAIstate.after_turn_cleanup, ] for action in action_list: try: main_timer.start(action.__name__) action() main_timer.stop() except Exception as e: print_error(e, location=action.__name__) main_timer.end() turn_timer.end() turn_timer.start("Server_Processing") try: fo.doneTurn() except Exception as e: print_error(e) # TODO move it to cycle above if using_statprof: try: statprof.stop() statprof.display() statprof.start() except: pass
def 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 _calculate_military_priority(): """Calculates the demand for military ships by military targeted systems.""" global unmetThreat universe = fo.getUniverse() capital_id = PlanetUtilsAI.get_capital() if capital_id is None or capital_id == INVALID_ID: return 0 # no capitol (not even a capitol-in-the-making), means can't produce any ships have_l1_weaps = (tech_is_complete("SHP_WEAPON_1_4") or (tech_is_complete("SHP_WEAPON_1_3") and tech_is_complete("SHP_MIL_ROBO_CONT")) or tech_is_complete("SHP_WEAPON_2_1") or tech_is_complete("SHP_WEAPON_4_1")) have_l2_weaps = (tech_is_complete("SHP_WEAPON_2_3") or tech_is_complete("SHP_WEAPON_4_1")) enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) allotted_invasion_targets = 1 + int(fo.currentTurn()/25) target_planet_ids = [pid for pid, pscore, trp in AIstate.invasionTargets[:allotted_invasion_targets]] + [pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs.items()[:allottedColonyTargets]] + [pid for pid, pscore in foAI.foAIstate.colonisableOutpostIDs.items()[:allottedColonyTargets]] my_systems = set(AIstate.popCtrSystemIDs).union(AIstate.outpostSystemIDs) target_systems = set(PlanetUtilsAI.get_systems(target_planet_ids)) cur_ship_rating = ProductionAI.cur_best_military_design_rating() current_turn = fo.currentTurn() ships_needed = 0 defense_ships_needed = 0 ships_needed_allocation = [] for sys_id in my_systems.union(target_systems): status = foAI.foAIstate.systemStatus.get(sys_id, {}) my_rating = status.get('myFleetRating', 0) my_defenses = status.get('mydefenses', {}).get('overall', 0) base_monster_threat = status.get('monsterThreat', 0) # scale monster threat so that in early - mid game big monsters don't over-drive military production monster_threat = base_monster_threat if current_turn > 200: pass elif current_turn > 100: if base_monster_threat >= 2000: monster_threat = 2000 + (current_turn / 100.0 - 1) * (base_monster_threat - 2000) elif current_turn > 30: if base_monster_threat >= 2000: monster_threat = 0 else: if base_monster_threat > 200: monster_threat = 0 if sys_id in my_systems: threat_root = status.get('fleetThreat', 0)**0.5 + 0.8*status.get('max_neighbor_threat', 0)**0.5 + 0.2*status.get('neighborThreat', 0)**0.5 + monster_threat**0.5 + status.get('planetThreat', 0)**0.5 else: threat_root = status.get('fleetThreat', 0)**0.5 + monster_threat**0.5 + status.get('planetThreat', 0)**0.5 ships_needed_here = math.ceil((max(0, (threat_root - (my_rating ** 0.5 + my_defenses ** 0.5))) ** 2) / cur_ship_rating) ships_needed += ships_needed_here ships_needed_allocation.append((universe.getSystem(sys_id), ships_needed_here)) if sys_id in my_systems: defense_ships_needed += ships_needed_here part1 = min(1 * fo.currentTurn(), 40) part2 = max(0, int(75 * ships_needed)) military_priority = part1 + part2 if not have_l1_weaps: military_priority /= 2.0 elif not (have_l2_weaps and enemies_sighted): military_priority /= 1.5 fmt_string = "Calculating Military Priority: min(t,40) + max(0,75 * ships_needed) \n\t Priority: %d \t ships_needed: %d \t defense_ships_needed: %d \t curShipRating: %.0f \t l1_weaps: %s \t enemies_sighted: %s" print fmt_string % (military_priority, ships_needed, defense_ships_needed, cur_ship_rating, have_l1_weaps, enemies_sighted) print "Source of milship demand: ", ships_needed_allocation military_priority *= foAI.foAIstate.character.military_priority_scaling() return max(military_priority, 0)
def generateOrders(): # pylint: disable=invalid-name """ Called once per turn to tell the Python AI to generate and issue orders, i.e. to control its empire. After leaving this function, the AI's turn will be finished and its orders will be sent to the server. """ try: rules = fo.getGameRules() debug("Defined game rules:") for rule_name, rule_value in rules.getRulesAsStrings().items(): debug("%s: %s", rule_name, rule_value) debug("Rule RULE_NUM_COMBAT_ROUNDS value: " + str(rules.getInt("RULE_NUM_COMBAT_ROUNDS"))) except Exception as e: error("Exception %s when trying to get game rules" % e, exc_info=True) # If nothing can be ordered anyway, exit early. # Note that there is no need to update meters etc. in this case. empire = fo.getEmpire() if empire is None: fatal("This client has no empire. Aborting order generation.") return if empire.eliminated: info("This empire has been eliminated. Aborting order generation.") return # This code block is required for correct AI work. info("Meter / Resource Pool updating...") fo.initMeterEstimatesDiscrepancies() fo.updateMeterEstimates(False) fo.updateResourcePools() turn = fo.currentTurn() aistate = get_aistate() debug("\n\n\n" + "=" * 20) debug(f"Starting turn {turn}") debug("=" * 20 + "\n") turn_timer.start("AI planning") # set the random seed (based on galaxy seed, empire name and current turn) # for game-reload consistency. random_seed = str( fo.getGalaxySetupData().seed) + "%05d%s" % (turn, fo.getEmpire().name) random.seed(random_seed) empire = fo.getEmpire() aggression_name = get_trait_name_aggression(aistate.character) debug( "***************************************************************************" ) debug( "******* Log info for AI progress chart script. Do not modify. **********" ) debug("Generating Orders") name_parts = ( empire.name, empire.empireID, "pid", fo.playerID(), fo.playerName(), "RIdx", ResearchAI.get_research_index(), aggression_name.capitalize(), ) empire_name = "_".join(str(part) for part in name_parts) debug(f"EmpireID: {empire.empireID} Name: {empire_name} Turn: {turn}") debug(f"EmpireColors: {empire.colour}") planet_id = PlanetUtilsAI.get_capital() if planet_id: planet = fo.getUniverse().getPlanet(planet_id) debug("CapitalID: " + str(planet_id) + " Name: " + planet.name + " Species: " + planet.speciesName) else: debug("CapitalID: None Currently Name: None Species: None ") debug( "***************************************************************************" ) debug( "***************************************************************************" ) # When loading a savegame, the AI will already have issued orders for this turn. # To avoid duplicate orders, generally try not to replay turns. However, for debugging # purposes it is often useful to replay the turn and observe varying results after # code changes. Set the replay_after_load flag in the AI config to let the AI issue # new orders after a game load. Note that the orders from the original savegame are # still being issued and the AIstate was saved after those orders were issued. # TODO: Consider adding an option to clear AI orders after load (must save AIstate at turn start then) if fo.currentTurn() == aistate.last_turn_played: info("The AIstate indicates that this turn was already played.") if not check_bool(get_option_dict().get("replay_turn_after_load", "False")): info( "Aborting new order generation. Orders from savegame will still be issued." ) return info("Issuing new orders anyway.") if turn == 1: human_player = fo.empirePlayerID(1) greet = diplomatic_corp.get_first_turn_greet_message() fo.sendChatMessage( human_player, "%s (%s): [[%s]]" % (empire.name, get_trait_name_aggression(aistate.character), greet)) aistate.prepare_for_new_turn() debug("Calling AI Modules") # call AI modules action_list = [ ColonisationAI.survey_universe, ShipDesignAI.Cache.update_for_new_turn, PriorityAI.calculate_priorities, ExplorationAI.assign_scouts_to_explore_systems, ColonisationAI.assign_colony_fleets_to_colonise, InvasionAI.assign_invasion_fleets_to_invade, MilitaryAI.assign_military_fleets_to_systems, FleetUtilsAI.generate_fleet_orders_for_fleet_missions, FleetUtilsAI.issue_fleet_orders_for_fleet_missions, ResearchAI.generate_research_orders, ProductionAI.generate_production_orders, ResourcesAI.generate_resources_orders, ] for action in action_list: try: main_timer.start(action.__name__) action() main_timer.stop() except Exception as e: error("Exception %s while trying to %s" % (e, action.__name__), exc_info=True) main_timer.stop_print_and_clear() turn_timer.stop_print_and_clear() turn_timer.start("Server_Processing") aistate.last_turn_played = fo.currentTurn()
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 generateOrders(): # pylint: disable=invalid-name """Called once per turn to tell the Python AI to generate and issue orders to control its empire. at end of this function, fo.doneTurn() should be called to indicate to the client that orders are finished and can be sent to the server for processing.""" turn_timer.start("AI planning") universe = fo.getUniverse() empire = fo.getEmpire() planet_id = PlanetUtilsAI.get_capital() # set the random seed (based on galaxy seed, empire ID and current turn) # for game-reload consistency random_seed = str(fo.getGalaxySetupData().seed) + "%03d%05d" % ( fo.empireID(), fo.currentTurn()) random.seed(random_seed) planet = None if planet_id is not None: planet = universe.getPlanet(planet_id) aggression_name = fo.aggression.values[foAIstate.aggression].name print "***************************************************************************" print "********** String for chart. Do not modify. ***************************" print("Generating Orders") print( "EmpireID: {empire.empireID}" " Name: {empire.name}_{empire.empireID}_pid:{p_id}_{p_name}RIdx_{res_idx}_{aggression}" " Turn: {turn}").format(empire=empire, p_id=fo.playerID(), p_name=fo.playerName(), res_idx=ResearchAI.get_research_index(), turn=fo.currentTurn(), aggression=aggression_name.capitalize()) print "EmpireColors: {0.colour.r} {0.colour.g} {0.colour.b} {0.colour.a}".format( empire) if planet: print "CapitalID: " + str( planet_id ) + " Name: " + planet.name + " Species: " + planet.speciesName else: print "CapitalID: None Currently Name: None Species: None " print "***************************************************************************" print "***************************************************************************" if fo.currentTurn() == 1: declare_war_on_all() human_player = fo.empirePlayerID(1) fo.sendChatMessage( human_player, '%s Empire (%s):\n"Ave, Human, morituri te salutant!"' % (empire.name, aggression_name)) # turn cleanup !!! this was formerly done at start of every turn -- not sure why foAIstate.split_new_fleets() foAIstate.refresh( ) # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats foAIstate.report_system_threats() # ...missions # ...demands/priorities print("Calling AI Modules") # call AI modules action_list = [ PriorityAI.calculate_priorities, ExplorationAI.assign_scouts_to_explore_systems, ColonisationAI.assign_colony_fleets_to_colonise, InvasionAI.assign_invasion_fleets_to_invade, MilitaryAI.assign_military_fleets_to_systems, FleetUtilsAI.generate_fleet_orders_for_fleet_missions, FleetUtilsAI.issue_fleet_orders_for_fleet_missions, ResearchAI.generate_research_orders, ProductionAI.generateProductionOrders, ResourcesAI.generate_resources_orders, foAIstate.after_turn_cleanup, ] for action in action_list: try: main_timer.start(action.__name__) action() main_timer.stop() except Exception as e: print_error(e, location=action.__name__) main_timer.end() turn_timer.end() turn_timer.start("Server_Processing") try: fo.doneTurn() except Exception as e: print_error(e) # TODO move it to cycle above if using_statprof: try: statprof.stop() statprof.display() statprof.start() except: pass
def 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)) # Todo: This block had been originally added to address situations where fleet missions were not properly # terminating, leaving fleets stuck in stale deployments. Assess if this block is still needed at all; delete # if not, otherwise restructure the following code so that in event a reset is occurring greater priority is given # to providing military support to locations where a necessary Secure mission might have just been released (i.e., # at invasion and colony/outpost targets where the troopships and colony ships are on their way), or else allow # only a partial reset which does not reset Secure missions. enable_periodic_mission_reset = False if enable_periodic_mission_reset and try_reset and ( fo.currentTurn() + empire_id) % 30 == 0 and thisround == "Main": debug( "Resetting all Military missions as part of an automatic periodic reset to clear stale missions." ) 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( get_fleet_rating(x) for x in 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 = get_fleet_rating(fleet_id) this_rating_vs_planets = get_fleet_rating_against_planets(fleet_id) already_assigned_rating[last_sys] = combine_ratings( already_assigned_rating.get(last_sys, 0), this_rating) already_assigned_rating_vs_planets[last_sys] = 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] = combine_ratings( my_defense_rating, already_assigned_rating[sys_id]) if _verbose_mil_reporting and already_assigned_rating[sys_id]: debug( "\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 = next(iter( aistate.fleetStatus.items()))[1]["sysID"] except: # noqa: E722 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 list(aistate.colonisableOutpostIDs.items()) [:num_targets] if pscore > InvasionAI.MIN_INVASION_SCORE ] + [ pid for pid, (pscore, spec) in list(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() 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(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 systems_connected(sys_id, home_system_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: debug( "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % (thisround, dict([(sid, alloc) for sid, alloc, _, _, _ in new_allocations]))) debug("(Apparently) remaining military rating: %.1f" % remaining_mil_rating) return new_allocations
def _calculate_military_priority(): """Calculates the demand for military ships by military targeted systems.""" global unmetThreat universe = fo.getUniverse() capital_id = PlanetUtilsAI.get_capital() if capital_id is None or capital_id == INVALID_ID: return 0 # no capitol (not even a capitol-in-the-making), means can't produce any ships have_l1_weaps = (tech_is_complete("SHP_WEAPON_1_4") or (tech_is_complete("SHP_WEAPON_1_3") and tech_is_complete("SHP_MIL_ROBO_CONT")) or tech_is_complete("SHP_WEAPON_2_1") or tech_is_complete("SHP_WEAPON_4_1")) have_l2_weaps = (tech_is_complete("SHP_WEAPON_2_3") or tech_is_complete("SHP_WEAPON_4_1")) enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) allotted_invasion_targets = 1 + int(fo.currentTurn()/25) target_planet_ids = [pid for pid, pscore, trp in AIstate.invasionTargets[:allotted_invasion_targets]] + [pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs.items()[:allottedColonyTargets]] + [pid for pid, pscore in foAI.foAIstate.colonisableOutpostIDs.items()[:allottedColonyTargets]] my_systems = set(state.get_empire_planets_by_system()) target_systems = set(PlanetUtilsAI.get_systems(target_planet_ids)) cur_ship_rating = ProductionAI.cur_best_military_design_rating() current_turn = fo.currentTurn() ships_needed = 0 defense_ships_needed = 0 ships_needed_allocation = [] for sys_id in my_systems.union(target_systems): status = foAI.foAIstate.systemStatus.get(sys_id, {}) my_rating = status.get('myFleetRating', 0) my_defenses = status.get('mydefenses', {}).get('overall', 0) base_monster_threat = status.get('monsterThreat', 0) # scale monster threat so that in early - mid game big monsters don't over-drive military production monster_threat = base_monster_threat if current_turn > 200: pass elif current_turn > 100: if base_monster_threat >= 2000: monster_threat = 2000 + (current_turn / 100.0 - 1) * (base_monster_threat - 2000) elif current_turn > 30: if base_monster_threat >= 2000: monster_threat = 0 else: if base_monster_threat > 200: monster_threat = 0 if sys_id in my_systems: threat_root = status.get('fleetThreat', 0)**0.5 + 0.8*status.get('max_neighbor_threat', 0)**0.5 + 0.2*status.get('neighborThreat', 0)**0.5 + monster_threat**0.5 + status.get('planetThreat', 0)**0.5 else: threat_root = status.get('fleetThreat', 0)**0.5 + monster_threat**0.5 + status.get('planetThreat', 0)**0.5 ships_needed_here = math.ceil((max(0, (threat_root - (my_rating ** 0.5 + my_defenses ** 0.5))) ** 2) / cur_ship_rating) ships_needed += ships_needed_here ships_needed_allocation.append((universe.getSystem(sys_id), ships_needed_here)) if sys_id in my_systems: defense_ships_needed += ships_needed_here part1 = min(1 * fo.currentTurn(), 40) part2 = max(0, int(75 * ships_needed)) military_priority = part1 + part2 if not have_l1_weaps: military_priority /= 2.0 elif not (have_l2_weaps and enemies_sighted): military_priority /= 1.5 fmt_string = "Calculating Military Priority: min(t,40) + max(0,75 * ships_needed) \n\t Priority: %d \t ships_needed: %d \t defense_ships_needed: %d \t curShipRating: %.0f \t l1_weaps: %s \t enemies_sighted: %s" print fmt_string % (military_priority, ships_needed, defense_ships_needed, cur_ship_rating, have_l1_weaps, enemies_sighted) print "Source of milship demand: ", ships_needed_allocation military_priority *= foAI.foAIstate.character.military_priority_scaling() return max(military_priority, 0)
def generateOrders(): # pylint: disable=invalid-name """Called once per turn to tell the Python AI to generate and issue orders to control its empire. at end of this function, fo.doneTurn() should be called to indicate to the client that orders are finished and can be sent to the server for processing.""" try: rules = fo.getGameRules() debug("Defined game rules:") for rule_name, rule_value in rules.getRulesAsStrings().items(): debug("%s: %s", rule_name, rule_value) debug("Rule RULE_NUM_COMBAT_ROUNDS value: " + str(rules.getInt("RULE_NUM_COMBAT_ROUNDS"))) except Exception as e: error("Exception %s when trying to get game rules" % e, exc_info=True) empire = fo.getEmpire() if empire is None: fatal("This client has no empire. Doing nothing to generate orders.") try: # early abort if no empire. no need to do meter calculations # on last-seen gamestate if nothing can be ordered anyway... # # note that doneTurn() is issued on behalf of the client network # id, not the empire id, so not having a correct empire id does # not invalidate doneTurn() fo.doneTurn() except Exception as e: error("Exception %s in doneTurn() on non-existent empire" % e, exc_info=True) return if empire.eliminated: debug("This empire has been eliminated. Aborting order generation") try: # early abort if already eliminated. no need to do meter calculations # on last-seen gamestate if nothing can be ordered anyway... fo.doneTurn() except Exception as e: error("Exception %s while trying doneTurn() on eliminated empire" % e, exc_info=True) return # This code block is required for correct AI work. info("Meter / Resource Pool updating...") fo.initMeterEstimatesDiscrepancies() fo.updateMeterEstimates(False) fo.updateResourcePools() turn = fo.currentTurn() aistate = get_aistate() turn_uid = aistate.set_turn_uid() debug("\n\n\n" + "=" * 20) debug("Starting turn %s (%s) of game: %s" % (turn, turn_uid, aistate.uid)) debug("=" * 20 + "\n") turn_timer.start("AI planning") # set the random seed (based on galaxy seed, empire name and current turn) # for game-reload consistency. random_seed = str(fo.getGalaxySetupData().seed) + "%05d%s" % (turn, fo.getEmpire().name) random.seed(random_seed) universe = fo.getUniverse() empire = fo.getEmpire() planet_id = PlanetUtilsAI.get_capital() planet = None if planet_id is not None: planet = universe.getPlanet(planet_id) aggression_name = get_trait_name_aggression(aistate.character) debug("***************************************************************************") debug("******* Log info for AI progress chart script. Do not modify. **********") debug("Generating Orders") debug("EmpireID: {empire.empireID}" " Name: {empire.name}_{empire.empireID}_pid:{p_id}_{p_name}RIdx_{res_idx}_{aggression}" " Turn: {turn}".format(empire=empire, p_id=fo.playerID(), p_name=fo.playerName(), res_idx=ResearchAI.get_research_index(), turn=turn, aggression=aggression_name.capitalize())) debug("EmpireColors: {0.colour.r} {0.colour.g} {0.colour.b} {0.colour.a}".format(empire)) if planet: debug("CapitalID: " + str(planet_id) + " Name: " + planet.name + " Species: " + planet.speciesName) else: debug("CapitalID: None Currently Name: None Species: None ") debug("***************************************************************************") debug("***************************************************************************") # When loading a savegame, the AI will already have issued orders for this turn. # To avoid duplicate orders, generally try not to replay turns. However, for debugging # purposes it is often useful to replay the turn and observe varying results after # code changes. Set the replay_after_load flag in the AI config to let the AI issue # new orders after a game load. Note that the orders from the original savegame are # still being issued and the AIstate was saved after those orders were issued. # TODO: Consider adding an option to clear AI orders after load (must save AIstate at turn start then) if fo.currentTurn() == aistate.last_turn_played: info("The AIstate indicates that this turn was already played.") if not check_bool(get_option_dict().get('replay_turn_after_load', 'False')): info("Aborting new order generation. Orders from savegame will still be issued.") try: fo.doneTurn() except Exception as e: error("Exception %s while trying doneTurn()" % e, exc_info=True) return else: info("Issuing new orders anyway.") if turn == 1: human_player = fo.empirePlayerID(1) greet = diplomatic_corp.get_first_turn_greet_message() fo.sendChatMessage(human_player, '%s (%s): [[%s]]' % (empire.name, get_trait_name_aggression(aistate.character), greet)) aistate.prepare_for_new_turn() turn_state.state.update() debug("Calling AI Modules") # call AI modules action_list = [ColonisationAI.survey_universe, ProductionAI.find_best_designs_this_turn, PriorityAI.calculate_priorities, ExplorationAI.assign_scouts_to_explore_systems, ColonisationAI.assign_colony_fleets_to_colonise, InvasionAI.assign_invasion_fleets_to_invade, MilitaryAI.assign_military_fleets_to_systems, FleetUtilsAI.generate_fleet_orders_for_fleet_missions, FleetUtilsAI.issue_fleet_orders_for_fleet_missions, ResearchAI.generate_research_orders, ProductionAI.generate_production_orders, ResourcesAI.generate_resources_orders, ] for action in action_list: try: main_timer.start(action.__name__) action() main_timer.stop() except Exception as e: error("Exception %s while trying to %s" % (e, action.__name__), exc_info=True) main_timer.stop_print_and_clear() turn_timer.stop_print_and_clear() debug('Size of issued orders: ' + str(fo.getOrders().size)) turn_timer.start("Server_Processing") try: fo.doneTurn() except Exception as e: error("Exception %s while trying doneTurn()" % e, exc_info=True) # TODO move it to cycle above finally: aistate.last_turn_played = fo.currentTurn() if using_statprof: try: statprof.stop() statprof.display() statprof.start() except: pass
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 get_military_fleets(milFleetIDs=None, tryReset=True, thisround="Main"): """get armed military fleets""" global military_allocations, totMilRating, num_milships universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID capital_id = PlanetUtilsAI.get_capital() if capital_id is None: homeworld = None else: homeworld = universe.getPlanet(capital_id) if homeworld: home_system_id = homeworld.systemID else: home_system_id = -1 if milFleetIDs is not None: all_military_fleet_ids = milFleetIDs else: all_military_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role( AIFleetMissionType.FLEET_MISSION_MILITARY) if tryReset and (fo.currentTurn() + empire_id) % 30 == 0 and thisround == "Main": try_again(all_military_fleet_ids, try_reset=False, thisround=thisround + " Reset") num_milships = 0 for fid in all_military_fleet_ids: num_milships += foAI.foAIstate.fleetStatus.get(fid, {}).get('nships', 0) this_tot_mil_rating = sum( map(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0), all_military_fleet_ids)) if "Main" in thisround: totMilRating = this_tot_mil_rating print "==================================================" print "%s Round Total Military Rating: %d" % (thisround, totMilRating) print "---------------------------------" foAI.foAIstate.militaryRating = totMilRating milFleetIDs = list( FleetUtilsAI.extract_fleet_ids_without_mission_types( all_military_fleet_ids)) mil_needing_repair_ids, milFleetIDs = avail_mil_needing_repair( milFleetIDs, split_ships=True) avail_mil_rating = sum( map(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0), milFleetIDs)) if "Main" in thisround: print "==================================================" print "%s Round Available Military Rating: %d" % (thisround, avail_mil_rating) print "---------------------------------" remaining_mil_rating = avail_mil_rating allocations = [] allocation_groups = {} if not milFleetIDs: if "Main" in thisround: military_allocations = [] return [] # for each system, get total rating of fleets assigned to it already_assigned_rating = {} assigned_attack = {} assigned_hp = {} for sys_id in universe.systemIDs: assigned_attack[sys_id] = 0 assigned_hp[sys_id] = 0 for fleet_id in [ fid for fid in all_military_fleet_ids if fid not in milFleetIDs ]: ai_fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id) sys_targets = [] for ai_fleet_mission_type in ai_fleet_mission.get_mission_types(): ai_targets = ai_fleet_mission.get_targets(ai_fleet_mission_type) for aiTarget in ai_targets: sys_targets.extend(aiTarget.get_required_system_ai_targets()) if not sys_targets: # shouldn't really be possible continue last_sys = sys_targets[ -1].target_id # will count this fleet as assigned to last system in target list assigned_attack[last_sys] += foAI.foAIstate.get_rating(fleet_id).get( 'attack', 0) assigned_hp[last_sys] += foAI.foAIstate.get_rating(fleet_id).get( 'health', 0) for sys_id in universe.systemIDs: mydefenses = foAI.foAIstate.systemStatus.get(sys_id, {}).get('mydefenses', {}) mypattack = mydefenses.get('attack', 0) myphealth = mydefenses.get('health', 0) already_assigned_rating[sys_id] = (assigned_attack[sys_id] + mypattack) * (assigned_hp[sys_id] + myphealth) # get systems to defend capital_id = PlanetUtilsAI.get_capital() if capital_id is not None: capital_planet = universe.getPlanet(capital_id) else: capital_planet = None # TODO: if no owned planets try to capture one! if capital_planet: capital_sys_id = capital_planet.systemID else: # should be rare, but so as to not break code below, pick a randomish mil-centroid system capital_sys_id = None # unless we can find one to use system_dict = {} for fleet_id in all_military_fleet_ids: status = foAI.foAIstate.fleetStatus.get(fleet_id, None) if status is not None: sys_id = status['sysID'] if not list(universe.getSystem(sys_id).planetIDs): continue system_dict[sys_id] = system_dict.get(sys_id, 0) + status.get( 'rating', {}).get('overall', 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 if False: if fo.currentTurn() < 20: threat_bias = 0 elif fo.currentTurn() < 40: threat_bias = 10 elif fo.currentTurn() < 60: threat_bias = 80 elif fo.currentTurn() < 80: threat_bias = 200 else: threat_bias = 400 else: threat_bias = 0 safety_factor = get_safety_factor() top_target_planets = [ pid for pid, pscore, trp in AIstate. invasionTargets[:PriorityAI.allottedInvasionTargets] if pscore > 20 ] + [ pid for pid, (pscore, spec) in foAI.foAIstate.colonisablePlanetIDs.items()[:10] if pscore > 20 ] top_target_planets.extend(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) top_target_systems = [] for sys_id in AIstate.invasionTargetedSystemIDs + PlanetUtilsAI.get_systems( top_target_planets): if sys_id not in top_target_systems: top_target_systems.append( sys_id) # doing this rather than set, to preserve order # allocation format: ( sysID, newAllocation, takeAny, maxMultiplier ) # ================================ # --------Capital Threat ---------- capital_threat = safety_factor * (2 * threat_bias + sum([ foAI.foAIstate.systemStatus[capital_sys_id][thrt_key] for thrt_key in ['totalThreat', 'neighborThreat'] ])) needed_rating = rating_needed(1.4 * capital_threat, already_assigned_rating[capital_sys_id]) new_alloc = 0 if tryReset: if needed_rating > 0.5 * avail_mil_rating: try_again(all_military_fleet_ids) return if needed_rating > 0: new_alloc = min(remaining_mil_rating, needed_rating) allocations.append( (capital_sys_id, new_alloc, True, 1.6 * capital_threat)) allocation_groups.setdefault('capitol', []).append( (capital_sys_id, new_alloc, True, 2 * capital_threat)) remaining_mil_rating -= new_alloc if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "Empire Capital System: (%d) %s -- threat : %d, military allocation: existing: %d ; new: %d" % ( capital_sys_id, universe.getSystem(capital_sys_id).name, capital_threat, already_assigned_rating[capital_sys_id], new_alloc) print "-----------------" # ================================ # --------Empire Occupied Systems ---------- 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}) if "Main" in thisround: if verbose_mil_reporting: print "Empire-Occupied Systems: %s" % ([ "| %d %s |" % (eo_sys_id, universe.getSystem(eo_sys_id).name) for eo_sys_id in empire_occupied_system_ids ]) print "-----------------" new_alloc = 0 if empire_occupied_system_ids: oc_sys_tot_threat = [(o_s_id, threat_bias + safety_factor * sum([ foAI.foAIstate.systemStatus.get(o_s_id, {}).get(thrt_key, 0) for thrt_key in ['totalThreat', 'neighborThreat'] ])) for o_s_id in empire_occupied_system_ids] totoc_sys_threat = sum([thrt for sid, thrt in oc_sys_tot_threat]) tot_cur_alloc = sum( [already_assigned_rating[sid] for sid, thrt in oc_sys_tot_threat]) oc_sys_alloc = 0 for sid, thrt in oc_sys_tot_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.3 * thrt, cur_alloc) if (needed_rating > 0.8 * remaining_mil_rating) and tryReset: try_again(all_military_fleet_ids) return this_alloc = 0 if needed_rating > 0 and remaining_mil_rating > 0: this_alloc = max( 0, min(needed_rating, 0.5 * avail_mil_rating, remaining_mil_rating)) new_alloc += this_alloc allocations.append((sid, this_alloc, True, 1.6 * thrt)) allocation_groups.setdefault('occupied', []).append( (sid, this_alloc, True, 2 * thrt)) remaining_mil_rating -= this_alloc oc_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Provincial Occupied system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % ( sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "Provincial Empire-Occupied Sytems under total threat: %d -- total mil allocation: existing %d ; new: %d" % ( totoc_sys_threat, tot_cur_alloc, oc_sys_alloc) print "-----------------" # ================================ # --------Top Targeted Systems ---------- # TODO: do native invasions highest priority other_targeted_system_ids = top_target_systems if "Main" in thisround: if verbose_mil_reporting: print "Top Colony and Invasion Targeted Systems : %s" % ([ "| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids ]) print "-----------------" # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if other_targeted_system_ids: ot_sys_alloc = 0 ot_sys_threat = [ (o_s_id, threat_bias + safety_factor * (foAI.foAIstate.systemStatus.get(o_s_id, {}).get( 'totalThreat', 0) + 0.5 * foAI.foAIstate.systemStatus.get( o_s_id, {}).get('neightborThreat', 0))) for o_s_id in other_targeted_system_ids ] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum( [already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.4 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) max_alloc = safety_factor * 3 * max( foAI.foAIstate.systemStatus.get(sid, {}).get( 'totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get( 'neightborThreat', 0)) new_alloc += this_alloc allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('topTargets', []).append( (sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % ( sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Top Colony and Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % ( totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" # ================================ # --------Targeted Systems ---------- # TODO: do native invasions highest priority other_targeted_system_ids = [ sys_id for sys_id in set(PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if sys_id not in top_target_systems ] if "Main" in thisround: if verbose_mil_reporting: print "Other Invasion Targeted Systems : %s" % ([ "| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids ]) print "-----------------" # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if other_targeted_system_ids: ot_sys_alloc = 0 ot_sys_threat = [ (o_s_id, threat_bias + safety_factor * (foAI.foAIstate.systemStatus.get(o_s_id, {}).get( 'totalThreat', 0) + 0.5 * foAI.foAIstate.systemStatus.get( o_s_id, {}).get('neighborThreat', 0))) for o_s_id in other_targeted_system_ids ] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum( [already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.4 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = safety_factor * 3 * max( foAI.foAIstate.systemStatus.get(sid, {}).get( 'totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get( 'neightborThreat', 0)) allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('otherTargets', []).append( (sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % ( sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % ( totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" other_targeted_system_ids = [ sys_id for sys_id in list( set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if sys_id not in top_target_systems ] if "Main" in thisround: if verbose_mil_reporting: print "Other Targeted Systems : %s" % ([ "| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids ]) print "-----------------" if other_targeted_system_ids: ot_sys_alloc = 0 ot_sys_threat = [ (o_s_id, safety_factor * (threat_bias + foAI.foAIstate.systemStatus.get(o_s_id, {}).get( 'totalThreat', 0) + 0.5 * foAI.foAIstate.systemStatus.get( o_s_id, {}).get('neighborThreat', 0))) for o_s_id in other_targeted_system_ids ] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum( [already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) new_alloc = 0 for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.2 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = safety_factor * 3 * max( foAI.foAIstate.systemStatus.get(sid, {}).get( 'totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get( 'neightborThreat', 0)) allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('otherTargets', []).append( (sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % ( sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Other Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % ( totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" other_targeted_system_ids = [] # targetable_ids = ColonisationAI.annexableSystemIDs.union(empire.fleetSupplyableSystemIDs) targetable_ids = set( ColonisationAI.systems_by_supply_tier.get(0, []) + ColonisationAI.systems_by_supply_tier.get(1, [])) for sys_id in AIstate.opponentSystemIDs: if sys_id in targetable_ids: other_targeted_system_ids.append(sys_id) else: for nID in universe.getImmediateNeighbors(sys_id, empire_id): if nID in targetable_ids: other_targeted_system_ids.append(sys_id) break if "Main" in thisround: if verbose_mil_reporting: print "Blockade Targeted Systems : %s" % ([ "| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids ]) print "-----------------" if other_targeted_system_ids: ot_sys_alloc = 0 ot_sys_threat = [ (o_s_id, threat_bias + safety_factor * (foAI.foAIstate.systemStatus.get(o_s_id, {}).get( 'totalThreat', 0) + 0.5 * foAI.foAIstate.systemStatus.get( o_s_id, {}).get('neighborThreat', 0))) for o_s_id in other_targeted_system_ids ] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum( [already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) new_alloc = 0 for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.2 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = safety_factor * 2 * max( foAI.foAIstate.systemStatus.get(sid, {}).get( 'totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get( 'neightborThreat', 0)) allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('otherTargets', []).append( (sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Blockade Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % ( sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Blockade Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % ( totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" current_mil_systems = [sid for sid, alloc, take_any, mm in allocations] interior_ids = list(foAI.foAIstate.expInteriorSystemIDs) interior_targets1 = ( targetable_ids.union(interior_ids)).difference(current_mil_systems) interior_targets = [ sid for sid in interior_targets1 if (threat_bias + foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0) > 0.8 * already_assigned_rating[sid]) ] if "Main" in thisround: if verbose_mil_reporting: print print "Other Empire-Proximal Systems : %s" % ([ "| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in interior_targets1 ]) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if interior_targets: ot_sys_alloc = 0 ot_sys_threat = [ (o_s_id, threat_bias + safety_factor * (foAI.foAIstate.systemStatus.get( o_s_id, {}).get('totalThreat', 0))) for o_s_id in interior_targets ] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum( [already_assigned_rating[sid] for sid, thrt in ot_sys_threat]) for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.2 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = already_assigned_rating[sid] > 0 if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = safety_factor * 2 * max( foAI.foAIstate.systemStatus.get(sid, {}).get( 'totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get( 'neightborThreat', 0)) allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('otherTargets', []).append( (sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Other interior system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % ( sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Other Interior Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % ( totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" elif "Main" in thisround: if verbose_mil_reporting: print "-----------------" print "No Other Interior Systems with fleet threat " print "-----------------" monster_dens = [] # explo_target_ids, _ = ExplorationAI.get_current_exploration_info(verbose=False) explo_target_ids = [] if "Main" in thisround: if verbose_mil_reporting: print print "Exploration-targeted Systems: %s" % ([ "| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in explo_target_ids ]) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if explo_target_ids: ot_sys_alloc = 0 ot_sys_threat = [( o_s_id, safety_factor * (foAI.foAIstate.systemStatus.get(o_s_id, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(o_s_id, {}).get( 'monsterThreat', 0) + foAI.foAIstate.systemStatus.get( o_s_id, {}).get('planetThreat', 0))) for o_s_id in explo_target_ids] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum([ 0.8 * already_assigned_rating[sid] for sid, thrt in ot_sys_threat ]) for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.2 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = False if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = safety_factor * 2 * max( foAI.foAIstate.systemStatus.get(sid, {}).get( 'totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get( 'neightborThreat', 0)) allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('exploreTargets', []).append( (sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Exploration-targeted %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % ( sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Exploration-targeted s under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % ( totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys( ) + foAI.foAIstate.visBorderSystemIDs.keys() accessible_system_ids = [ sys_id for sys_id in visible_system_ids if universe.systemsConnected(sys_id, home_system_id, empire_id) ] current_mil_systems = [ sid for sid, alloc, take_any, multiplier in 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 (threat_bias + foAI.foAIstate.systemStatus.get(sid, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(sid, {}).get('planetThreat', 0) > 0.8 * already_assigned_rating[sid]) ] if "Main" in thisround: if verbose_mil_reporting: print print "Empire-Accessible Systems not yet allocated military: %s" % ( [ "| %d %s |" % (sys_id, universe.getSystem(sys_id) and universe.getSystem(sys_id).name) for sys_id in border_targets1 ]) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if border_targets: ot_sys_alloc = 0 ot_sys_threat = [( o_s_id, threat_bias + safety_factor * (foAI.foAIstate.systemStatus.get(o_s_id, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(o_s_id, {}).get( 'monsterThreat', 0) + foAI.foAIstate.systemStatus.get( o_s_id, {}).get('planetThreat', 0))) for o_s_id in border_targets] totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat]) tot_cur_alloc = sum([ 0.8 * already_assigned_rating[sid] for sid, thrt in ot_sys_threat ]) for sid, thrt in ot_sys_threat: cur_alloc = already_assigned_rating[sid] needed_rating = rating_needed(1.2 * thrt, cur_alloc) this_alloc = 0 # only record more allocation for this invasion if we already started or have enough rating available take_any = False if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any): this_alloc = max(0, min(needed_rating, remaining_mil_rating)) new_alloc += this_alloc max_alloc = safety_factor * 2 * max( foAI.foAIstate.systemStatus.get(sid, {}).get( 'totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get( 'neightborThreat', 0)) allocations.append((sid, this_alloc, take_any, max_alloc)) allocation_groups.setdefault('accessibleTargets', []).append( (sid, this_alloc, take_any, max_alloc)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Other Empire-Accessible system %4d ( %10s ) has local biased threat %8d ; existing military allocation %d and new allocation %8d" % ( sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" print "Other Empire-Accessible Systems under total biased threat: %d -- total mil allocation-- existing: %d ; new: %d" % ( totot_sys_threat, tot_cur_alloc, ot_sys_alloc) print "-----------------" elif "Main" in thisround: if verbose_mil_reporting: print "-----------------" print "No Other Empire-Accessible Systems with biased local threat " print "-----------------" # monster den treatment probably unnecessary now if "Main" in thisround: if verbose_mil_reporting: print print "Big-Monster Dens: %s" % ([ "| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in monster_dens ]) print "-----------------" # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety new_alloc = 0 if monster_dens: ot_sys_alloc = 0 ot_sys_threat = [( o_s_id, safety_factor * (foAI.foAIstate.systemStatus.get(o_s_id, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(o_s_id, {}).get( 'monsterThreat', 0) + foAI.foAIstate.systemStatus.get( o_s_id, {}).get('planetThreat', 0))) for o_s_id in monster_dens] for sid, thrt in ot_sys_threat: cur_alloc = 0.8 * already_assigned_rating[sid] this_alloc = 0 if (thrt > cur_alloc) and remaining_mil_rating > 2 * thrt: this_alloc = int(0.99999 + (thrt - cur_alloc) * 1.5) new_alloc += this_alloc allocations.append((sid, this_alloc, False, 5)) remaining_mil_rating -= this_alloc ot_sys_alloc += this_alloc if "Main" in thisround or this_alloc > 0: if verbose_mil_reporting: print "Monster Den %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % ( sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc) if "Main" in thisround or new_alloc > 0: if verbose_mil_reporting: print "-----------------" if remaining_mil_rating <= 6: new_allocations = [(sid, alc, alc, ta) for (sid, alc, ta, mm) in allocations] else: print "%s Round Remaining military strength allocation %d will be allocated as surplus allocation to top current priorities" % ( thisround, remaining_mil_rating) new_allocations = [] for cat in [ 'capitol', 'topTargets', 'otherTargets', 'accessibleTargets', 'occupied', 'exploreTargets' ]: for sid, alloc, take_any, max_alloc in allocation_groups.get( cat, []): if remaining_mil_rating <= 0: new_allocations.append((sid, alloc, alloc, take_any)) else: new_rating = min( remaining_mil_rating + alloc, max( alloc, rating_needed(max_alloc, already_assigned_rating[sid]))) new_allocations.append((sid, new_rating, alloc, take_any)) remaining_mil_rating -= (new_rating - alloc) if "Main" in thisround: military_allocations = new_allocations minMilAllocations.clear() minMilAllocations.update([(sid, alloc) for sid, alloc, take_any, mm in allocations]) if verbose_mil_reporting or "Main" in thisround: print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % ( thisround, dict([(sid, alloc) for sid, alloc, minalloc, take_any in new_allocations])) # export military systems for other AI modules if "Main" in thisround: AIstate.militarySystemIDs = list( set([sid for sid, alloc, minalloc, take_any in new_allocations ]).union([ sid for sid in already_assigned_rating if already_assigned_rating[sid] > 0 ])) else: AIstate.militarySystemIDs = list( set([sid for sid, alloc, minalloc, take_any in new_allocations ]).union(AIstate.militarySystemIDs)) return new_allocations
def generateOrders(): # pylint: disable=invalid-name """Called once per turn to tell the Python AI to generate and issue orders to control its empire. at end of this function, fo.doneTurn() should be called to indicate to the client that orders are finished and can be sent to the server for processing.""" rules = fo.getGameRules() print "Defined game rules:" for rule in rules.getRulesAsStrings: print "Name: " + rule.name + " value: " + str(rule.value) print "Rule RULE_NUM_COMBAT_ROUNDS value: " + str( rules.getInt("RULE_NUM_COMBAT_ROUNDS")) empire = fo.getEmpire() if empire is None: print "This client has no empire. Doing nothing to generate orders." try: # early abort if no empire. no need to do meter calculations # on last-seen gamestate if nothing can be ordered anyway... # # note that doneTurn() is issued on behalf of the client network # id, not the empire id, so not having a correct empire id does # not invalidate doneTurn() fo.doneTurn() except Exception as e: print_error(e) return if empire.eliminated: print "This empire has been eliminated. Aborting order generation" try: # early abort if already eliminated. no need to do meter calculations # on last-seen gamestate if nothing can be ordered anyway... fo.doneTurn() except Exception as e: print_error(e) return # This code block is required for correct AI work. print "Meter / Resource Pool updating..." fo.initMeterEstimatesDiscrepancies() fo.updateMeterEstimates(False) fo.updateResourcePools() turn = fo.currentTurn() turn_uid = foAIstate.set_turn_uid() print "\n\n\n", "=" * 20, print "Starting turn %s (%s) of game: %s" % (turn, turn_uid, foAIstate.uid), print "=" * 20, "\n" turn_timer.start("AI planning") # set the random seed (based on galaxy seed, empire name and current turn) # for game-reload consistency. random_seed = str( fo.getGalaxySetupData().seed) + "%05d%s" % (turn, fo.getEmpire().name) random.seed(random_seed) universe = fo.getUniverse() empire = fo.getEmpire() planet_id = PlanetUtilsAI.get_capital() planet = None if planet_id is not None: planet = universe.getPlanet(planet_id) aggression_name = get_trait_name_aggression(foAIstate.character) print "***************************************************************************" print "******* Log info for AI progress chart script. Do not modify. **********" print("Generating Orders") print( "EmpireID: {empire.empireID}" " Name: {empire.name}_{empire.empireID}_pid:{p_id}_{p_name}RIdx_{res_idx}_{aggression}" " Turn: {turn}").format(empire=empire, p_id=fo.playerID(), p_name=fo.playerName(), res_idx=ResearchAI.get_research_index(), turn=turn, aggression=aggression_name.capitalize()) print "EmpireColors: {0.colour.r} {0.colour.g} {0.colour.b} {0.colour.a}".format( empire) if planet: print "CapitalID: " + str( planet_id ) + " Name: " + planet.name + " Species: " + planet.speciesName else: print "CapitalID: None Currently Name: None Species: None " print "***************************************************************************" print "***************************************************************************" if turn == 1: declare_war_on_all() human_player = fo.empirePlayerID(1) greet = diplomatic_corp.get_first_turn_greet_message() fo.sendChatMessage( human_player, '%s (%s): [[%s]]' % (empire.name, get_trait_name_aggression( foAIstate.character), greet)) # turn cleanup !!! this was formerly done at start of every turn -- not sure why foAIstate.split_new_fleets() foAIstate.refresh( ) # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats foAIstate.report_system_threats() print("Calling AI Modules") # call AI modules action_list = [ ColonisationAI.survey_universe, ProductionAI.find_best_designs_this_turn, PriorityAI.calculate_priorities, ExplorationAI.assign_scouts_to_explore_systems, ColonisationAI.assign_colony_fleets_to_colonise, InvasionAI.assign_invasion_fleets_to_invade, MilitaryAI.assign_military_fleets_to_systems, FleetUtilsAI.generate_fleet_orders_for_fleet_missions, FleetUtilsAI.issue_fleet_orders_for_fleet_missions, ResearchAI.generate_research_orders, ProductionAI.generate_production_orders, ResourcesAI.generate_resources_orders, ] for action in action_list: try: main_timer.start(action.__name__) action() main_timer.stop() except Exception as e: print_error(e, location=action.__name__) main_timer.stop_print_and_clear() turn_timer.stop_print_and_clear() turn_timer.start("Server_Processing") try: fo.doneTurn() except Exception as e: print_error(e) # TODO move it to cycle above if using_statprof: try: statprof.stop() statprof.display() statprof.start() except: pass
def get_invasion_fleets(): invasion_timer.start("gathering initial info") all_invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) AIstate.invasionFleetIDs = FleetUtilsAI.extract_fleet_ids_without_mission_types(all_invasion_fleet_ids) # get suppliable planets universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID capital_id = PlanetUtilsAI.get_capital() homeworld=None if capital_id: homeworld = universe.getPlanet(capital_id) if homeworld: home_system_id = homeworld.systemID else: home_system_id = -1 fleet_suppliable_system_ids = empire.fleetSupplyableSystemIDs fleet_suppliable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(fleet_suppliable_system_ids) prime_invadable_system_ids = set(ColonisationAI.annexable_system_ids) prime_invadable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(prime_invadable_system_ids) visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate. visBorderSystemIDs.keys() if home_system_id != -1: accessible_system_ids = [sys_id for sys_id in visible_system_ids if (sys_id != -1) and universe.systemsConnected(sys_id, home_system_id, empire_id)] else: print "Invasion Warning: this empire has no identifiable homeworld, will therefor treat all visible planets as accessible." accessible_system_ids = visible_system_ids # TODO: check if any troop ships still owned, use their system as home system acessible_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(accessible_system_ids) print "Accessible Systems: ", ", ".join(PlanetUtilsAI.sys_name_ids(accessible_system_ids)) print all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids(acessible_planet_ids) # need these for unpopulated outposts all_populated_planets = PlanetUtilsAI.get_populated_planet_ids(acessible_planet_ids) # need this for natives print "All Visible and accessible Populated PlanetIDs (including this empire's): ", ", ".join(PlanetUtilsAI.planet_name_ids(all_populated_planets)) print print "Prime Invadable Target Systems: ", ", ".join(PlanetUtilsAI.sys_name_ids(prime_invadable_system_ids)) print empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) invadable_planet_ids = set(prime_invadable_planet_ids).intersection(set(all_owned_planet_ids).union(all_populated_planets) - set(empire_owned_planet_ids)) print "Prime Invadable PlanetIDs: ", ", ".join(PlanetUtilsAI.planet_name_ids(invadable_planet_ids)) print print "Current Invasion Targeted SystemIDs: ", ", ".join(PlanetUtilsAI.sys_name_ids(AIstate.invasionTargetedSystemIDs)) invasion_targeted_planet_ids = get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) invasion_targeted_planet_ids.extend(get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_ORBITAL_INVASION)) all_invasion_targeted_system_ids = set(PlanetUtilsAI.get_systems(invasion_targeted_planet_ids)) print "Current Invasion Targeted PlanetIDs: ", ", ".join(PlanetUtilsAI.planet_name_ids(invasion_targeted_planet_ids)) invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) if not invasion_fleet_ids: print "Available Invasion Fleets: 0" else: print "Invasion FleetIDs: %s" % FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) num_invasion_fleets = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(invasion_fleet_ids)) print "Invasion Fleets Without Missions: %s" % num_invasion_fleets invasion_timer.start("planning troop base production") # only do base invasions if aggression is typical or above reserved_troop_base_targets = [] if foAI.foAIstate.aggression > fo.aggression.typical: available_pp = {} for el in empire.planetsWithAvailablePP: # keys are sets of ints; data is doubles avail_pp = el.data() for pid in el.key(): available_pp[pid] = avail_pp for pid in invadable_planet_ids: # TODO: reorganize planet = universe.getPlanet(pid) if not planet: continue sys_id = planet.systemID sys_partial_vis_turn = universe.getVisibilityTurnsMap(planet.systemID, empire_id).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(pid, empire_id).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: continue for pid2 in ColonisationAI.empire_species_systems.get(sys_id, {}).get('pids', []): if available_pp.get(pid2, 0) < 2: # TODO: improve troop base PP sufficiency determination break planet2 = universe.getPlanet(pid2) if not planet2: continue if pid not in foAI.foAIstate.qualifyingTroopBaseTargets and planet2.speciesName in ColonisationAI.empire_ship_builders: foAI.foAIstate.qualifyingTroopBaseTargets.setdefault(pid, [pid2, -1]) break for pid in list(foAI.foAIstate.qualifyingTroopBaseTargets): planet = universe.getPlanet(pid) # TODO: also check that still have a colony in this system that can make troops if planet and planet.owner == empire_id: del foAI.foAIstate.qualifyingTroopBaseTargets[pid] secure_ai_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types([EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE]) for pid in (set(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) - set(invasion_targeted_planet_ids)): # TODO: consider overriding standard invasion mission planet = universe.getPlanet(pid) if foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] != -1: reserved_troop_base_targets.append(pid) if planet: all_invasion_targeted_system_ids.add(planet.systemID) continue # already building for here sys_id = planet.systemID this_sys_status = foAI.foAIstate.systemStatus.get(sys_id, {}) if (planet.currentMeterValue(fo.meterType.shield) > 0 and this_sys_status.get('myFleetRating', 0) < 0.8 * this_sys_status.get('totalThreat', 0)): continue loc = foAI.foAIstate.qualifyingTroopBaseTargets[pid][0] best_base_trooper_here = ProductionAI.getBestShipInfo(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_ORBITAL_INVASION, loc)[1] if best_base_trooper_here == None: # shouldn't be possible at this point, but just to be safe continue # TODO: have TroopShipDesigner give the expected number of troops including species effects troops_per_ship = best_base_trooper_here.troopCapacity if not troops_per_ship: continue this_score, p_troops = evaluate_invasion_planet(pid, empire, secure_ai_fleet_missions, False) best_ship, col_design, build_choices = ProductionAI.getBestShipInfo(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_ORBITAL_INVASION, loc) if not best_ship: continue n_bases = math.ceil((p_troops+1) / troops_per_ship) # TODO: reconsider this +1 safety factor print "Invasion base planning, need %d troops at %d pership, will build %d ships." % ((p_troops+1), troops_per_ship, n_bases) retval = fo.issueEnqueueShipProductionOrder(best_ship, loc) print "Enqueueing %d Troop Bases at %s for %s" % (n_bases, PlanetUtilsAI.planet_name_ids([loc]), PlanetUtilsAI.planet_name_ids([pid])) if retval != 0: all_invasion_targeted_system_ids.add(planet.systemID) reserved_troop_base_targets.append(pid) foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] = loc fo.issueChangeProductionQuantityOrder(empire.productionQueue.size - 1, 1, int(n_bases)) fo.issueRequeueProductionOrder(empire.productionQueue.size - 1, 0) invasion_timer.start("evaluating target planets") # TODO: check if any invasion_targeted_planet_ids need more troops assigned evaluated_planet_ids = list(set(invadable_planet_ids) - set(invasion_targeted_planet_ids) - set(reserved_troop_base_targets)) print "Evaluating potential invasions, PlanetIDs: %s" % evaluated_planet_ids evaluated_planets = assign_invasion_values(evaluated_planet_ids, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, fleet_suppliable_planet_ids, empire) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, (pscore, ptroops) in evaluated_planets.items()] sorted_planets.sort(key=lambda x: x[1], reverse=True) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, pscore, ptroops in sorted_planets] print if sorted_planets: print "Invadable planets:\n%-6s | %-6s | %-16s | %-16s | Troops" % ('ID', 'Score', 'Name', 'Race') for pid, pscore, ptroops in sorted_planets: planet = universe.getPlanet(pid) if planet: print "%6d | %6d | %16s | %16s | %d" % (pid, pscore, planet.name, planet.speciesName, ptroops) else: print "%6d | %6d | Error: invalid planet ID" % (pid, pscore) else: print "No Invadable planets identified" sorted_planets = filter(lambda x: x[1] > 0, sorted_planets) # export opponent planets for other AI modules AIstate.opponentPlanetIDs = [pid for pid, _, _ in sorted_planets] AIstate.invasionTargets = sorted_planets # export invasion targeted systems for other AI modules AIstate.invasionTargetedSystemIDs = list(all_invasion_targeted_system_ids) invasion_timer.stop(section_name="evaluating %d target planets" % (len(evaluated_planet_ids))) invasion_timer.end()
def 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: print "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_id=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) system_status = foAI.foAIstate.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 = 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) 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 == 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) 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: print(' - 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) 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]
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 = {} systems_status = foAI.foAIstate.systemStatus enemy_sup_factor = {} # enemy supply for sys_id in universe.systemIDs: already_assigned_rating[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) already_assigned_rating[last_sys] = CombatRatingsAI.combine_ratings(already_assigned_rating.get(last_sys, 0), this_rating) for sys_id in universe.systemIDs: my_defense_rating = systems_status.get(sys_id, {}).get('mydefenses', {}).get('overall', 0) already_assigned_rating[sys_id] = CombatRatingsAI.combine_ratings(my_defense_rating, already_assigned_rating[sys_id]) if _verbose_mil_reporting and already_assigned_rating[sys_id]: print "\t System %s already assigned rating %.1f" % ( universe.getSystem(sys_id), already_assigned_rating[sys_id]) # get systems to defend capital_id = PlanetUtilsAI.get_capital() if capital_id is not None: capital_planet = universe.getPlanet(capital_id) else: capital_planet = None # TODO: if no owned planets try to capture one! if capital_planet: capital_sys_id = capital_planet.systemID else: # should be rare, but so as to not break code below, pick a randomish mil-centroid system capital_sys_id = None # unless we can find one to use system_dict = {} for fleet_id in all_military_fleet_ids: status = foAI.foAIstate.fleetStatus.get(fleet_id, None) if status is not None: sys_id = status['sysID'] if not list(universe.getSystem(sys_id).planetIDs): continue system_dict[sys_id] = system_dict.get(sys_id, 0) + status.get('rating', 0) ranked_systems = sorted([(val, sys_id) for sys_id, val in system_dict.items()]) if ranked_systems: capital_sys_id = ranked_systems[-1][-1] else: try: capital_sys_id = foAI.foAIstate.fleetStatus.items()[0][1]['sysID'] except: pass num_targets = max(10, PriorityAI.allotted_outpost_targets) top_target_planets = ([pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allottedInvasionTargets] if pscore > 20] + [pid for pid, (pscore, spec) in foAI.foAIstate.colonisableOutpostIDs.items()[:num_targets] if pscore > 20] + [pid for pid, (pscore, spec) in foAI.foAIstate.colonisablePlanetIDs.items()[:num_targets] if pscore > 20]) top_target_planets.extend(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) base_col_target_systems = PlanetUtilsAI.get_systems(top_target_planets) top_target_systems = [] for sys_id in AIstate.invasionTargetedSystemIDs + base_col_target_systems: if sys_id not in top_target_systems: if foAI.foAIstate.systemStatus[sys_id]['totalThreat'] > get_tot_mil_rating(): continue top_target_systems.append(sys_id) # doing this rather than set, to preserve order try: # capital defense allocation_helper = AllocationHelper(already_assigned_rating, 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(ColonisationAI.systems_by_supply_tier.get(0, []) + ColonisationAI.systems_by_supply_tier.get(1, [])) 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.keys() + foAI.foAIstate.visBorderSystemIDs.keys() accessible_system_ids = [sys_id for sys_id in visible_system_ids if universe.systemsConnected(sys_id, home_system_id, empire_id)] current_mil_systems = [sid for sid, alloc, 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 allocation_groups = allocation_helper.allocation_by_groups allocations = allocation_helper.allocations 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, take_any, max_alloc in allocation_groups.get(cat, []): if remaining_mil_rating <= 0: break this_alloc = min(remaining_mil_rating, max_alloc) new_allocations.append((sid, this_alloc, alloc, 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, take_any, max_alloc in allocation_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, take_any, max_alloc in allocation_groups.get(cat, []): if sid not in base_allocs: break if remaining_mil_rating <= 0: new_allocations.append((sid, alloc, alloc, take_any)) else: new_rating = min(remaining_mil_rating + alloc, max_alloc) new_allocations.append((sid, new_rating, alloc, take_any)) remaining_mil_rating -= (new_rating - alloc) if "Main" in thisround: _military_allocations = new_allocations _min_mil_allocations.clear() _min_mil_allocations.update([(sid, alloc) for sid, alloc, take_any, _ in allocations]) if _verbose_mil_reporting or "Main" in thisround: print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % (thisround, dict([(sid, alloc) for sid, alloc, minalloc, take_any in new_allocations])) print "(Apparently) remaining military rating: %.1f" % remaining_mil_rating # export military systems for other AI modules if "Main" in thisround: AIstate.militarySystemIDs = list(set([sid for sid, alloc, minalloc, take_any in new_allocations]).union( [sid for sid in allocation_helper.already_assigned_rating if allocation_helper.already_assigned_rating[sid] > 0])) else: AIstate.militarySystemIDs = list(set([sid for sid, alloc, minalloc, take_any in new_allocations]).union(AIstate.militarySystemIDs)) return new_allocations
def calculateMilitaryPriority(): """calculates the demand for military ships by military targeted systems""" global unmetThreat universe = fo.getUniverse() empire = fo.getEmpire() empireID = empire.empireID capitalID = PlanetUtilsAI.get_capital() if capitalID is not None and capitalID != -1: homeworld = universe.getPlanet(capitalID) else: return 0 # no capitol (not even a capitol-in-the-making), means can't produce any ships have_l1_weaps = (tech_is_complete("SHP_WEAPON_1_4") or (tech_is_complete("SHP_WEAPON_1_3") and tech_is_complete("SHP_MIL_ROBO_CONT")) or tech_is_complete("SHP_WEAPON_2_1") or tech_is_complete("SHP_WEAPON_4_1")) have_l2_weaps = (tech_is_complete("SHP_WEAPON_2_3") or tech_is_complete("SHP_WEAPON_4_1")) enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) allottedInvasionTargets = 1 + int(fo.currentTurn() / 25) targetPlanetIDs = [ pid for pid, pscore, trp in AIstate.invasionTargets[:allottedInvasionTargets] ] + [ pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs.items() [:allottedColonyTargets] ] + [ pid for pid, pscore in foAI.foAIstate.colonisableOutpostIDs.items() [:allottedColonyTargets] ] mySystems = set(AIstate.popCtrSystemIDs).union(AIstate.outpostSystemIDs) targetSystems = set(PlanetUtilsAI.get_systems(targetPlanetIDs)) curShipRating = ProductionAI.cur_best_mil_ship_rating() cSRR = curShipRating**0.5 defense_ships_needed = 0 currentTurn = fo.currentTurn() ships_needed = 0 defense_ships_needed = 0 ships_needed_allocation = [] for sysID in mySystems.union(targetSystems): status = foAI.foAIstate.systemStatus.get(sysID, {}) myRating = status.get('myFleetRating', 0) my_defenses = status.get('mydefenses', {}).get('overall', 0) baseMonsterThreat = status.get('monsterThreat', 0) #scale monster threat so that in early - mid game big monsters don't over-drive military production monsterThreat = baseMonsterThreat if currentTurn > 200: pass elif currentTurn > 100: if baseMonsterThreat >= 2000: monsterThreat = 2000 + (currentTurn / 100.0 - 1) * (baseMonsterThreat - 2000) elif currentTurn > 30: if baseMonsterThreat >= 2000: monsterThreat = 0 else: if baseMonsterThreat > 200: monsterThreat = 0 if sysID in mySystems: threatRoot = status.get('fleetThreat', 0)**0.5 + 0.8 * status.get( 'max_neighbor_threat', 0)**0.5 + 0.2 * status.get( 'neighborThreat', 0)**0.5 + monsterThreat**0.5 + status.get( 'planetThreat', 0)**0.5 else: threatRoot = status.get('fleetThreat', 0)**0.5 + monsterThreat**0.5 + status.get( 'planetThreat', 0)**0.5 ships_needed_here = math.ceil( (max(0, (threatRoot - (myRating**0.5 + my_defenses**0.5)))**2) / curShipRating) ships_needed += ships_needed_here ships_needed_allocation.append((sysID, ships_needed_here)) if sysID in mySystems: defense_ships_needed += ships_needed_here scale = (75 + ProductionAI.curBestMilShipCost()) / 2.0 #militaryPriority = int( 40 + max(0, 75*unmetThreat / curShipRating) ) part1 = min(1 * fo.currentTurn(), 40) part2 = max(0, int(75 * ships_needed)) militaryPriority = part1 + part2 #militaryPriority = min(1*fo.currentTurn(), 40) + max(0, int(scale*ships_needed)) if not have_l1_weaps: militaryPriority /= 2.0 elif not (have_l2_weaps and enemies_sighted): militaryPriority /= 1.5 #print "Calculating Military Priority: 40 + 75 * unmetThreat/curShipRating \n\t Priority: %d \t unmetThreat %.0f curShipRating: %.0f"%(militaryPriority, unmetThreat, curShipRating) fmt_string = "Calculating Military Priority: min(t,40) + %d * ships_needed \n\t Priority: %d \t ships_needed: %d \t defense_ships_needed: %d \t curShipRating: %.0f \t l1_weaps: %s \t enemies_sighted: %s" print fmt_string % (scale, militaryPriority, ships_needed, defense_ships_needed, curShipRating, have_l1_weaps, enemies_sighted) print "Source of milship demand: ", ships_needed_allocation if foAI.foAIstate.aggression < fo.aggression.typical: militaryPriority *= (1.0 + foAI.foAIstate.aggression) / ( 1.0 + fo.aggression.typical) return max(militaryPriority, 0)