def _get_base_outpost_defense_value() -> float: """Return planet defenses contribution towards planet evaluations.""" # TODO: assess current AI defense technology, compare the resulting planetary rating to the current # best military ship design, derive a planet defenses value related to the cost of such a ship net_count = sum( tech_is_complete(tech_name) for tech_name in AIDependencies.DEFENSE_DEFENSE_NET_TECHS) regen_count = sum( tech_is_complete(tech_name) for tech_name in AIDependencies.DEFENSE_REGEN_TECHS) garrison_count = sum( tech_is_complete(tech_name) for tech_name in AIDependencies.DEFENSE_GARRISON_TECHS) shield_count = sum( tech_is_complete(tech_name) for tech_name in AIDependencies.DEFENSE_SHIELDS_TECHS) # not counting mine techs because their effects are per-system, not per-planet # for now, just combing these for rough composite factor # since outposts have no infrastructure (until late game at least), many of these have less weight # than for colonies result = 3 * (0.1 + net_count) * (1 + regen_count / 3.0) * ( 1 + garrison_count / 6.0) * (1 + shield_count / 3.0) return round(result, 2)
def _get_base_colony_defense_value(): """ :return:planet defenses contribution towards planet evaluations :rtype float """ # TODO: assess current AI defense technology, compare the resulting planetary rating to the current # best military ship design, derive a planet defenses value related to the cost of such a ship net_count = sum( tech_is_complete(tech_name) for tech_name in AIDependencies.DEFENSE_DEFENSE_NET_TECHS) regen_count = sum( tech_is_complete(tech_name) for tech_name in AIDependencies.DEFENSE_REGEN_TECHS) garrison_count = sum( tech_is_complete(tech_name) for tech_name in AIDependencies.DEFENSE_GARRISON_TECHS) shield_count = sum( tech_is_complete(tech_name) for tech_name in AIDependencies.DEFENSE_SHIELDS_TECHS) # not counting mine techs because their effects are per-system, not per-planet # for now, just combing these for rough composite factor result = 4 * (0.1 + net_count) * (1 + regen_count / 2.0) * ( 1 + garrison_count / 4.0) * (1 + shield_count / 2.0) return round(result, 2)
def _get_colony_builders() -> Dict[SpeciesName, List[PlanetId]]: """ Return mutable state. """ colony_build_locations = {} # get it into colonizer list even if no colony yet if tech_is_complete(AIDependencies.EXOBOT_TECH_NAME): colony_build_locations["SP_EXOBOT"] = [] # get it into colonizer list even if no colony yet for spec_name in AIDependencies.EXTINCT_SPECIES: if tech_is_complete("TECH_COL_" + spec_name): colony_build_locations["SP_" + spec_name] = [] return colony_build_locations
def is_possible(tech_name): return all([ empire.getTechStatus(tech_name) == fo.techStatus.researchable, not tech_is_complete(tech_name), not exclude_tech(tech_name), tech_name not in queued_techs, ])
def _calculate_outpost_priority(): """Calculates the demand for outpost ships by colonisable planets.""" global allotted_outpost_targets base_outpost_cost = AIDependencies.OUTPOST_POD_COST enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() total_pp = fo.getEmpire().productionPoints num_colonies = len(list(AIstate.popCtrIDs)) colony_growth_barrier = foAI.foAIstate.character.max_number_colonies() if num_colonies > colony_growth_barrier: return 0.0 mil_prio = foAI.foAIstate.get_priority(PriorityType.PRODUCTION_MILITARY) not_sparse, enemy_unseen = 0, 0 is_sparse, enemy_seen = 1, 1 allotted_portion = { (not_sparse, enemy_unseen): (0.6, 0.8), (not_sparse, enemy_seen): (0.3, 0.4), (is_sparse, enemy_unseen): (0.8, 0.9), (is_sparse, enemy_seen): (0.3, 0.4), }[(galaxy_is_sparse, any(enemies_sighted))] allotted_portion = foAI.foAIstate.character.preferred_outpost_portion( allotted_portion) if mil_prio < 100: allotted_portion *= 2 elif mil_prio < 200: allotted_portion *= 1.5 allotted_outpost_targets = 1 + int( total_pp * 3 * allotted_portion / base_outpost_cost) num_outpost_targets = len([ pid for (pid, (score, specName)) in foAI.foAIstate.colonisableOutpostIDs.items() if score > 1.0 * base_outpost_cost / 3.0 ][:allotted_outpost_targets]) if num_outpost_targets == 0 or not tech_is_complete( AIDependencies.OUTPOSTING_TECH): return 0 outpost_ship_ids = FleetUtilsAI.get_empire_fleet_ids_by_role( MissionType.OUTPOST) num_outpost_ships = len( FleetUtilsAI.extract_fleet_ids_without_mission_types(outpost_ship_ids)) outpost_priority = ( 50.0 * (num_outpost_targets - num_outpost_ships)) / num_outpost_targets # print # print "Number of Outpost Ships : " + str(num_outpost_ships) # print "Number of Colonisable outposts: " + str(num_outpost_planet_ids) print "Priority for outpost ships: " + str(outpost_priority) if outpost_priority < 1: return 0 return outpost_priority
def get_extra_colony_builders() -> List[str]: """ Returns species the empire can build without having a colony, i.e. Exobots plus extinct species that has been enabled. """ ret = ["SP_EXOBOT"] for spec_name in AIDependencies.EXTINCT_SPECIES: if tech_is_complete("TECH_COL_" + spec_name): ret.append("SP_" + spec_name) return ret
def calculateOutpostPriority(): """calculates the demand for outpost ships by colonisable planets""" global allotted_outpost_targets base_outpost_cost = AIDependencies.OUTPOST_POD_COST enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() total_pp = fo.getEmpire().productionPoints num_colonies = len(list(AIstate.popCtrIDs)) # significant growth barrier for low aggression, negligible for high aggression if num_colonies > colony_growth_barrier: return 0.0 mil_prio = foAI.foAIstate.get_priority( EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY) NOT_SPARCE, ENEMY_UNSEEN = 0, 0 IS_SPARCE, ENEMY_SEEN = 1, 1 allotted_portion = { (NOT_SPARCE, ENEMY_UNSEEN): (0.6, 0.8), (NOT_SPARCE, ENEMY_SEEN): (0.3, 0.4), (IS_SPARCE, ENEMY_UNSEEN): (0.8, 0.9), (IS_SPARCE, ENEMY_SEEN): (0.3, 0.4), }[(galaxy_is_sparse, any(enemies_sighted))][fo.empireID() % 2] if mil_prio < 100: allotted_portion *= 2 elif mil_prio < 200: allotted_portion *= 1.5 allotted_outpost_targets = 1 + int( total_pp * 3 * allotted_portion / base_outpost_cost) num_outpost_targets = len([ pid for (pid, (score, specName)) in foAI.foAIstate.colonisableOutpostIDs.items() if score > 1.0 * base_outpost_cost / 3.0 ][:allotted_outpost_targets]) if num_outpost_targets == 0 or not tech_is_complete( AIDependencies.OUTPOSTING_TECH): return 0 outpostShipIDs = FleetUtilsAI.get_empire_fleet_ids_by_role( EnumsAI.AIFleetMissionType.FLEET_MISSION_OUTPOST) num_outpost_ships = len( FleetUtilsAI.extract_fleet_ids_without_mission_types(outpostShipIDs)) outpost_priority = 50 * (num_outpost_targets - num_outpost_ships) / num_outpost_targets # print # print "Number of Outpost Ships : " + str(num_outpost_ships) # print "Number of Colonisable outposts: " + str(num_outpost_planet_ids) print "Priority for outpost ships : " + str(outpost_priority) if outpost_priority < 1: return 0 return outpost_priority
def calculateOutpostPriority(): """calculates the demand for outpost ships by colonisable planets""" global allotted_outpost_targets base_outpost_cost = AIDependencies.OUTPOST_POD_COST enemies_sighted = foAI.foAIstate.misc.get("enemies_sighted", {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() total_pp = fo.getEmpire().productionPoints num_colonies = len(list(AIstate.popCtrIDs)) # significant growth barrier for low aggression, negligible for high aggression if num_colonies > colony_growth_barrier: return 0.0 mil_prio = foAI.foAIstate.get_priority(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_MILITARY) NOT_SPARCE, ENEMY_UNSEEN = 0, 0 IS_SPARCE, ENEMY_SEEN = 1, 1 allotted_portion = { (NOT_SPARCE, ENEMY_UNSEEN): (0.6, 0.8), (NOT_SPARCE, ENEMY_SEEN): (0.3, 0.4), (IS_SPARCE, ENEMY_UNSEEN): (0.8, 0.9), (IS_SPARCE, ENEMY_SEEN): (0.3, 0.4), }[(galaxy_is_sparse, any(enemies_sighted))][fo.empireID() % 2] if mil_prio < 100: allotted_portion *= 2 elif mil_prio < 200: allotted_portion *= 1.5 allotted_outpost_targets = 1 + int(total_pp * 3 * allotted_portion / base_outpost_cost) num_outpost_targets = len( [ pid for (pid, (score, specName)) in foAI.foAIstate.colonisableOutpostIDs.items() if score > 1.0 * base_outpost_cost / 3.0 ][:allotted_outpost_targets] ) if num_outpost_targets == 0 or not tech_is_complete(AIDependencies.OUTPOSTING_TECH): return 0 outpostShipIDs = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_OUTPOST) num_outpost_ships = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(outpostShipIDs)) outpost_priority = 50 * (num_outpost_targets - num_outpost_ships) / num_outpost_targets # print # print "Number of Outpost Ships : " + str(num_outpost_ships) # print "Number of Colonisable outposts: " + str(num_outpost_planet_ids) print "Priority for outpost ships : " + str(outpost_priority) if outpost_priority < 1: return 0 return outpost_priority
def _calculate_outpost_priority(): """Calculates the demand for outpost ships by colonisable planets.""" global allotted_outpost_targets base_outpost_cost = AIDependencies.OUTPOST_POD_COST enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() total_pp = fo.getEmpire().productionPoints colony_growth_barrier = foAI.foAIstate.character.max_number_colonies() if state.get_number_of_colonies() > colony_growth_barrier: return 0.0 mil_prio = foAI.foAIstate.get_priority(PriorityType.PRODUCTION_MILITARY) not_sparse, enemy_unseen = 0, 0 is_sparse, enemy_seen = 1, 1 allotted_portion = { (not_sparse, enemy_unseen): (0.6, 0.8), (not_sparse, enemy_seen): (0.3, 0.4), (is_sparse, enemy_unseen): (0.8, 0.9), (is_sparse, enemy_seen): (0.3, 0.4), }[(galaxy_is_sparse, any(enemies_sighted))] allotted_portion = foAI.foAIstate.character.preferred_outpost_portion(allotted_portion) if mil_prio < 100: allotted_portion *= 2 elif mil_prio < 200: allotted_portion *= 1.5 allotted_outpost_targets = 1 + int(total_pp * 3 * allotted_portion / base_outpost_cost) num_outpost_targets = len([pid for (pid, (score, specName)) in foAI.foAIstate.colonisableOutpostIDs.items() if score > max(1.0 * base_outpost_cost / 3.0, ColonisationAI.MINIMUM_COLONY_SCORE)] [:allotted_outpost_targets]) if num_outpost_targets == 0 or not tech_is_complete(AIDependencies.OUTPOSTING_TECH): return 0 outpost_ship_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.OUTPOST) num_outpost_ships = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(outpost_ship_ids)) outpost_priority = (50.0 * (num_outpost_targets - num_outpost_ships)) / num_outpost_targets # discourage early outposting for SP_SLY, due to supply and stealth considerations they are best off # using colony ships until they have other colonizers (and more established military) if list(ColonisationAI.empire_colonizers) == ["SP_SLY"]: outpost_priority /= 3.0 # print # print "Number of Outpost Ships : " + str(num_outpost_ships) # print "Number of Colonisable outposts: " + str(num_outpost_planet_ids) print "Priority for outpost ships: " + str(outpost_priority) if outpost_priority < 1: return 0 return outpost_priority
def _calculate_outpost_priority(): """Calculates the demand for outpost ships by colonisable planets.""" global allotted_outpost_targets base_outpost_cost = AIDependencies.OUTPOST_POD_COST enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() total_pp = fo.getEmpire().productionPoints num_colonies = len(list(AIstate.popCtrIDs)) colony_growth_barrier = foAI.foAIstate.character.max_number_colonies() if num_colonies > colony_growth_barrier: return 0.0 mil_prio = foAI.foAIstate.get_priority(PriorityType.PRODUCTION_MILITARY) not_sparse, enemy_unseen = 0, 0 is_sparse, enemy_seen = 1, 1 allotted_portion = { (not_sparse, enemy_unseen): (0.6, 0.8), (not_sparse, enemy_seen): (0.3, 0.4), (is_sparse, enemy_unseen): (0.8, 0.9), (is_sparse, enemy_seen): (0.3, 0.4), }[(galaxy_is_sparse, any(enemies_sighted))] allotted_portion = foAI.foAIstate.character.preferred_outpost_portion(allotted_portion) if mil_prio < 100: allotted_portion *= 2 elif mil_prio < 200: allotted_portion *= 1.5 allotted_outpost_targets = 1 + int(total_pp * 3 * allotted_portion / base_outpost_cost) num_outpost_targets = len([pid for (pid, (score, specName)) in foAI.foAIstate.colonisableOutpostIDs.items() if score > 1.0 * base_outpost_cost / 3.0][:allotted_outpost_targets]) if num_outpost_targets == 0 or not tech_is_complete(AIDependencies.OUTPOSTING_TECH): return 0 outpost_ship_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.OUTPOST) num_outpost_ships = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(outpost_ship_ids)) outpost_priority = 50 * (num_outpost_targets - num_outpost_ships) / num_outpost_targets # print # print "Number of Outpost Ships : " + str(num_outpost_ships) # print "Number of Colonisable outposts: " + str(num_outpost_planet_ids) print "Priority for outpost ships: " + str(outpost_priority) if outpost_priority < 1: return 0 return outpost_priority
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 _base_asteroid_mining_val(): """returns an estimation for the industry value of an asteroid belt for a colony in the system""" return 2 if tech_is_complete("PRO_MICROGRAV_MAN") else 1
def set_planet_resource_foci(): """set resource focus of planets """ newFoci = {} print "\n============================" print "Collecting info to assess Planet Focus Changes\n" universe = fo.getUniverse() empire = fo.getEmpire() currentTurn = fo.currentTurn() # set the random seed (based on galaxy seed, empire ID and current turn) # for game-reload consistency freq = min(3, (max(5, currentTurn - 80)) / 4.0) ** (1.0 / 3) if not (limitAssessments and (abs(currentTurn - lastFociCheck[0]) < 1.5 * freq) and (random.random() < 1.0 / freq)): lastFociCheck[0] = currentTurn resource_timer.start("getPlanets") empirePlanetIDs = list(PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)) resource_timer.start("Filter") resource_timer.start("Priority") # TODO: take into acct splintering of resource groups # fleetSupplyableSystemIDs = empire.fleetSupplyableSystemIDs # fleetSupplyablePlanetIDs = PlanetUtilsAI.get_planets_in__systems_ids(fleetSupplyableSystemIDs) ppPrio = foAI.foAIstate.get_priority(AIPriorityType.PRIORITY_RESOURCE_PRODUCTION) rpPrio = foAI.foAIstate.get_priority(AIPriorityType.PRIORITY_RESOURCE_RESEARCH) priorityRatio = float(rpPrio) / (ppPrio + 0.0001) resource_timer.start("Shuffle") # not supporting Growth for general planets until also adding code to make sure would actually benefit # shuffle(generalPlanetIDs) resource_timer.start("Targets") planets = map(universe.getPlanet, empirePlanetIDs) planetMap.clear() planetMap.update(zip(empirePlanetIDs, planets)) if useGrowth: for metab, metabIncPop in ColonisationAI.empire_metabolisms.items(): for special in [aspec for aspec in AIDependencies.metabolismBoostMap.get(metab, []) if aspec in ColonisationAI.available_growth_specials]: rankedPlanets = [] for pid in ColonisationAI.available_growth_specials[special]: planet = planetMap[pid] cur_focus = planet.focus pop = planet.currentMeterValue(fo.meterType.population) if (pop > metabIncPop - 2 * planet.size) or (GFocus not in planet.availableFoci): # not enough benefit to lose local production, or can't put growth focus here continue for special2 in ["COMPUTRONIUM_SPECIAL"]: if special2 in planet.specials: break else: # didn't have any specials that would override interest in growth special print "Considering Growth Focus for %s (%d) with special %s; planet has pop %.1f and %s metabolism incremental pop is %.1f" % ( planet.name, pid, special, pop, metab, metabIncPop) if cur_focus == GFocus: pop -= 4 # discourage changing current focus to minimize focus-changing penalties rankedPlanets.append((pop, pid, cur_focus)) if not rankedPlanets: continue rankedPlanets.sort() print "Considering Growth Focus choice for special %s; possible planet pop, id pairs are %s" % (metab, rankedPlanets) for spSize, spPID, cur_focus in rankedPlanets: # index 0 should be able to set focus, but just in case... result = 1 if cur_focus != GFocus: result = fo.issueChangeFocusOrder(spPID, GFocus) if result == 1: newFoci[spPID] = GFocus if spPID in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index(spPID)] print "%s focus of planet %s (%d) at Growth Focus" % (["set", "left"][cur_focus == GFocus], planetMap[spPID].name, spPID) break else: print "failed setting focus of planet %s (%d) at Growth Focus; focus left at %s" % (planetMap[spPID].name, spPID, planetMap[spPID].focus) already_have_comp_moon = False for pid in empirePlanetIDs: planet = planetMap[pid] if "COMPUTRONIUM_SPECIAL" in planet.specials and RFocus in planet.availableFoci and not already_have_comp_moon: curFocus = planet.focus newFoci[pid] = RFocus result = 0 if curFocus != RFocus: result = fo.issueChangeFocusOrder(pid, RFocus) if result == 1: universe.updateMeterEstimates(empirePlanetIDs) if curFocus == RFocus or result == 1: already_have_comp_moon = True print "%s focus of planet %s (%d) (with Computronium Moon) at Research Focus" % (["set", "left"][curFocus == RFocus], planetMap[pid].name, pid) if pid in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index(pid)] continue if ((([bld.buildingTypeName for bld in map(universe.getObject, planet.buildingIDs) if bld.buildingTypeName in ["BLD_CONC_CAMP", "BLD_CONC_CAMP_REMNANT"]] != []) or ([ccspec for ccspec in planet.specials if ccspec in ["CONC_CAMP_MASTER_SPECIAL", "CONC_CAMP_SLAVE_SPECIAL"]] != [])) and IFocus in planet.availableFoci): curFocus = planet.focus newFoci[pid] = IFocus result = 0 if curFocus != IFocus: result = fo.issueChangeFocusOrder(pid, IFocus) if result == 1: print ("Tried setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, got result %d and focus %s" % (newFoci[pid], planet.name, pid, planet.speciesName, curFocus, result, planetMap[pid].focus)) universe.updateMeterEstimates(empirePlanetIDs) if (result != 1) or planetMap[pid].focus != IFocus: newplanet = universe.getPlanet(pid) print ("Error: Failed setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, but new planet copy shows %s" % (newFoci[pid], planetMap[pid].name, pid, planetMap[pid].speciesName, planetMap[pid].focus, newplanet.focus)) if curFocus == IFocus or result == 1: print "%s focus of planet %s (%d) (with Concentration Camps/Remnants) at Industry Focus" % (["set", "left"][curFocus == IFocus], planetMap[pid].name, pid) if pid in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index(pid)] continue # pp, rp = get_resource_target_totals(empirePlanetIDs, planetMap) pp, rp = get_resource_target_totals(planetMap.keys()) for pid in empirePlanetIDs: planet = planetMap[pid] if PFocus in planet.availableFoci and assess_protection_focus(pid): curFocus = planet.focus newFoci[pid] = PFocus result = 0 if curFocus != PFocus: result = fo.issueChangeFocusOrder(pid, PFocus) if result == 1: print ("Tried setting %s for planet %s (%d) with species %s and current focus %s, got result %d and focus %s" % (newFoci[pid], planet.name, pid, planet.speciesName, curFocus, result, planet.focus)) universe.updateMeterEstimates(empirePlanetIDs) if (result != 1) or planet.focus != PFocus: newplanet = universe.getPlanet(pid) print ("Error: Failed setting %s for planet %s (%d) with species %s and current focus %s, but new planet copy shows %s" % (newFoci[pid], planet.name, pid, planet.speciesName, planet.focus, newplanet.focus)) if curFocus == PFocus or result == 1: print "%s focus of planet %s (%d) at Protection(Defense) Focus" % (["set", "left"][curFocus == PFocus], planet.name, pid) if pid in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index(pid)] continue print "\n-----------------------------------------" print "Making Planet Focus Change Determinations\n" ratios = [] # for each planet, calculate RP:PP value ratio at which industry/Mining focus and research focus would have the same total value, & sort by that # include a bias to slightly discourage changing foci curTargetPP = 0.001 curTargetRP = 0.001 resource_timer.start("Loop") # loop has_force = tech_is_complete("CON_FRC_ENRG_STRC") preset_ids = set(planetMap.keys()) - set(empirePlanetIDs) ctPP0, ctRP0 = 0, 0 for pid in preset_ids: nPP, nRP = newTargets.get(pid, {}).get(planetMap[pid].focus, [0, 0]) curTargetPP += nPP curTargetRP += nRP iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0]) ctPP0 += iPP ctRP0 += iRP id_set = set(empirePlanetIDs) for adj_round in [1, 2, 3, 4]: maxi_ratio = ctRP0 / max(0.01, ctPP0) # should only change between rounds 1 and 2 for pid in list(id_set): if adj_round == 1: # tally max Industry iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0]) ctPP0 += iPP ctRP0 += iRP continue II, IR = newTargets[pid][IFocus] RI, RR = newTargets[pid][RFocus] CI, CR = currentOutput[pid][IFocus], currentOutput[pid][RFocus] research_penalty = (currentFocus[pid] != RFocus) # calculate factor F at which II + F * IR == RI + F * RR =====> F = ( II-RI ) / (RR-IR) thisFactor = (II - RI) / max(0.01, RR - IR) # don't let denominator be zero for planets where focus doesn't change RP planet = planetMap[pid] if adj_round == 2: # take research at planets with very cheap research if (maxi_ratio < priorityRatio) and (curTargetRP < priorityRatio * ctPP0) and (thisFactor <= 1.0): curTargetPP += RI curTargetRP += RR newFoci[pid] = RFocus id_set.discard(pid) continue if adj_round == 3: # take research at planets where can do reasonable balance if has_force or (foAI.foAIstate.aggression < fo.aggression.aggressive) or (curTargetRP >= priorityRatio * ctPP0): continue pop = planet.currentMeterValue(fo.meterType.population) t_pop = planet.currentMeterValue(fo.meterType.targetPopulation) # if AI is aggressive+, and this planet in range where temporary Research focus can get an additional RP at cost of 1 PP, and still need some RP, then do it if pop < t_pop - 5: continue if (CI > II + 8) or (((RR > II) or ((RR - CR) >= 1 + 2 * research_penalty)) and ((RR - IR) >= 3) and ((CR - IR) >= 0.7 * ((II - CI) * (1 + 0.1 * research_penalty)))): curTargetPP += CI - 1 - research_penalty curTargetRP += CR + 1 newFoci[pid] = RFocus id_set.discard(pid) continue # adj_round == 4 assume default IFocus curTargetPP += II # icurTargets initially calculated by Industry focus, which will be our default focus curTargetRP += IR newFoci[pid] = IFocus ratios.append((thisFactor, pid)) ratios.sort() printedHeader = False fociMap = {IFocus: "Industry", RFocus: "Research", MFocus: "Mining", GFocus: "Growth", PFocus: "Defense"} gotAlgo = tech_is_complete("LRN_ALGO_ELEGANCE") for ratio, pid in ratios: do_research = False # (newFoci[pid]==RFocus) if (priorityRatio < (curTargetRP / (curTargetPP + 0.0001))) and not do_research: # we have enough RP if ratio < 1.1 and foAI.foAIstate.aggression > fo.aggression.cautious: # but wait, RP is still super cheap relative to PP, maybe will take more RP if priorityRatio < 1.5 * (curTargetRP / (curTargetPP + 0.0001)): # yeah, really a glut of RP, stop taking RP break else: # RP not super cheap & we have enough, stop taking it break II, IR = newTargets[pid][IFocus] RI, RR = newTargets[pid][RFocus] # if currentFocus[pid] == MFocus: # II = max( II, newTargets[pid][MFocus][0] ) if do_research or (gotAlgo and ( (ratio > 2.0 and curTargetPP < 15) or (ratio > 2.5 and curTargetPP < 25 and II > 5) or (ratio > 3.0 and curTargetPP < 40 and II > 5) or (ratio > 4.0 and curTargetPP < 100 and II > 10) or ((curTargetRP + RR - IR) / max(0.001, curTargetPP - II + RI) > 2 * priorityRatio))): # we already have algo elegance and more RP would be too expensive, or overkill if not printedHeader: printedHeader = True print "Rejecting further Research Focus choices as too expensive:" print "%34s|%20s|%15s |%15s|%15s |%15s |%15s" % (" Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " rejectedFocus ", " rejected target RP/PP ", "rejected RP-PP EQF") oldFocus = currentFocus[pid] cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][RFocus] otPP, otRP = newTargets[pid].get(oldFocus, (0, 0)) ntPP, ntRP = newTargets[pid].get(RFocus, (0, 0)) print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f | cF: %8s | nF: %8s | cT: %5.1f / %5.1f | %.2f" % (pid, planetMap[pid].name, cRP, cPP, otRP, otPP, fociMap.get(oldFocus, 'unknown'), fociMap[RFocus], ntRP, ntPP, ratio) continue # RP is getting too expensive, but might be willing to still allocate from a planet with less PP to lose # if planetMap[pid].currentMeterValue(fo.meterType.targetPopulation) >0: #only set to research if pop won't die out newFoci[pid] = RFocus curTargetRP += (RR - IR) curTargetPP -= (II - RI) print "============================" print "Planet Focus Assignments to achieve target RP/PP ratio of %.2f from current ratio of %.2f ( %.1f / %.1f )" % (priorityRatio, rp / (pp + 0.0001), rp, pp) print "Max Industry assignments would result in target RP/PP ratio of %.2f ( %.1f / %.1f )" % (ctRP0 / (ctPP0 + 0.0001), ctRP0, ctPP0) print "-------------------------------------" print "%34s|%20s|%15s |%15s|%15s |%15s " % (" Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " newFocus ", " new target RP/PP ") totalChanged = 0 for id_set in empirePlanetIDs, preset_ids: for pid in id_set: canFocus = planetMap[pid].currentMeterValue(fo.meterType.targetPopulation) > 0 oldFocus = currentFocus[pid] newFocus = newFoci.get(pid, IFocus) cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][RFocus] otPP, otRP = newTargets[pid].get(oldFocus, (0, 0)) ntPP, ntRP = otPP, otRP if newFocus != oldFocus and newFocus in planetMap[pid].availableFoci: # planetMap[pid].focus totalChanged += 1 if newFocus != planetMap[pid].focus: result = fo.issueChangeFocusOrder(pid, newFocus) if result == 1: ntPP, ntRP = newTargets[pid].get(newFocus, (0, 0)) else: print "Trouble changing focus of planet %s (%d) to %s" % (planetMap[pid].name, pid, newFocus) print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f | cF: %8s | nF: %8s | cT: %5.1f / %5.1f " % (pid, planetMap[pid].name, cRP, cPP, otRP, otPP, fociMap.get(oldFocus, 'unknown'), fociMap[newFocus], ntRP, ntPP) print "-------------------------------------" print "-------------------------------------" print "Final Ratio Target (turn %4d) RP/PP : %.2f ( %.1f / %.1f ) after %d Focus changes" % (fo.currentTurn(), curTargetRP / (curTargetPP + 0.0001), curTargetRP, curTargetPP, totalChanged) resource_timer.end() aPP, aRP = empire.productionPoints, empire.resourceProduction(fo.resourceType.research) # Next string used in charts. Don't modify it! print "Current Output (turn %4d) RP/PP : %.2f ( %.1f / %.1f )" % (fo.currentTurn(), aRP / (aPP + 0.0001), aRP, aPP) print "------------------------" print "ResourcesAI Time Requirements:"
def get_completed_techs(): """Get completed and available for use techs.""" return [tech for tech in fo.techs() if tech_is_complete(tech)]
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)
def set_planet_resource_foci(): """set resource focus of planets """ newFoci = {} print "\n============================" print "Collecting info to assess Planet Focus Changes\n" universe = fo.getUniverse() empire = fo.getEmpire() currentTurn = fo.currentTurn() # set the random seed (based on galaxy seed, empire ID and current turn) # for game-reload consistency freq = min(3, (max(5, currentTurn - 80)) / 4.0)**(1.0 / 3) if not (limitAssessments and (abs(currentTurn - lastFociCheck[0]) < 1.5 * freq) and (random.random() < 1.0 / freq)): lastFociCheck[0] = currentTurn resource_timer.start("getPlanets") empirePlanetIDs = list( PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)) resource_timer.start("Filter") resource_timer.start("Priority") # TODO: take into acct splintering of resource groups # fleetSupplyableSystemIDs = empire.fleetSupplyableSystemIDs # fleetSupplyablePlanetIDs = PlanetUtilsAI.get_planets_in__systems_ids(fleetSupplyableSystemIDs) ppPrio = foAI.foAIstate.get_priority( AIPriorityType.PRIORITY_RESOURCE_PRODUCTION) rpPrio = foAI.foAIstate.get_priority( AIPriorityType.PRIORITY_RESOURCE_RESEARCH) priorityRatio = float(rpPrio) / (ppPrio + 0.0001) resource_timer.start("Shuffle") # not supporting Growth for general planets until also adding code to make sure would actually benefit # shuffle(generalPlanetIDs) resource_timer.start("Targets") planets = map(universe.getPlanet, empirePlanetIDs) planetMap.clear() planetMap.update(zip(empirePlanetIDs, planets)) if useGrowth: for metab, metabIncPop in ColonisationAI.empire_metabolisms.items( ): for special in [ aspec for aspec in AIDependencies.metabolismBoostMap.get( metab, []) if aspec in ColonisationAI.available_growth_specials ]: rankedPlanets = [] for pid in ColonisationAI.available_growth_specials[ special]: planet = planetMap[pid] cur_focus = planet.focus pop = planet.currentMeterValue(fo.meterType.population) if (pop > metabIncPop - 2 * planet.size) or ( GFocus not in planet.availableFoci ): # not enough benefit to lose local production, or can't put growth focus here continue for special2 in ["COMPUTRONIUM_SPECIAL"]: if special2 in planet.specials: break else: # didn't have any specials that would override interest in growth special print "Considering Growth Focus for %s (%d) with special %s; planet has pop %.1f and %s metabolism incremental pop is %.1f" % ( planet.name, pid, special, pop, metab, metabIncPop) if cur_focus == GFocus: pop -= 4 # discourage changing current focus to minimize focus-changing penalties rankedPlanets.append((pop, pid, cur_focus)) if not rankedPlanets: continue rankedPlanets.sort() print "Considering Growth Focus choice for special %s; possible planet pop, id pairs are %s" % ( metab, rankedPlanets) for spSize, spPID, cur_focus in rankedPlanets: # index 0 should be able to set focus, but just in case... result = 1 if cur_focus != GFocus: result = fo.issueChangeFocusOrder(spPID, GFocus) if result == 1: newFoci[spPID] = GFocus if spPID in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index( spPID)] print "%s focus of planet %s (%d) at Growth Focus" % ( ["set", "left"][cur_focus == GFocus], planetMap[spPID].name, spPID) break else: print "failed setting focus of planet %s (%d) at Growth Focus; focus left at %s" % ( planetMap[spPID].name, spPID, planetMap[spPID].focus) for pid in empirePlanetIDs: planet = planetMap[pid] if "COMPUTRONIUM_SPECIAL" in planet.specials: # TODO: ensure only one (extremely rarely needed) curFocus = planet.focus if RFocus not in planet.availableFoci: continue newFoci[pid] = RFocus result = 0 if curFocus != RFocus: result = fo.issueChangeFocusOrder(pid, RFocus) if result == 1: universe.updateMeterEstimates(empirePlanetIDs) if curFocus == RFocus or result == 1: print "%s focus of planet %s (%d) (with Computronium Moon) at Research Focus" % ( ["set", "left" ][curFocus == RFocus], planetMap[pid].name, pid) if pid in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index(pid)] elif (([ bld.buildingTypeName for bld in map(universe.getObject, planet.buildingIDs) if bld.buildingTypeName in ["BLD_CONC_CAMP", "BLD_CONC_CAMP_REMNANT"] ] != []) or ([ ccspec for ccspec in planet.specials if ccspec in ["CONC_CAMP_MASTER_SPECIAL", "CONC_CAMP_SLAVE_SPECIAL"] ] != [])): if IFocus not in planet.availableFoci: continue curFocus = planet.focus newFoci[pid] = IFocus result = 0 if curFocus != IFocus: result = fo.issueChangeFocusOrder(pid, IFocus) if result == 1: print( "Tried setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, got result %d and focus %s" % (newFoci[pid], planet.name, pid, planet.speciesName, curFocus, result, planetMap[pid].focus)) universe.updateMeterEstimates(empirePlanetIDs) if (result != 1) or planetMap[pid].focus != IFocus: newplanet = universe.getPlanet(pid) print( "Error: Failed setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, but new planet copy shows %s" % (newFoci[pid], planetMap[pid].name, pid, planetMap[pid].speciesName, planetMap[pid].focus, newplanet.focus)) if curFocus == IFocus or result == 1: print "%s focus of planet %s (%d) (with Concentration Camps/Remnants) at Industry Focus" % ( ["set", "left" ][curFocus == IFocus], planetMap[pid].name, pid) if pid in empirePlanetIDs: del empirePlanetIDs[empirePlanetIDs.index(pid)] # pp, rp = get_resource_target_totals(empirePlanetIDs, planetMap) pp, rp = get_resource_target_totals(planetMap.keys()) print "\n-----------------------------------------" print "Making Planet Focus Change Determinations\n" ratios = [] # for each planet, calculate RP:PP value ratio at which industry/Mining focus and research focus would have the same total value, & sort by that # include a bias to slightly discourage changing foci curTargetPP = 0.001 curTargetRP = 0.001 resource_timer.start("Loop") # loop has_force = tech_is_complete("CON_FRC_ENRG_STRC") preset_ids = set(planetMap.keys()) - set(empirePlanetIDs) ctPP0, ctRP0 = 0, 0 for pid in preset_ids: nPP, nRP = newTargets.get(pid, {}).get(planetMap[pid].focus, [0, 0]) curTargetPP += nPP curTargetRP += nRP iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0]) ctPP0 += iPP ctRP0 += iRP id_set = set(empirePlanetIDs) for adj_round in [1, 2, 3, 4]: maxi_ratio = ctRP0 / max( 0.01, ctPP0) # should only change between rounds 1 and 2 for pid in list(id_set): if adj_round == 1: # tally max Industry iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0]) ctPP0 += iPP ctRP0 += iRP continue II, IR = newTargets[pid][IFocus] RI, RR = newTargets[pid][RFocus] CI, CR = currentOutput[pid][IFocus], currentOutput[pid][RFocus] research_penalty = (currentFocus[pid] != RFocus) # calculate factor F at which II + F * IR == RI + F * RR =====> F = ( II-RI ) / (RR-IR) thisFactor = (II - RI) / max( 0.01, RR - IR ) # don't let denominator be zero for planets where focus doesn't change RP planet = planetMap[pid] if adj_round == 2: # take research at planets with very cheap research if (maxi_ratio < priorityRatio) and ( curTargetRP < priorityRatio * ctPP0) and (thisFactor <= 1.0): curTargetPP += RI curTargetRP += RR newFoci[pid] = RFocus id_set.discard(pid) continue if adj_round == 3: # take research at planets where can do reasonable balance if has_force or (foAI.foAIstate.aggression < fo.aggression.aggressive) or ( curTargetRP >= priorityRatio * ctPP0): continue pop = planet.currentMeterValue(fo.meterType.population) t_pop = planet.currentMeterValue( fo.meterType.targetPopulation) # if AI is aggressive+, and this planet in range where temporary Research focus can get an additional RP at cost of 1 PP, and still need some RP, then do it if pop < t_pop - 5: continue if (CI > II + 8) or (((RR > II) or ( (RR - CR) >= 1 + 2 * research_penalty)) and ((RR - IR) >= 3) and ((CR - IR) >= 0.7 * ((II - CI) * (1 + 0.1 * research_penalty)))): curTargetPP += CI - 1 - research_penalty curTargetRP += CR + 1 newFoci[pid] = RFocus id_set.discard(pid) continue # adj_round == 4 assume default IFocus curTargetPP += II # icurTargets initially calculated by Industry focus, which will be our default focus curTargetRP += IR newFoci[pid] = IFocus ratios.append((thisFactor, pid)) ratios.sort() printedHeader = False fociMap = { IFocus: "Industry", RFocus: "Research", MFocus: "Mining", GFocus: "Growth" } gotAlgo = tech_is_complete("LRN_ALGO_ELEGANCE") for ratio, pid in ratios: do_research = False # (newFoci[pid]==RFocus) if (priorityRatio < (curTargetRP / (curTargetPP + 0.0001)) ) and not do_research: # we have enough RP if ratio < 1.1 and foAI.foAIstate.aggression > fo.aggression.cautious: # but wait, RP is still super cheap relative to PP, maybe will take more RP if priorityRatio < 1.5 * ( curTargetRP / (curTargetPP + 0.0001) ): # yeah, really a glut of RP, stop taking RP break else: # RP not super cheap & we have enough, stop taking it break II, IR = newTargets[pid][IFocus] RI, RR = newTargets[pid][RFocus] # if currentFocus[pid] == MFocus: # II = max( II, newTargets[pid][MFocus][0] ) if do_research or ( gotAlgo and ((ratio > 2.0 and curTargetPP < 15) or (ratio > 2.5 and curTargetPP < 25 and II > 5) or (ratio > 3.0 and curTargetPP < 40 and II > 5) or (ratio > 4.0 and curTargetPP < 100 and II > 10) or ((curTargetRP + RR - IR) / max(0.001, curTargetPP - II + RI) > 2 * priorityRatio)) ): # we already have algo elegance and more RP would be too expensive, or overkill if not printedHeader: printedHeader = True print "Rejecting further Research Focus choices as too expensive:" print "%34s|%20s|%15s |%15s|%15s |%15s |%15s" % ( " Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " rejectedFocus ", " rejected target RP/PP ", "rejected RP-PP EQF") oldFocus = currentFocus[pid] cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][ RFocus] otPP, otRP = newTargets[pid].get(oldFocus, (0, 0)) ntPP, ntRP = newTargets[pid].get(RFocus, (0, 0)) print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f | cF: %8s | nF: %8s | cT: %5.1f / %5.1f | %.2f" % ( pid, planetMap[pid].name, cRP, cPP, otRP, otPP, fociMap.get(oldFocus, 'unknown'), fociMap[RFocus], ntRP, ntPP, ratio) continue # RP is getting too expensive, but might be willing to still allocate from a planet with less PP to lose # if planetMap[pid].currentMeterValue(fo.meterType.targetPopulation) >0: #only set to research if pop won't die out newFoci[pid] = RFocus curTargetRP += (RR - IR) curTargetPP -= (II - RI) print "============================" print "Planet Focus Assignments to achieve target RP/PP ratio of %.2f from current ratio of %.2f ( %.1f / %.1f )" % ( priorityRatio, rp / (pp + 0.0001), rp, pp) print "Max Industry assignments would result in target RP/PP ratio of %.2f ( %.1f / %.1f )" % ( ctRP0 / (ctPP0 + 0.0001), ctRP0, ctPP0) print "-------------------------------------" print "%34s|%20s|%15s |%15s|%15s |%15s " % ( " Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " newFocus ", " new target RP/PP ") totalChanged = 0 for id_set in empirePlanetIDs, preset_ids: for pid in id_set: canFocus = planetMap[pid].currentMeterValue( fo.meterType.targetPopulation) > 0 oldFocus = currentFocus[pid] newFocus = newFoci.get(pid, IFocus) cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][ RFocus] otPP, otRP = newTargets[pid].get(oldFocus, (0, 0)) ntPP, ntRP = otPP, otRP if newFocus != oldFocus and newFocus in planetMap[ pid].availableFoci: # planetMap[pid].focus totalChanged += 1 if newFocus != planetMap[pid].focus: result = fo.issueChangeFocusOrder(pid, newFocus) if result == 1: ntPP, ntRP = newTargets[pid].get(newFocus, (0, 0)) else: print "Trouble changing focus of planet %s (%d) to %s" % ( planetMap[pid].name, pid, newFocus) print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f | cF: %8s | nF: %8s | cT: %5.1f / %5.1f " % ( pid, planetMap[pid].name, cRP, cPP, otRP, otPP, fociMap.get(oldFocus, 'unknown'), fociMap[newFocus], ntRP, ntPP) print "-------------------------------------" print "-------------------------------------" print "Final Ratio Target (turn %4d) RP/PP : %.2f ( %.1f / %.1f ) after %d Focus changes" % ( fo.currentTurn(), curTargetRP / (curTargetPP + 0.0001), curTargetRP, curTargetPP, totalChanged) resource_timer.end() aPP, aRP = empire.productionPoints, empire.resourceProduction( fo.resourceType.research) # Next string used in charts. Don't modify it! print "Current Output (turn %4d) RP/PP : %.2f ( %.1f / %.1f )" % ( fo.currentTurn(), aRP / (aPP + 0.0001), aRP, aPP) print "------------------------" print "ResourcesAI Time Requirements:"
def _calculate_research_priority(): """Calculates the AI empire's demand for research.""" universe = fo.getUniverse() empire = fo.getEmpire() current_turn = fo.currentTurn() enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) recent_enemies = [x for x in enemies_sighted if x > current_turn - 8] industry_priority = foAI.foAIstate.get_priority(PriorityType.RESOURCE_PRODUCTION) got_algo = tech_is_complete(AIDependencies.LRN_ALGO_ELEGANCE) got_quant = tech_is_complete(AIDependencies.LRN_QUANT_NET) research_queue_list = ResearchAI.get_research_queue_techs() orb_gen_tech = AIDependencies.PRO_ORBITAL_GEN got_orb_gen = tech_is_complete(orb_gen_tech) mgrav_prod_tech = AIDependencies.PRO_MICROGRAV_MAN got_mgrav_prod = tech_is_complete(mgrav_prod_tech) # got_solar_gen = tech_is_complete(AIDependencies.PRO_SOL_ORB_GEN) milestone_techs = ["PRO_SENTIENT_AUTOMATION", "LRN_DISTRIB_THOUGHT", "LRN_QUANT_NET", "SHP_WEAPON_2_4", "SHP_WEAPON_3_2", "SHP_WEAPON_4_2"] milestones_done = [mstone for mstone in milestone_techs if tech_is_complete(mstone)] print "Research Milestones accomplished at turn %d: %s" % (current_turn, milestones_done) total_pp = empire.productionPoints total_rp = empire.resourceProduction(fo.resourceType.research) industry_surge = (foAI.foAIstate.character.may_surge_industry(total_pp, total_rp) and (((orb_gen_tech in research_queue_list[:2] or got_orb_gen) and state.have_gas_giant) or ((mgrav_prod_tech in research_queue_list[:2] or got_mgrav_prod) and state.have_asteroids)) and (state.get_number_of_colonies() < 12)) # get current industry production & Target owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) planets = map(universe.getPlanet, owned_planet_ids) target_rp = sum(map(lambda x: x.currentMeterValue(fo.meterType.targetResearch), planets)) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) style_index = foAI.foAIstate.character.preferred_research_cutoff([0, 1]) if foAI.foAIstate.character.may_maximize_research(): style_index += 1 cutoff_sets = [[25, 45, 70, 110], [30, 45, 70, 150], [25, 40, 80, 160]] cutoffs = cutoff_sets[style_index] settings = [[1.3, .7, .5, .4, .35], [1.4, 0.8, 0.6, 0.5, 0.35], [1.4, 0.8, 0.6, 0.5, 0.4]][style_index] if (current_turn < cutoffs[0]) or (not got_algo) or ((style_index == 0) and not got_orb_gen and (current_turn < cutoffs[1])): research_priority = settings[0] * industry_priority # high research at beginning of game to get easy gro tech and to get research booster Algotrithmic Elegance elif (not got_orb_gen) or (current_turn < cutoffs[1]): research_priority = settings[1] * industry_priority # med-high research elif current_turn < cutoffs[2]: research_priority = settings[2] * industry_priority # med-high industry elif current_turn < cutoffs[3]: research_priority = settings[3] * industry_priority # med-high industry else: research_queue = list(empire.researchQueue) research_priority = settings[4] * industry_priority # high industry , low research if len(research_queue) == 0: research_priority = 0 # done with research elif len(research_queue) < 5 and research_queue[-1].allocation > 0: research_priority = len(research_queue) * 0.01 * industry_priority # barely not done with research elif len(research_queue) < 10 and research_queue[-1].allocation > 0: research_priority = (4 + 2 * len(research_queue)) * 0.01 * industry_priority # almost done with research elif len(research_queue) < 20 and research_queue[int(len(research_queue) / 2)].allocation > 0: research_priority *= 0.7 # closing in on end of research if industry_surge: if galaxy_is_sparse and not any(enemies_sighted): research_priority *= 0.5 else: research_priority *= 0.8 if ((tech_is_complete("SHP_WEAPON_2_4") or tech_is_complete("SHP_WEAPON_4_1")) and tech_is_complete(AIDependencies.PROD_AUTO_NAME)): # industry_factor = [ [0.25, 0.2], [0.3, 0.25], [0.3, 0.25] ][style_index ] # researchPriority = min(researchPriority, industry_factor[got_solar_gen]*industryPriority) research_priority *= 0.9 if got_quant: research_priority = min(research_priority + 0.1 * industry_priority, research_priority * 1.3) research_priority = int(research_priority) print "Research Production (current/target) : ( %.1f / %.1f )" % (total_rp, target_rp) print "Priority for Research: %d (new target ~ %d RP)" % (research_priority, total_pp * research_priority / industry_priority) if len(enemies_sighted) < (2 + current_turn/20.0): # TODO: adjust for colonisation priority research_priority *= 1.2 if (current_turn > 20) and (len(recent_enemies) > 3): research_priority *= 0.8 return research_priority
def is_possible(tech_name): return all([empire.getTechStatus(tech_name) == fo.techStatus.researchable, not tech_is_complete(tech_name), not exclude_tech(tech_name), tech_name not in queued_techs])
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 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 generate_research_orders(): """generate research orders""" report_adjustments = False empire = fo.getEmpire() empire_id = empire.empireID enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() print "Research Queue Management:" resource_production = empire.resourceProduction(fo.resourceType.research) print "\nTotal Current Research Points: %.2f\n" % resource_production print "Techs researched and available for use:" completed_techs = sorted(list(get_completed_techs())) tlist = completed_techs+3*[" "] tlines = zip(tlist[0::3], tlist[1::3], tlist[2::3]) for tline in tlines: print "%25s %25s %25s" % tline print # # report techs currently at head of research queue # research_queue = empire.researchQueue research_queue_list = get_research_queue_techs() inProgressTechs.clear() tech_turns_left = {} if research_queue_list: print "Techs currently at head of Research Queue:" for element in list(research_queue)[:10]: tech_turns_left[element.tech] = element.turnsLeft if element.allocation > 0.0: inProgressTechs[element.tech] = True this_tech = fo.getTech(element.tech) if not this_tech: print "Error: can't retrieve tech ", element.tech continue missing_prereqs = [preReq for preReq in this_tech.recursivePrerequisites(empire_id) if preReq not in completed_techs] # unlocked_items = [(uli.name, uli.type) for uli in this_tech.unlocked_items] unlocked_items = [uli.name for uli in this_tech.unlockedItems] if not missing_prereqs: print " %25s allocated %6.2f RP -- unlockable items: %s " % (element.tech, element.allocation, unlocked_items) else: print " %25s allocated %6.2f RP -- missing preReqs: %s -- unlockable items: %s " % (element.tech, element.allocation, missing_prereqs, unlocked_items) print # # set starting techs, or after turn 100 add any additional default techs # if (fo.currentTurn() == 1) or ((fo.currentTurn() < 5) and (len(research_queue_list) == 0)): research_index = get_research_index() new_tech = TechsListsAI.sparse_galaxy_techs(research_index) if galaxy_is_sparse else TechsListsAI.primary_meta_techs(research_index) print "Empire %s (%d) is selecting research index %d" % (empire.name, empire_id, research_index) # techs_to_enqueue = (set(new_tech)-(set(completed_techs)|set(research_queue_list))) techs_to_enqueue = new_tech[:] tech_base = set(completed_techs+research_queue_list) techs_to_add = [] for tech in techs_to_enqueue: if tech not in tech_base: this_tech = fo.getTech(tech) if this_tech is None: print "Error: desired tech '%s' appears to not exist" % tech continue missing_prereqs = [preReq for preReq in this_tech.recursivePrerequisites(empire_id) if preReq not in tech_base] techs_to_add.extend(missing_prereqs + [tech]) tech_base.update(missing_prereqs+[tech]) cum_cost = 0 print " Enqueued Tech: %20s \t\t %8s \t %s" % ("Name", "Cost", "CumulativeCost") for name in techs_to_add: try: enqueue_res = fo.issueEnqueueTechOrder(name, -1) if enqueue_res == 1: this_tech = fo.getTech(name) this_cost = 0 if this_tech: this_cost = this_tech.researchCost(empire_id) cum_cost += this_cost print " Enqueued Tech: %20s \t\t %8.0f \t %8.0f" % (name, this_cost, cum_cost) else: print " Error: failed attempt to enqueued Tech: " + name except: print " Error: failed attempt to enqueued Tech: " + name print " Error: exception triggered and caught: ", traceback.format_exc() if foAI.foAIstate.aggression <= fo.aggression.cautious: research_queue_list = get_research_queue_techs() def_techs = TechsListsAI.defense_techs_1() for def_tech in def_techs: if def_tech not in research_queue_list[:5] and not tech_is_complete(def_tech): res = fo.issueEnqueueTechOrder(def_tech, min(3, len(research_queue_list))) print "Empire is very defensive, so attempted to fast-track %s, got result %d" % (def_tech, res) if False and foAI.foAIstate.aggression >= fo.aggression.aggressive: # with current stats of Conc Camps, disabling this fast-track research_queue_list = get_research_queue_techs() if "CON_CONC_CAMP" in research_queue_list: insert_idx = min(40, research_queue_list.index("CON_CONC_CAMP")) else: insert_idx = max(0, min(40, len(research_queue_list)-10)) if "SHP_DEFLECTOR_SHIELD" in research_queue_list: insert_idx = min(insert_idx, research_queue_list.index("SHP_DEFLECTOR_SHIELD")) for cc_tech in ["CON_ARCH_PSYCH", "CON_CONC_CAMP"]: if cc_tech not in research_queue_list[:insert_idx + 1] and not tech_is_complete(cc_tech): res = fo.issueEnqueueTechOrder(cc_tech, insert_idx) msg = "Empire is very aggressive, so attempted to fast-track %s, got result %d" % (cc_tech, res) if report_adjustments: chat_human(msg) else: print msg print"" generate_default_research_order() print "\n\nAll techs:" alltechs = fo.techs() # returns names of all techs for tname in alltechs: print tname print "\n-------------------------------\nAll unqueued techs:" # coveredTechs = new_tech+completed_techs for tname in [tn for tn in alltechs if tn not in tech_base]: print tname elif fo.currentTurn() > 100: generate_default_research_order() research_queue_list = get_research_queue_techs() num_techs_accelerated = 1 # will ensure leading tech doesn't get dislodged got_ggg_tech = tech_is_complete("PRO_ORBITAL_GEN") got_sym_bio = tech_is_complete("GRO_SYMBIOTIC_BIO") got_xeno_gen = tech_is_complete("GRO_XENO_GENETICS") # # Consider accelerating techs; priority is # Supply/Detect range # xeno arch # ast / GG # gro xeno gen # distrib thought # quant net # pro sing gen # death ray 1 cleanup # # Supply range and detection range if False: # disabled for now, otherwise just to help with cold-folding / organization if len(foAI.foAIstate.colonisablePlanetIDs) == 0: best_colony_site_score = 0 else: best_colony_site_score = foAI.foAIstate.colonisablePlanetIDs.items()[0][1] if len(foAI.foAIstate.colonisableOutpostIDs) == 0: best_outpost_site_score = 0 else: best_outpost_site_score = foAI.foAIstate.colonisableOutpostIDs.items()[0][1] need_improved_scouting = (best_colony_site_score < 150 or best_outpost_site_score < 200) if need_improved_scouting: if not tech_is_complete("CON_ORBITAL_CON"): num_techs_accelerated += 1 if ("CON_ORBITAL_CON" not in research_queue_list[:1 + num_techs_accelerated]) and ( tech_is_complete("PRO_FUSION_GEN") or ("PRO_FUSION_GEN" in research_queue_list[:1 + num_techs_accelerated])): res = fo.issueEnqueueTechOrder("CON_ORBITAL_CON", num_techs_accelerated) msg = "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d" % ("CON_ORBITAL_CON", res) if report_adjustments: chat_human(msg) else: print msg elif not tech_is_complete("CON_CONTGRAV_ARCH"): num_techs_accelerated += 1 if ("CON_CONTGRAV_ARCH" not in research_queue_list[:1+num_techs_accelerated]) and ( tech_is_complete("CON_METRO_INFRA")): for supply_tech in [_s_tech for _s_tech in ["CON_ARCH_MONOFILS", "CON_CONTGRAV_ARCH"] if not tech_is_complete(_s_tech)]: res = fo.issueEnqueueTechOrder(supply_tech, num_techs_accelerated) msg = "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d" % (supply_tech, res) if report_adjustments: chat_human(msg) else: print msg elif not tech_is_complete("CON_GAL_INFRA"): num_techs_accelerated += 1 if ("CON_GAL_INFRA" not in research_queue_list[:1+num_techs_accelerated]) and ( tech_is_complete("PRO_SINGULAR_GEN")): res = fo.issueEnqueueTechOrder("CON_GAL_INFRA", num_techs_accelerated) msg = "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d" % ("CON_GAL_INFRA", res) if report_adjustments: chat_human(msg) else: print msg else: pass research_queue_list = get_research_queue_techs() # could add more supply tech if False and not tech_is_complete("SPY_DETECT_2"): # disabled for now, detect2 num_techs_accelerated += 1 if "SPY_DETECT_2" not in research_queue_list[:2+num_techs_accelerated] and tech_is_complete("PRO_FUSION_GEN"): if "CON_ORBITAL_CON" not in research_queue_list[:1+num_techs_accelerated]: res = fo.issueEnqueueTechOrder("SPY_DETECT_2", num_techs_accelerated) else: co_idx = research_queue_list.index("CON_ORBITAL_CON") res = fo.issueEnqueueTechOrder("SPY_DETECT_2", co_idx + 1) msg = "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d" % ("CON_ORBITAL_CON", res) if report_adjustments: chat_human(msg) else: print msg research_queue_list = get_research_queue_techs() # # check to accelerate xeno_arch if True: # just to help with cold-folding / organization if (ColonisationAI.gotRuins and not tech_is_complete("LRN_XENOARCH") and foAI.foAIstate.aggression >= fo.aggression.typical): if "LRN_ARTIF_MINDS" in research_queue_list: insert_idx = 7 + research_queue_list.index("LRN_ARTIF_MINDS") elif "GRO_SYMBIOTIC_BIO" in research_queue_list: insert_idx = research_queue_list.index("GRO_SYMBIOTIC_BIO") + 1 else: insert_idx = num_techs_accelerated if "LRN_XENOARCH" not in research_queue_list[:insert_idx]: for xenoTech in ["LRN_XENOARCH", "LRN_TRANSLING_THT", "LRN_PHYS_BRAIN", "LRN_ALGO_ELEGANCE"]: if not tech_is_complete(xenoTech) and xenoTech not in research_queue_list[:(insert_idx + 4)]: res = fo.issueEnqueueTechOrder(xenoTech, insert_idx) num_techs_accelerated += 1 msg = "ANCIENT_RUINS: have an ancient ruins, so attempted to fast-track %s to enable LRN_XENOARCH, got result %d" % (xenoTech, res) if report_adjustments: chat_human(msg) else: print msg research_queue_list = get_research_queue_techs() if False and not enemies_sighted: # curently disabled # params = [ (tech, gate, target_slot, add_tech_list), ] params = [("GRO_XENO_GENETICS", "PRO_EXOBOTS", "PRO_EXOBOTS", ["GRO_GENETIC_MED", "GRO_XENO_GENETICS"]), ("PRO_EXOBOTS", "PRO_SENTIENT_AUTOMATION", "PRO_SENTIENT_AUTOMATION", ["PRO_EXOBOTS"]), ("PRO_SENTIENT_AUTOMATION", "PRO_NANOTECH_PROD", "PRO_NANOTECH_PROD", ["PRO_SENTIENT_AUTOMATION"]), ("PRO_INDUSTRY_CENTER_I", "GRO_SYMBIOTIC_BIO", "GRO_SYMBIOTIC_BIO", ["PRO_ROBOTIC_PROD", "PRO_FUSION_GEN", "PRO_INDUSTRY_CENTER_I"]), ("GRO_SYMBIOTIC_BIO", "SHP_ORG_HULL", "SHP_ZORTRIUM_PLATE", ["GRO_SYMBIOTIC_BIO"]), ] for (tech, gate, target_slot, add_tech_list) in params: if tech_is_complete(tech): break if tech_turns_left.get(gate, 0) not in [0, 1, 2]: # needs to exclude -1, the flag for no predicted completion continue if target_slot in research_queue_list: target_index = 1 + research_queue_list.index(target_slot) else: target_index = num_techs_accelerated for move_tech in add_tech_list: print "for tech %s, target_slot %s, target_index:%s ; num_techs_accelerated:%s" % (move_tech, target_slot, target_index, num_techs_accelerated) if tech_is_complete(move_tech): continue if target_index <= num_techs_accelerated: num_techs_accelerated += 1 if move_tech not in research_queue_list[:1 + target_index]: res = fo.issueEnqueueTechOrder(move_tech, target_index) msg = "Research: To prioritize %s, have advanced %s to slot %d" % (tech, move_tech, target_index) if report_adjustments: chat_human(msg) else: print msg target_index += 1 # # check to accelerate asteroid or GG tech if True: # just to help with cold-folding / organization if ColonisationAI.got_ast: insert_idx = num_techs_accelerated if "GRO_SYMBIOTIC_BIO" not in research_queue_list else research_queue_list.index("GRO_SYMBIOTIC_BIO") ast_tech = "PRO_MICROGRAV_MAN" if not (tech_is_complete(ast_tech) or ast_tech in research_queue_list[:(1 + insert_idx)]): res = fo.issueEnqueueTechOrder(ast_tech, insert_idx) num_techs_accelerated += 1 msg = "Asteroids: plan to colonize an asteroid belt, so attempted to fast-track %s , got result %d" % (ast_tech, res) if report_adjustments: chat_human(msg) else: print msg research_queue_list = get_research_queue_techs() elif tech_is_complete("SHP_ZORTRIUM_PLATE"): insert_idx = (1 + insert_idx) if "LRN_FORCE_FIELD" not in research_queue_list else max(1 + insert_idx, research_queue_list.index("LRN_FORCE_FIELD") - 1) for ast_tech in ["SHP_ASTEROID_HULLS", "SHP_IMPROVED_ENGINE_COUPLINGS"]: if not tech_is_complete(ast_tech) and ast_tech not in research_queue_list[:insert_idx + 1]: res = fo.issueEnqueueTechOrder(ast_tech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 msg = "Asteroids: plan to colonize an asteroid belt, so attempted to fast-track %s , got result %d" % (ast_tech, res) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() if ColonisationAI.got_gg and not tech_is_complete("PRO_ORBITAL_GEN"): fusion_idx = 0 if "PRO_FUSION_GEN" not in research_queue_list else (1 + research_queue_list.index("PRO_FUSION_GEN")) forcefields_idx = 0 if "LRN_FORCE_FIELD" not in research_queue_list else (1 + research_queue_list.index("LRN_FORCE_FIELD")) insert_idx = max(fusion_idx, forcefields_idx) if enemies_sighted else fusion_idx if "PRO_ORBITAL_GEN" not in research_queue_list[:insert_idx+1]: res = fo.issueEnqueueTechOrder("PRO_ORBITAL_GEN", insert_idx) num_techs_accelerated += 1 msg = "GasGiant: plan to colonize a gas giant, so attempted to fast-track %s, got result %d" % ("PRO_ORBITAL_GEN", res) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # assess if our empire has any non-lousy colonizers, & boost gro_xeno_gen if we don't if True: # just to help with cold-folding / organization if got_ggg_tech and got_sym_bio and (not got_xeno_gen) and foAI.foAIstate.aggression >= fo.aggression.cautious: most_adequate = 0 for specName in ColonisationAI.empire_colonizers: environs = {} this_spec = fo.getSpecies(specName) if not this_spec: continue for ptype in [fo.planetType.swamp, fo.planetType.radiated, fo.planetType.toxic, fo.planetType.inferno, fo.planetType.barren, fo.planetType.tundra, fo.planetType.desert, fo.planetType.terran, fo.planetType.ocean, fo.planetType.asteroids]: environ = this_spec.getPlanetEnvironment(ptype) environs.setdefault(environ, []).append(ptype) most_adequate = max(most_adequate, len(environs.get(fo.planetEnvironment.adequate, []))) if most_adequate == 0: insert_idx = num_techs_accelerated for xg_tech in ["GRO_XENO_GENETICS", "GRO_GENETIC_ENG"]: if xg_tech not in research_queue_list[:1+num_techs_accelerated] and not tech_is_complete(xg_tech): res = fo.issueEnqueueTechOrder(xg_tech, insert_idx) num_techs_accelerated += 1 msg = "Empire has poor colonizers, so attempted to fast-track %s, got result %d" % (xg_tech, res) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # check to accelerate distrib thought if True: # just to help with cold-folding / organization if not tech_is_complete("LRN_DISTRIB_THOUGHT"): got_telepathy = False for specName in ColonisationAI.empire_species: this_spec = fo.getSpecies(specName) if this_spec and ("TELEPATHIC" in list(this_spec.tags)): got_telepathy = True break if (foAI.foAIstate.aggression > fo.aggression.cautious) and (empire.population() > ([300, 100][got_telepathy])): insert_idx = num_techs_accelerated for dt_ech in ["LRN_PHYS_BRAIN", "LRN_TRANSLING_THT", "LRN_PSIONICS", "LRN_DISTRIB_THOUGHT"]: if dt_ech not in research_queue_list[:insert_idx + 2] and not tech_is_complete(dt_ech): res = fo.issueEnqueueTechOrder(dt_ech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 fmt_str = "Empire has a telepathic race, so attempted to fast-track %s (got result %d)" fmt_str += " with current target_RP %.1f and current pop %.1f, on turn %d" msg = fmt_str % (dt_ech, res, resource_production, empire.population(), fo.currentTurn()) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # check to accelerate quant net if False: # disabled for now, otherwise just to help with cold-folding / organization if (foAI.foAIstate.aggression > fo.aggression.cautious) and (ColonisationAI.empire_status.get('researchers', 0) >= 40): if not tech_is_complete("LRN_QUANT_NET"): insert_idx = num_techs_accelerated # TODO determine min target slot if reenabling for qnTech in ["LRN_NDIM_SUBSPACE", "LRN_QUANT_NET"]: if qnTech not in research_queue_list[:insert_idx + 2] and not tech_is_complete(qnTech): res = fo.issueEnqueueTechOrder(qnTech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 msg = "Empire has many researchers, so attempted to fast-track %s (got result %d) on turn %d" % (qnTech, res, fo.currentTurn()) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # if we own a blackhole, accelerate sing_gen and conc camp if True: # just to help with cold-folding / organization if (fo.currentTurn() > 50 and len(AIstate.empireStars.get(fo.starType.blackHole, [])) != 0 and foAI.foAIstate.aggression > fo.aggression.cautious and not tech_is_complete(AIDependencies.PRO_SINGULAR_GEN) and tech_is_complete(AIDependencies.PRO_SOL_ORB_GEN)): # sing_tech_list = [ "LRN_GRAVITONICS" , "PRO_SINGULAR_GEN"] # formerly also "CON_ARCH_PSYCH", "CON_CONC_CAMP", sing_gen_tech = fo.getTech(AIDependencies.PRO_SINGULAR_GEN) sing_tech_list = [pre_req for pre_req in sing_gen_tech.recursivePrerequisites(empire_id) if not tech_is_complete(pre_req)] sing_tech_list += [AIDependencies.PRO_SINGULAR_GEN] for singTech in sing_tech_list: if singTech not in research_queue_list[:num_techs_accelerated+1]: res = fo.issueEnqueueTechOrder(singTech, num_techs_accelerated) num_techs_accelerated += 1 msg = "have a black hole star outpost/colony, so attempted to fast-track %s, got result %d" % (singTech, res) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # if got deathray from Ruins, remove most prereqs from queue if True: # just to help with cold-folding / organization if tech_is_complete("SHP_WEAPON_4_1"): this_tech = fo.getTech("SHP_WEAPON_4_1") if this_tech: missing_prereqs = [preReq for preReq in this_tech.recursivePrerequisites(empire_id) if preReq in research_queue_list] if len(missing_prereqs) > 2: # leave plasma 4 and 3 if up to them already for preReq in missing_prereqs: # sorted(missing_prereqs, reverse=True)[2:] if preReq in research_queue_list: fo.issueDequeueTechOrder(preReq) research_queue_list = get_research_queue_techs() if "SHP_WEAPON_4_2" in research_queue_list: # (should be) idx = research_queue_list.index("SHP_WEAPON_4_2") fo.issueEnqueueTechOrder("SHP_WEAPON_4_2", max(0, idx-18))
def generate_research_orders(): """generate research orders""" report_adjustments = False empire = fo.getEmpire() empire_id = empire.empireID enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() print "Research Queue Management:" resource_production = empire.resourceProduction(fo.resourceType.research) print "\nTotal Current Research Points: %.2f\n" % resource_production print "Techs researched and available for use:" completed_techs = sorted(list(get_completed_techs())) tlist = completed_techs + 3 * [" "] tlines = zip(tlist[0::3], tlist[1::3], tlist[2::3]) for tline in tlines: print "%25s %25s %25s" % tline print # # report techs currently at head of research queue # research_queue = empire.researchQueue research_queue_list = get_research_queue_techs() inProgressTechs.clear() tech_turns_left = {} if research_queue_list: print "Techs currently at head of Research Queue:" for element in list(research_queue)[:10]: tech_turns_left[element.tech] = element.turnsLeft if element.allocation > 0.0: inProgressTechs[element.tech] = True this_tech = fo.getTech(element.tech) if not this_tech: print "Error: can't retrieve tech ", element.tech continue missing_prereqs = [ preReq for preReq in this_tech.recursivePrerequisites(empire_id) if preReq not in completed_techs ] # unlocked_items = [(uli.name, uli.type) for uli in this_tech.unlocked_items] unlocked_items = [uli.name for uli in this_tech.unlockedItems] if not missing_prereqs: print " %25s allocated %6.2f RP -- unlockable items: %s " % ( element.tech, element.allocation, unlocked_items) else: print " %25s allocated %6.2f RP -- missing preReqs: %s -- unlockable items: %s " % ( element.tech, element.allocation, missing_prereqs, unlocked_items) print # # set starting techs, or after turn 100 add any additional default techs # if (fo.currentTurn() == 1) or ((fo.currentTurn() < 5) and (len(research_queue_list) == 0)): research_index = get_research_index() new_tech = TechsListsAI.sparse_galaxy_techs( research_index ) if galaxy_is_sparse else TechsListsAI.primary_meta_techs( research_index) print "Empire %s (%d) is selecting research index %d" % ( empire.name, empire_id, research_index) # techs_to_enqueue = (set(new_tech)-(set(completed_techs)|set(research_queue_list))) techs_to_enqueue = new_tech[:] tech_base = set(completed_techs + research_queue_list) techs_to_add = [] for tech in techs_to_enqueue: if tech not in tech_base: this_tech = fo.getTech(tech) if this_tech is None: print "Error: desired tech '%s' appears to not exist" % tech continue missing_prereqs = [ preReq for preReq in this_tech.recursivePrerequisites(empire_id) if preReq not in tech_base ] techs_to_add.extend(missing_prereqs + [tech]) tech_base.update(missing_prereqs + [tech]) cum_cost = 0 print " Enqueued Tech: %20s \t\t %8s \t %s" % ("Name", "Cost", "CumulativeCost") for name in techs_to_add: try: enqueue_res = fo.issueEnqueueTechOrder(name, -1) if enqueue_res == 1: this_tech = fo.getTech(name) this_cost = 0 if this_tech: this_cost = this_tech.researchCost(empire_id) cum_cost += this_cost print " Enqueued Tech: %20s \t\t %8.0f \t %8.0f" % ( name, this_cost, cum_cost) else: print " Error: failed attempt to enqueued Tech: " + name except: print " Error: failed attempt to enqueued Tech: " + name print " Error: exception triggered and caught: ", traceback.format_exc( ) if foAI.foAIstate.aggression <= fo.aggression.cautious: research_queue_list = get_research_queue_techs() def_techs = TechsListsAI.defense_techs_1() for def_tech in def_techs: if def_tech not in research_queue_list[: 5] and not tech_is_complete( def_tech): res = fo.issueEnqueueTechOrder( def_tech, min(3, len(research_queue_list))) print "Empire is very defensive, so attempted to fast-track %s, got result %d" % ( def_tech, res) if False and foAI.foAIstate.aggression >= fo.aggression.aggressive: # with current stats of Conc Camps, disabling this fast-track research_queue_list = get_research_queue_techs() if "CON_CONC_CAMP" in research_queue_list: insert_idx = min(40, research_queue_list.index("CON_CONC_CAMP")) else: insert_idx = max(0, min(40, len(research_queue_list) - 10)) if "SHP_DEFLECTOR_SHIELD" in research_queue_list: insert_idx = min( insert_idx, research_queue_list.index("SHP_DEFLECTOR_SHIELD")) for cc_tech in ["CON_ARCH_PSYCH", "CON_CONC_CAMP"]: if cc_tech not in research_queue_list[:insert_idx + 1] and not tech_is_complete( cc_tech): res = fo.issueEnqueueTechOrder(cc_tech, insert_idx) msg = "Empire is very aggressive, so attempted to fast-track %s, got result %d" % ( cc_tech, res) if report_adjustments: chat_human(msg) else: print msg print "" generate_default_research_order() print "\n\nAll techs:" alltechs = fo.techs() # returns names of all techs for tname in alltechs: print tname print "\n-------------------------------\nAll unqueued techs:" # coveredTechs = new_tech+completed_techs for tname in [tn for tn in alltechs if tn not in tech_base]: print tname elif fo.currentTurn() > 100: generate_default_research_order() research_queue_list = get_research_queue_techs() num_techs_accelerated = 1 # will ensure leading tech doesn't get dislodged got_ggg_tech = tech_is_complete("PRO_ORBITAL_GEN") got_sym_bio = tech_is_complete("GRO_SYMBIOTIC_BIO") got_xeno_gen = tech_is_complete("GRO_XENO_GENETICS") # # Consider accelerating techs; priority is # Supply/Detect range # xeno arch # ast / GG # gro xeno gen # distrib thought # quant net # pro sing gen # death ray 1 cleanup # # Supply range and detection range if False: # disabled for now, otherwise just to help with cold-folding / organization if len(foAI.foAIstate.colonisablePlanetIDs) == 0: best_colony_site_score = 0 else: best_colony_site_score = foAI.foAIstate.colonisablePlanetIDs.items( )[0][1] if len(foAI.foAIstate.colonisableOutpostIDs) == 0: best_outpost_site_score = 0 else: best_outpost_site_score = foAI.foAIstate.colonisableOutpostIDs.items( )[0][1] need_improved_scouting = (best_colony_site_score < 150 or best_outpost_site_score < 200) if need_improved_scouting: if not tech_is_complete("CON_ORBITAL_CON"): num_techs_accelerated += 1 if ("CON_ORBITAL_CON" not in research_queue_list[:1 + num_techs_accelerated] ) and (tech_is_complete("PRO_FUSION_GEN") or ("PRO_FUSION_GEN" in research_queue_list[:1 + num_techs_accelerated])): res = fo.issueEnqueueTechOrder("CON_ORBITAL_CON", num_techs_accelerated) msg = "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d" % ( "CON_ORBITAL_CON", res) if report_adjustments: chat_human(msg) else: print msg elif not tech_is_complete("CON_CONTGRAV_ARCH"): num_techs_accelerated += 1 if ("CON_CONTGRAV_ARCH" not in research_queue_list[:1 + num_techs_accelerated] ) and (tech_is_complete("CON_METRO_INFRA")): for supply_tech in [ _s_tech for _s_tech in ["CON_ARCH_MONOFILS", "CON_CONTGRAV_ARCH"] if not tech_is_complete(_s_tech) ]: res = fo.issueEnqueueTechOrder(supply_tech, num_techs_accelerated) msg = "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d" % ( supply_tech, res) if report_adjustments: chat_human(msg) else: print msg elif not tech_is_complete("CON_GAL_INFRA"): num_techs_accelerated += 1 if ("CON_GAL_INFRA" not in research_queue_list[:1 + num_techs_accelerated] ) and (tech_is_complete("PRO_SINGULAR_GEN")): res = fo.issueEnqueueTechOrder("CON_GAL_INFRA", num_techs_accelerated) msg = "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d" % ( "CON_GAL_INFRA", res) if report_adjustments: chat_human(msg) else: print msg else: pass research_queue_list = get_research_queue_techs() # could add more supply tech if False and not tech_is_complete( "SPY_DETECT_2"): # disabled for now, detect2 num_techs_accelerated += 1 if "SPY_DETECT_2" not in research_queue_list[:2 + num_techs_accelerated] and tech_is_complete( "PRO_FUSION_GEN" ): if "CON_ORBITAL_CON" not in research_queue_list[:1 + num_techs_accelerated]: res = fo.issueEnqueueTechOrder("SPY_DETECT_2", num_techs_accelerated) else: co_idx = research_queue_list.index("CON_ORBITAL_CON") res = fo.issueEnqueueTechOrder("SPY_DETECT_2", co_idx + 1) msg = "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d" % ( "CON_ORBITAL_CON", res) if report_adjustments: chat_human(msg) else: print msg research_queue_list = get_research_queue_techs() # # check to accelerate xeno_arch if True: # just to help with cold-folding / organization if (ColonisationAI.gotRuins and not tech_is_complete("LRN_XENOARCH") and foAI.foAIstate.aggression >= fo.aggression.typical): if "LRN_ARTIF_MINDS" in research_queue_list: insert_idx = 7 + research_queue_list.index("LRN_ARTIF_MINDS") elif "GRO_SYMBIOTIC_BIO" in research_queue_list: insert_idx = research_queue_list.index("GRO_SYMBIOTIC_BIO") + 1 else: insert_idx = num_techs_accelerated if "LRN_XENOARCH" not in research_queue_list[:insert_idx]: for xenoTech in [ "LRN_XENOARCH", "LRN_TRANSLING_THT", "LRN_PHYS_BRAIN", "LRN_ALGO_ELEGANCE" ]: if not tech_is_complete( xenoTech ) and xenoTech not in research_queue_list[:(insert_idx + 4)]: res = fo.issueEnqueueTechOrder(xenoTech, insert_idx) num_techs_accelerated += 1 msg = "ANCIENT_RUINS: have an ancient ruins, so attempted to fast-track %s to enable LRN_XENOARCH, got result %d" % ( xenoTech, res) if report_adjustments: chat_human(msg) else: print msg research_queue_list = get_research_queue_techs() if False and not enemies_sighted: # curently disabled # params = [ (tech, gate, target_slot, add_tech_list), ] params = [ ("GRO_XENO_GENETICS", "PRO_EXOBOTS", "PRO_EXOBOTS", ["GRO_GENETIC_MED", "GRO_XENO_GENETICS"]), ("PRO_EXOBOTS", "PRO_SENTIENT_AUTOMATION", "PRO_SENTIENT_AUTOMATION", ["PRO_EXOBOTS"]), ("PRO_SENTIENT_AUTOMATION", "PRO_NANOTECH_PROD", "PRO_NANOTECH_PROD", ["PRO_SENTIENT_AUTOMATION"]), ("PRO_INDUSTRY_CENTER_I", "GRO_SYMBIOTIC_BIO", "GRO_SYMBIOTIC_BIO", ["PRO_ROBOTIC_PROD", "PRO_FUSION_GEN", "PRO_INDUSTRY_CENTER_I"]), ("GRO_SYMBIOTIC_BIO", "SHP_ORG_HULL", "SHP_ZORTRIUM_PLATE", ["GRO_SYMBIOTIC_BIO"]), ] for (tech, gate, target_slot, add_tech_list) in params: if tech_is_complete(tech): break if tech_turns_left.get(gate, 0) not in [ 0, 1, 2 ]: # needs to exclude -1, the flag for no predicted completion continue if target_slot in research_queue_list: target_index = 1 + research_queue_list.index(target_slot) else: target_index = num_techs_accelerated for move_tech in add_tech_list: print "for tech %s, target_slot %s, target_index:%s ; num_techs_accelerated:%s" % ( move_tech, target_slot, target_index, num_techs_accelerated) if tech_is_complete(move_tech): continue if target_index <= num_techs_accelerated: num_techs_accelerated += 1 if move_tech not in research_queue_list[:1 + target_index]: res = fo.issueEnqueueTechOrder(move_tech, target_index) msg = "Research: To prioritize %s, have advanced %s to slot %d" % ( tech, move_tech, target_index) if report_adjustments: chat_human(msg) else: print msg target_index += 1 # # check to accelerate asteroid or GG tech if True: # just to help with cold-folding / organization if ColonisationAI.got_ast: insert_idx = num_techs_accelerated if "GRO_SYMBIOTIC_BIO" not in research_queue_list else research_queue_list.index( "GRO_SYMBIOTIC_BIO") ast_tech = "PRO_MICROGRAV_MAN" if not (tech_is_complete(ast_tech) or ast_tech in research_queue_list[:(1 + insert_idx)]): res = fo.issueEnqueueTechOrder(ast_tech, insert_idx) num_techs_accelerated += 1 msg = "Asteroids: plan to colonize an asteroid belt, so attempted to fast-track %s , got result %d" % ( ast_tech, res) if report_adjustments: chat_human(msg) else: print msg research_queue_list = get_research_queue_techs() elif tech_is_complete("SHP_ZORTRIUM_PLATE"): insert_idx = ( 1 + insert_idx ) if "LRN_FORCE_FIELD" not in research_queue_list else max( 1 + insert_idx, research_queue_list.index("LRN_FORCE_FIELD") - 1) for ast_tech in [ "SHP_ASTEROID_HULLS", "SHP_IMPROVED_ENGINE_COUPLINGS" ]: if not tech_is_complete( ast_tech ) and ast_tech not in research_queue_list[:insert_idx + 1]: res = fo.issueEnqueueTechOrder(ast_tech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 msg = "Asteroids: plan to colonize an asteroid belt, so attempted to fast-track %s , got result %d" % ( ast_tech, res) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() if ColonisationAI.got_gg and not tech_is_complete("PRO_ORBITAL_GEN"): fusion_idx = 0 if "PRO_FUSION_GEN" not in research_queue_list else ( 1 + research_queue_list.index("PRO_FUSION_GEN")) forcefields_idx = 0 if "LRN_FORCE_FIELD" not in research_queue_list else ( 1 + research_queue_list.index("LRN_FORCE_FIELD")) insert_idx = max( fusion_idx, forcefields_idx) if enemies_sighted else fusion_idx if "PRO_ORBITAL_GEN" not in research_queue_list[:insert_idx + 1]: res = fo.issueEnqueueTechOrder("PRO_ORBITAL_GEN", insert_idx) num_techs_accelerated += 1 msg = "GasGiant: plan to colonize a gas giant, so attempted to fast-track %s, got result %d" % ( "PRO_ORBITAL_GEN", res) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # assess if our empire has any non-lousy colonizers, & boost gro_xeno_gen if we don't if True: # just to help with cold-folding / organization if got_ggg_tech and got_sym_bio and ( not got_xeno_gen ) and foAI.foAIstate.aggression >= fo.aggression.cautious: most_adequate = 0 for specName in ColonisationAI.empire_colonizers: environs = {} this_spec = fo.getSpecies(specName) if not this_spec: continue for ptype in [ fo.planetType.swamp, fo.planetType.radiated, fo.planetType.toxic, fo.planetType.inferno, fo.planetType.barren, fo.planetType.tundra, fo.planetType.desert, fo.planetType.terran, fo.planetType.ocean, fo.planetType.asteroids ]: environ = this_spec.getPlanetEnvironment(ptype) environs.setdefault(environ, []).append(ptype) most_adequate = max( most_adequate, len(environs.get(fo.planetEnvironment.adequate, []))) if most_adequate == 0: insert_idx = num_techs_accelerated for xg_tech in ["GRO_XENO_GENETICS", "GRO_GENETIC_ENG"]: if xg_tech not in research_queue_list[:1 + num_techs_accelerated] and not tech_is_complete( xg_tech): res = fo.issueEnqueueTechOrder(xg_tech, insert_idx) num_techs_accelerated += 1 msg = "Empire has poor colonizers, so attempted to fast-track %s, got result %d" % ( xg_tech, res) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # check to accelerate distrib thought if True: # just to help with cold-folding / organization if not tech_is_complete("LRN_DISTRIB_THOUGHT"): got_telepathy = False for specName in ColonisationAI.empire_species: this_spec = fo.getSpecies(specName) if this_spec and ("TELEPATHIC" in list(this_spec.tags)): got_telepathy = True break if (foAI.foAIstate.aggression > fo.aggression.cautious) and (empire.population() > ([300, 100][got_telepathy])): insert_idx = num_techs_accelerated for dt_ech in [ "LRN_PHYS_BRAIN", "LRN_TRANSLING_THT", "LRN_PSIONICS", "LRN_DISTRIB_THOUGHT" ]: if dt_ech not in research_queue_list[:insert_idx + 2] and not tech_is_complete( dt_ech): res = fo.issueEnqueueTechOrder(dt_ech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 fmt_str = "Empire has a telepathic race, so attempted to fast-track %s (got result %d)" fmt_str += " with current target_RP %.1f and current pop %.1f, on turn %d" msg = fmt_str % (dt_ech, res, resource_production, empire.population(), fo.currentTurn()) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # check to accelerate quant net if False: # disabled for now, otherwise just to help with cold-folding / organization if (foAI.foAIstate.aggression > fo.aggression.cautious) and ( ColonisationAI.empire_status.get('researchers', 0) >= 40): if not tech_is_complete("LRN_QUANT_NET"): insert_idx = num_techs_accelerated # TODO determine min target slot if reenabling for qnTech in ["LRN_NDIM_SUBSPACE", "LRN_QUANT_NET"]: if qnTech not in research_queue_list[:insert_idx + 2] and not tech_is_complete( qnTech): res = fo.issueEnqueueTechOrder(qnTech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 msg = "Empire has many researchers, so attempted to fast-track %s (got result %d) on turn %d" % ( qnTech, res, fo.currentTurn()) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # if we own a blackhole, accelerate sing_gen and conc camp if True: # just to help with cold-folding / organization if (fo.currentTurn() > 50 and len(AIstate.empireStars.get(fo.starType.blackHole, [])) != 0 and foAI.foAIstate.aggression > fo.aggression.cautious and not tech_is_complete(AIDependencies.PRO_SINGULAR_GEN) and tech_is_complete(AIDependencies.PRO_SOL_ORB_GEN)): # sing_tech_list = [ "LRN_GRAVITONICS" , "PRO_SINGULAR_GEN"] # formerly also "CON_ARCH_PSYCH", "CON_CONC_CAMP", sing_gen_tech = fo.getTech(AIDependencies.PRO_SINGULAR_GEN) sing_tech_list = [ pre_req for pre_req in sing_gen_tech.recursivePrerequisites(empire_id) if not tech_is_complete(pre_req) ] sing_tech_list += [AIDependencies.PRO_SINGULAR_GEN] for singTech in sing_tech_list: if singTech not in research_queue_list[:num_techs_accelerated + 1]: res = fo.issueEnqueueTechOrder(singTech, num_techs_accelerated) num_techs_accelerated += 1 msg = "have a black hole star outpost/colony, so attempted to fast-track %s, got result %d" % ( singTech, res) print msg if report_adjustments: chat_human(msg) research_queue_list = get_research_queue_techs() # # if got deathray from Ruins, remove most prereqs from queue if True: # just to help with cold-folding / organization if tech_is_complete("SHP_WEAPON_4_1"): this_tech = fo.getTech("SHP_WEAPON_4_1") if this_tech: missing_prereqs = [ preReq for preReq in this_tech.recursivePrerequisites(empire_id) if preReq in research_queue_list ] if len(missing_prereqs ) > 2: # leave plasma 4 and 3 if up to them already for preReq in missing_prereqs: # sorted(missing_prereqs, reverse=True)[2:] if preReq in research_queue_list: fo.issueDequeueTechOrder(preReq) research_queue_list = get_research_queue_techs() if "SHP_WEAPON_4_2" in research_queue_list: # (should be) idx = research_queue_list.index("SHP_WEAPON_4_2") fo.issueEnqueueTechOrder("SHP_WEAPON_4_2", max(0, idx - 18))
def assess_protection_focus(pid): """Return True if planet should use Protection Focus""" this_planet = planetMap[pid] sys_status = foAI.foAIstate.systemStatus.get(this_planet.systemID, {}) threat_from_supply = ( 0.25 * foAI.foAIstate.empire_standard_enemy_rating * min(2, len(sys_status.get('enemies_nearly_supplied', [])))) print "Planet %s has regional+supply threat of %.1f" % ( 'P_%d<%s>' % (pid, this_planet.name), threat_from_supply) regional_threat = sys_status.get('regional_threat', 0) + threat_from_supply if not regional_threat: # no need for protection if currentFocus[pid] == PFocus: print "Advising dropping Protection Focus at %s due to no regional threat" % this_planet return False cur_prod_val = weighted_sum_output( [currentOutput[pid][IFocus], currentOutput[pid][RFocus]]) target_prod_val = max( map(weighted_sum_output, [newTargets[pid][IFocus], newTargets[pid][RFocus]])) prot_prod_val = weighted_sum_output(newTargets[pid][PFocus]) local_production_diff = 0.8 * cur_prod_val + 0.2 * target_prod_val - prot_prod_val fleet_threat = sys_status.get('fleetThreat', 0) # TODO: relax the below rejection once the overall determination of PFocus is better tuned if not fleet_threat and local_production_diff > 8: if currentFocus[pid] == PFocus: print "Advising dropping Protection Focus at %s due to excessive productivity loss" % this_planet return False local_p_defenses = sys_status.get('mydefenses', {}).get('overall', 0) # TODO have adjusted_p_defenses take other in-system planets into account adjusted_p_defenses = local_p_defenses * ( 1.0 if currentFocus[pid] != PFocus else 0.5) local_fleet_rating = sys_status.get('myFleetRating', 0) combined_local_defenses = sys_status.get('all_local_defenses', 0) my_neighbor_rating = sys_status.get('my_neighbor_rating', 0) neighbor_threat = sys_status.get('neighborThreat', 0) safety_factor = 1.2 if currentFocus[pid] == PFocus else 0.5 cur_shield = this_planet.currentMeterValue(fo.meterType.shield) max_shield = this_planet.currentMeterValue(fo.meterType.maxShield) cur_troops = this_planet.currentMeterValue(fo.meterType.troops) max_troops = this_planet.currentMeterValue(fo.meterType.maxTroops) cur_defense = this_planet.currentMeterValue(fo.meterType.defense) max_defense = this_planet.currentMeterValue(fo.meterType.maxDefense) def_meter_pairs = [(cur_troops, max_troops), (cur_shield, max_shield), (cur_defense, max_defense)] use_protection = True if (fleet_threat and # i.e., an enemy is sitting on us ( currentFocus[pid] != PFocus or # too late to start protection TODO: but maybe regen worth it # protection forcus only useful here if it maintains an elevated level all([ AIDependencies.PROT_FOCUS_MULTIPLIER * a < b for a, b in def_meter_pairs ]))): use_protection = False elif ((cur_shield < max_shield - 2 and not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and (cur_defense < max_defense - 2 and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and (cur_troops < max_troops - 2)): use_protection = False elif max(max_shield, max_troops, max_defense ) < 3: # joke defenses, don't bother with protection focus use_protection = False elif regional_threat and local_production_diff <= 2.0: pass # i.e., use_protection = True elif safety_factor * regional_threat <= local_fleet_rating: use_protection = False elif (safety_factor * regional_threat <= combined_local_defenses and (currentFocus[pid] != PFocus or (0.5 * safety_factor * regional_threat <= local_fleet_rating and fleet_threat == 0 and neighbor_threat < combined_local_defenses and local_production_diff > 5))): use_protection = False elif (regional_threat <= FleetUtilsAI.combine_ratings( local_fleet_rating, adjusted_p_defenses) and safety_factor * regional_threat <= FleetUtilsAI.combine_ratings_list( [my_neighbor_rating, local_fleet_rating, adjusted_p_defenses]) and local_production_diff > 5): use_protection = False if use_protection or currentFocus[pid] == PFocus: advisory_str = "Advising %sProtection Focus" % ["dropping ", "" ][use_protection] advisory_str += ( " for planet %s, with local_prod_diff of %.1f, comb. local defenses %.1f" + ", local fleet rating %.1f and regional threat %.1f, threat sources: %s" ) print advisory_str % (this_planet, local_production_diff, combined_local_defenses, local_fleet_rating, regional_threat, sys_status['regional_fleet_threats']) return use_protection
def evaluate_invasion_planet(planet_id, secure_fleet_missions, verbose=True): """Return the invasion value (score, troops) of a planet.""" detail = [] building_values = {"BLD_IMPERIAL_PALACE": 1000, "BLD_CULTURE_ARCHIVES": 1000, "BLD_AUTO_HISTORY_ANALYSER": 100, "BLD_SHIPYARD_BASE": 100, "BLD_SHIPYARD_ORG_ORB_INC": 200, "BLD_SHIPYARD_ORG_XENO_FAC": 200, "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200, "BLD_SHIPYARD_CON_NANOROBO": 300, "BLD_SHIPYARD_CON_GEOINT": 400, "BLD_SHIPYARD_CON_ADV_ENGINE": 1000, "BLD_SHIPYARD_AST": 300, "BLD_SHIPYARD_AST_REF": 1000, "BLD_SHIPYARD_ENRG_SOLAR": 1500, "BLD_INDUSTRY_CENTER": 500, "BLD_GAS_GIANT_GEN": 200, "BLD_SOL_ORB_GEN": 800, "BLD_BLACK_HOLE_POW_GEN": 2000, "BLD_ENCLAVE_VOID": 500, "BLD_NEUTRONIUM_EXTRACTOR": 2000, "BLD_NEUTRONIUM_SYNTH": 2000, "BLD_NEUTRONIUM_FORGE": 1000, "BLD_CONC_CAMP": 100, "BLD_BIOTERROR_PROJECTOR": 1000, "BLD_SHIPYARD_ENRG_COMP": 3000, } # TODO: add more factors, as used for colonization universe = fo.getUniverse() empire_id = fo.empireID() max_jumps = 8 planet = universe.getPlanet(planet_id) if planet is None: # TODO: exclude planets with stealth higher than empireDetection print "invasion AI couldn't access any info for planet id %d" % planet_id return [0, 0] sys_partial_vis_turn = universe.getVisibilityTurnsMap(planet.systemID, empire_id).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(planet_id, empire_id).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: print "invasion AI couldn't get current info on planet id %d (was stealthed at last sighting)" % planet_id # TODO: track detection strength, order new scouting when it goes up return [0, 0] # last time we had partial vis of the system, the planet was stealthed to us species_name = planet.speciesName species = fo.getSpecies(species_name) if not species or AIDependencies.TAG_DESTROYED_ON_CONQUEST in species.tags: # this call iterates over this Empire's available species with which it could colonize after an invasion planet_eval = ColonisationAI.assign_colonisation_values([planet_id], MissionType.INVASION, None, detail) pop_val = max(0.75 * planet_eval.get(planet_id, [0])[0], ColonisationAI.evaluate_planet(planet_id, MissionType.OUTPOST, None, detail)) else: pop_val = ColonisationAI.evaluate_planet(planet_id, MissionType.INVASION, species_name, detail) bld_tally = 0 for bldType in [universe.getBuilding(bldg).buildingTypeName for bldg in planet.buildingIDs]: bval = building_values.get(bldType, 50) bld_tally += bval detail.append("%s: %d" % (bldType, bval)) tech_tally = 0 for unlocked_tech in AIDependencies.SPECIES_TECH_UNLOCKS.get(species_name, []): if not tech_is_complete(unlocked_tech): rp_cost = fo.getTech(unlocked_tech).researchCost(empire_id) tech_tally += rp_cost * 4 detail.append("%s: %d" % (unlocked_tech, rp_cost * 4)) p_sys_id = planet.systemID capitol_id = PlanetUtilsAI.get_capital() least_jumps_path = [] clear_path = True if capitol_id: homeworld = universe.getPlanet(capitol_id) if homeworld: home_system_id = homeworld.systemID eval_system_id = planet.systemID if (home_system_id != INVALID_ID) and (eval_system_id != INVALID_ID): least_jumps_path = list(universe.leastJumpsPath(home_system_id, eval_system_id, empire_id)) max_jumps = len(least_jumps_path) system_status = foAI.foAIstate.systemStatus.get(p_sys_id, {}) system_fleet_treat = system_status.get('fleetThreat', 1000) system_monster_threat = system_status.get('monsterThreat', 0) sys_total_threat = system_fleet_treat + system_monster_threat + system_status.get('planetThreat', 0) max_path_threat = system_fleet_treat mil_ship_rating = MilitaryAI.cur_best_mil_ship_rating() for path_sys_id in least_jumps_path: path_leg_status = foAI.foAIstate.systemStatus.get(path_sys_id, {}) path_leg_threat = path_leg_status.get('fleetThreat', 1000) + path_leg_status.get('monsterThreat', 0) if path_leg_threat > 0.5 * mil_ship_rating: clear_path = False if path_leg_threat > max_path_threat: max_path_threat = path_leg_threat pop = planet.currentMeterValue(fo.meterType.population) target_pop = planet.currentMeterValue(fo.meterType.targetPopulation) troops = planet.currentMeterValue(fo.meterType.troops) max_troops = planet.currentMeterValue(fo.meterType.maxTroops) # TODO: refactor troop determination into function for use in mid-mission updates and also consider defender techs max_troops += AIDependencies.TROOPS_PER_POP * (target_pop - pop) this_system = universe.getSystem(p_sys_id) secure_targets = [p_sys_id] + list(this_system.planetIDs) system_secured = False for mission in secure_fleet_missions: if system_secured: break secure_fleet_id = mission.fleet.id s_fleet = universe.getFleet(secure_fleet_id) if not s_fleet or s_fleet.systemID != p_sys_id: continue if mission.type == MissionType.SECURE: target_obj = mission.target.get_object() if target_obj is not None and target_obj.id in secure_targets: system_secured = True break system_secured = system_secured and system_status.get('myFleetRating', 0) if verbose: print ("Invasion eval of %s\n" " - maxShields: %.1f\n" " - sysFleetThreat: %.1f\n" " - sysMonsterThreat: %.1f") % ( planet, planet.currentMeterValue(fo.meterType.maxShield), system_fleet_treat, system_monster_threat) supply_val = 0 enemy_val = 0 if planet.owner != -1: # value in taking this away from an enemy enemy_val = 20 * (planet.currentMeterValue(fo.meterType.targetIndustry) + 2*planet.currentMeterValue(fo.meterType.targetResearch)) if p_sys_id in ColonisationAI.annexable_system_ids: # TODO: extend to rings supply_val = 100 elif p_sys_id in ColonisationAI.annexable_ring1: supply_val = 200 elif p_sys_id in ColonisationAI.annexable_ring2: supply_val = 300 elif p_sys_id in ColonisationAI.annexable_ring3: supply_val = 400 if max_path_threat > 0.5 * mil_ship_rating: if max_path_threat < 3 * mil_ship_rating: supply_val *= 0.5 else: supply_val *= 0.2 threat_factor = min(1, 0.2*MilitaryAI.get_tot_mil_rating()/(sys_total_threat+0.001))**2 # devalue invasions that would require too much military force design_id, _, locs = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_INVASION) if not locs or not universe.getPlanet(locs[0]): # We are in trouble anyway, so just calculate whatever approximation... build_time = 4 planned_troops = troops if system_secured else min(troops + max_jumps + build_time, max_troops) planned_troops += .01 # we must attack with more troops than there are defenders troop_cost = math.ceil((planned_troops+_TROOPS_SAFETY_MARGIN) / 6.0) * 20 * FleetUtilsAI.get_fleet_upkeep() else: loc = locs[0] species_here = universe.getPlanet(loc).speciesName design = fo.getShipDesign(design_id) cost_per_ship = design.productionCost(empire_id, loc) build_time = design.productionTime(empire_id, loc) troops_per_ship = CombatRatingsAI.weight_attack_troops(design.troopCapacity, CombatRatingsAI.get_species_troops_grade(species_here)) planned_troops = troops if system_secured else min(troops + max_jumps + build_time, max_troops) planned_troops += .01 # we must attack with more troops than there are defenders ships_needed = math.ceil((planned_troops+_TROOPS_SAFETY_MARGIN) / float(troops_per_ship)) troop_cost = ships_needed * cost_per_ship # fleet upkeep is already included in query from server # apply some bias to expensive operations normalized_cost = float(troop_cost) / max(fo.getEmpire().productionPoints, 1) normalized_cost = max(1, normalized_cost) cost_score = (normalized_cost**2 / 50.0) * troop_cost base_score = pop_val + supply_val + bld_tally + tech_tally + enemy_val - cost_score planet_score = retaliation_risk_factor(planet.owner) * threat_factor * max(0, base_score) if clear_path: planet_score *= 1.5 if verbose: print (' - planet score: %.2f\n' ' - troop score: %.2f\n' ' - projected troop cost: %.1f\n' ' - threat factor: %s\n' ' - planet detail: %s\n' ' - popval: %.1f\n' ' - supplyval: %.1f\n' ' - bldval: %s\n' ' - enemyval: %s') % (planet_score, planned_troops, troop_cost, threat_factor, detail, pop_val, supply_val, bld_tally, enemy_val) return [planet_score, planned_troops]
def evaluate_invasion_planet(planet_id, 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 assess_protection_focus(pinfo, priority): """Return True if planet should use Protection Focus.""" this_planet = pinfo.planet # this is unrelated to military threats stability_bonus = (pinfo.current_focus == PROTECTION ) * fo.getNamedValue("PROTECION_FOCUS_STABILITY_BONUS") # industry and research produce nothing below 0 threshold = -1 * (pinfo.current_focus not in (INDUSTRY, RESEARCH)) # Negative IP lowers stability. Trying to counter this by setting planets to Protection just makes it worse! ip = fo.getEmpire().resourceAvailable(fo.resourceType.influence) if ip >= 0 and this_planet.currentMeterValue( fo.meterType.targetHappiness) < threshold + stability_bonus: debug("Advising Protection Focus at %s to avoid rebellion", this_planet) return True aistate = get_aistate() sys_status = aistate.systemStatus.get(this_planet.systemID, {}) threat_from_supply = ( 0.25 * aistate.empire_standard_enemy_rating * min(2, len(sys_status.get("enemies_nearly_supplied", [])))) debug("%s has regional+supply threat of %.1f", this_planet, threat_from_supply) regional_threat = sys_status.get("regional_threat", 0) + threat_from_supply if not regional_threat: # no need for protection if pinfo.current_focus == PROTECTION: debug( "Advising dropping Protection Focus at %s due to no regional threat", this_planet) return False cur_prod_val = weighted_sum_output((pinfo.current_output, priority)) target_prod_val = max( map( weighted_sum_output, [ (pinfo.possible_output[INDUSTRY], priority), (pinfo.possible_output[RESEARCH], priority), (pinfo.possible_output[INFLUENCE], priority), ], )) prot_prod_val = weighted_sum_output( (pinfo.possible_output[PROTECTION], priority)) local_production_diff = 0.5 * cur_prod_val + 0.5 * target_prod_val - prot_prod_val fleet_threat = sys_status.get("fleetThreat", 0) # TODO: relax the below rejection once the overall determination of PFocus is better tuned # priorities have a magnitude of 50 if not fleet_threat and local_production_diff > 200: if pinfo.current_focus == PROTECTION: debug( "Advising dropping Protection Focus at %s due to excessive productivity loss", this_planet) return False local_p_defenses = sys_status.get("mydefenses", {}).get("overall", 0) # TODO have adjusted_p_defenses take other in-system planets into account adjusted_p_defenses = local_p_defenses * ( 1.0 if pinfo.current_focus != PROTECTION else 0.5) local_fleet_rating = sys_status.get("myFleetRating", 0) combined_local_defenses = sys_status.get("all_local_defenses", 0) my_neighbor_rating = sys_status.get("my_neighbor_rating", 0) neighbor_threat = sys_status.get("neighborThreat", 0) safety_factor = 1.2 if pinfo.current_focus == PROTECTION else 0.5 cur_shield = this_planet.initialMeterValue(fo.meterType.shield) max_shield = this_planet.initialMeterValue(fo.meterType.maxShield) cur_troops = this_planet.initialMeterValue(fo.meterType.troops) max_troops = this_planet.initialMeterValue(fo.meterType.maxTroops) cur_defense = this_planet.initialMeterValue(fo.meterType.defense) max_defense = this_planet.initialMeterValue(fo.meterType.maxDefense) def_meter_pairs = [(cur_troops, max_troops), (cur_shield, max_shield), (cur_defense, max_defense)] use_protection = True reason = "" if fleet_threat and ( # i.e., an enemy is sitting on us pinfo.current_focus != PROTECTION or # too late to start protection TODO: but maybe regen worth it # protection focus only useful here if it maintains an elevated level all([ AIDependencies.PROT_FOCUS_MULTIPLIER * a <= b for a, b in def_meter_pairs ])): use_protection = False reason = "A" elif ((pinfo.current_focus != PROTECTION and cur_shield < max_shield - 2 and not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and (cur_defense < max_defense - 2 and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and (cur_troops < max_troops - 2)): use_protection = False reason = "B1" elif ( (pinfo.current_focus == PROTECTION and cur_shield * AIDependencies.PROT_FOCUS_MULTIPLIER < max_shield - 2 and not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and (cur_defense * AIDependencies.PROT_FOCUS_MULTIPLIER < max_defense - 2 and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and (cur_troops * AIDependencies.PROT_FOCUS_MULTIPLIER < max_troops - 2)): use_protection = False reason = "B2" elif max(max_shield, max_troops, max_defense) < 3: # joke defenses, don't bother with protection focus use_protection = False reason = "C" elif regional_threat and local_production_diff <= 2.0: use_protection = True reason = "D" elif safety_factor * regional_threat <= local_fleet_rating: use_protection = False reason = "E" elif safety_factor * regional_threat <= combined_local_defenses and ( pinfo.current_focus != PROTECTION or (0.5 * safety_factor * regional_threat <= local_fleet_rating and fleet_threat == 0 and neighbor_threat < combined_local_defenses and local_production_diff > 5)): use_protection = False reason = "F" elif (regional_threat <= combine_ratings(local_fleet_rating, adjusted_p_defenses) and safety_factor * regional_threat <= combine_ratings( my_neighbor_rating, local_fleet_rating, adjusted_p_defenses) and local_production_diff > 5): use_protection = False reason = "G" if use_protection or pinfo.current_focus == PROTECTION: debug( "Advising %sProtection Focus (reason %s) for planet %s, with local_prod_diff of %.1f, comb. local" " defenses %.1f, local fleet rating %.1f and regional threat %.1f, threat sources: %s", ["dropping ", ""][use_protection], reason, this_planet, local_production_diff, combined_local_defenses, local_fleet_rating, regional_threat, sys_status["regional_fleet_threats"], ) return use_protection
def get_completed_techs(): """get completed and available for use techs""" return [tech for tech in fo.techs() if tech_is_complete(tech)]
def set_planet_industry_and_research_foci(focus_manager, priority_ratio): """Adjust planet's industry versus research focus while targeting the given ratio and avoiding penalties from changing focus.""" print "\n-----------------------------------------" print "Making Planet Focus Change Determinations\n" ratios = [] # for each planet, calculate RP:PP value ratio at which industry focus and # research focus would have the same total value, & sort by that include a # bias to slightly discourage changing foci target_pp = 0.001 target_rp = 0.001 resource_timer.start("Loop") # loop has_force = tech_is_complete("CON_FRC_ENRG_STRC") # cumulative all industry focus cumulative_pp, cumulative_rp = 0, 0 # Handle presets which only have possible output for preset focus for pid, pinfo in focus_manager.baked_planet_info.items(): future_pp, future_rp = pinfo.possible_output[pinfo.future_focus] target_pp += future_pp target_rp += future_rp cumulative_pp += future_pp cumulative_rp += future_rp # tally max Industry for pid, pinfo in focus_manager.raw_planet_info.items(): i_pp, i_rp = pinfo.possible_output[INDUSTRY] cumulative_pp += i_pp cumulative_rp += i_rp if RESEARCH not in pinfo.planet.availableFoci: if focus_manager.bake_future_focus(pid, INDUSTRY, False): target_pp += i_pp target_rp += i_rp # smallest possible ratio of research to industry with an all industry focus maxi_ratio = cumulative_rp / max(0.01, cumulative_pp) aistate = get_aistate() for adj_round in [1, 2, 3, 4]: for pid, pinfo in focus_manager.raw_planet_info.items(): ii, tr = pinfo.possible_output[INDUSTRY] ri, rr = pinfo.possible_output[RESEARCH] ci, cr = pinfo.current_output research_penalty = AIDependencies.FOCUS_CHANGE_PENALTY if (pinfo.current_focus != RESEARCH) else 0 # calculate factor F at which ii + F * tr == ri + F * rr =====> F = ( ii-ri ) / (rr-tr) factor = (ii - ri) / max(0.01, rr - tr) planet = pinfo.planet if adj_round == 1: # take research at planets that can not use industry focus if INDUSTRY not in pinfo.planet.availableFoci: target_pp += ri target_rp += rr focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 2: # take research at planets with very cheap research if (maxi_ratio < priority_ratio) and (target_rp < priority_ratio * cumulative_pp) and (factor <= 1.0): target_pp += ri target_rp += rr focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 3: # take research at planets where can do reasonable balance # if this planet in range where temporary Research focus ("research dithering") can get some additional # RP at a good PP cost, and still need some RP, then consider doing it. # Won't really work if AI has researched Force Energy Structures (meters fall too fast) # TODO: add similar decision points by which research-rich planets # might possibly choose to dither for industry points if any((has_force, not aistate.character.may_dither_focus_to_gain_research(), target_rp >= priority_ratio * cumulative_pp)): continue pop = planet.initialMeterValue(fo.meterType.population) t_pop = planet.initialMeterValue(fo.meterType.targetPopulation) # let pop stabilize before trying to dither; the calculations that determine whether dithering will be # advantageous assume a stable population, so a larger gap means a less reliable decision MAX_DITHER_POP_GAP = 5 # some smallish number if pop < t_pop - MAX_DITHER_POP_GAP: continue # if gap between R-focus and I-focus target research levels is too narrow, don't research dither. # A gap of 1 would provide a single point of RP, at a cost of 3 PP; a gap of 2 the cost is 2.7 PP/RP; # a gap of 3 at 2.1 PP/RP; a gap of 4, 1.8 PP/RP; a gap of 5, 1.7 PP/RP. The bigger the gap, the # better; a gap of 10 would provide RP at a cost of 1.3 PP/RP (or even less if the target PP gap # were smaller). MIN_DITHER_TARGET_RESEARCH_GAP = 3 if (rr - tr) < MIN_DITHER_TARGET_RESEARCH_GAP: continue # could double check that planet even has Industry Focus available, but no harm even if not # So at this point we have determined the planet has research targets compatible with employing focus # dither. The research focus phase will last until current research reaches the Research-Focus # research target, determined by the 'research_capped' indicator, at which point the focus is # changed to Industry (currently left to be handled by standard focus code later). The research_capped # indicator, though, is a spot indicator whose value under normal dither operation would revert on the # next turn, so we need another indicator to maintain the focus change until the Industry meter has # recovered to its max target level; the indicator to keep the research phase turned off needs to have # some type of hysteresis component or otherwise be sensitive to the direction of meter change; in the # indicator below this is accomplished primarily by comparing a difference of changes on both the # research and industry side, the 'research_penalty' adjustment in the industry_recovery_phase # calculation prevents the indicator from stopping recovery mode one turn too early. research_capped = (rr - cr) <= 0.5 industry_recovery_phase = (ii - ci) - (cr - tr) > AIDependencies.FOCUS_CHANGE_PENALTY - research_penalty if not (research_capped or industry_recovery_phase): target_pp += ci - 1 - research_penalty target_rp += cr + 1 focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 4: # assume default IFocus target_pp += ii # icurTargets initially calculated by Industry focus, which will be our default focus target_rp += tr ratios.append((factor, pid, pinfo)) ratios.sort() printed_header = False got_algo = tech_is_complete("LRN_ALGO_ELEGANCE") for ratio, pid, pinfo in ratios: if priority_ratio < (target_rp / (target_pp + 0.0001)): # we have enough RP if ratio < 1.1 and aistate.character.may_research_heavily(): # but wait, RP is still super cheap relative to PP, maybe will take more RP if priority_ratio < 1.5 * (target_rp / (target_pp + 0.0001)): # yeah, really a glut of RP, stop taking RP break else: # RP not super cheap & we have enough, stop taking it break ii, tr = pinfo.possible_output[INDUSTRY] ri, rr = pinfo.possible_output[RESEARCH] if ((ratio > 2.0 and target_pp < 15 and got_algo) or (ratio > 2.5 and target_pp < 25 and ii > 5 and got_algo) or (ratio > 3.0 and target_pp < 40 and ii > 5 and got_algo) or (ratio > 4.0 and target_pp < 100 and ii > 10) or ((target_rp + rr - tr) / max(0.001, target_pp - ii + ri) > 2 * priority_ratio)): # we already have algo elegance and more RP would be too expensive, or overkill if not printed_header: printed_header = True print "Rejecting further Research Focus choices as too expensive:" print "%34s|%20s|%15s |%15s|%15s |%15s |%15s" % (" Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " rejectedFocus ", " rejected target RP/PP ", "rejected RP-PP EQF") old_focus = pinfo.current_focus c_pp, c_rp = pinfo.current_output ot_pp, ot_rp = pinfo.possible_output[old_focus] nt_pp, nt_rp = pinfo.possible_output[RESEARCH] print ("pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f" " | cF: %8s | nF: %8s | cT: %5.1f / %5.1f | %.2f" % ( pid, pinfo.planet.name, c_rp, c_pp, ot_rp, ot_pp, _focus_names.get(old_focus, 'unknown'), _focus_names[RESEARCH], nt_rp, nt_pp, ratio)) # RP is getting too expensive, but might be willing to still allocate from a planet with less PP to lose continue focus_manager.bake_future_focus(pid, RESEARCH, False) target_rp += (rr - tr) target_pp -= (ii - ri) # Any planet still raw is set to industry for pid in focus_manager.raw_planet_info.keys(): focus_manager.bake_future_focus(pid, INDUSTRY, False)
def _calculate_research_priority(): """Calculates the AI empire's demand for research.""" universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID current_turn = fo.currentTurn() enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) recent_enemies = [x for x in enemies_sighted if x > current_turn - 8] industry_priority = foAI.foAIstate.get_priority(PriorityType.RESOURCE_PRODUCTION) got_algo = tech_is_complete(AIDependencies.LRN_ALGO_ELEGANCE) got_quant = tech_is_complete(AIDependencies.LRN_QUANT_NET) research_queue_list = ResearchAI.get_research_queue_techs() orb_gen_tech = AIDependencies.PRO_ORBITAL_GEN got_orb_gen = tech_is_complete(orb_gen_tech) mgrav_prod_tech = AIDependencies.PRO_MICROGRAV_MAN got_mgrav_prod = tech_is_complete(mgrav_prod_tech) # got_solar_gen = tech_is_complete(AIDependencies.PRO_SOL_ORB_GEN) milestone_techs = ["PRO_SENTIENT_AUTOMATION", "LRN_DISTRIB_THOUGHT", "LRN_QUANT_NET", "SHP_WEAPON_2_4", "SHP_WEAPON_3_2", "SHP_WEAPON_4_2"] milestones_done = [mstone for mstone in milestone_techs if tech_is_complete(mstone)] print "Research Milestones accomplished at turn %d: %s" % (current_turn, milestones_done) total_pp = empire.productionPoints total_rp = empire.resourceProduction(fo.resourceType.research) industry_surge = (foAI.foAIstate.character.may_surge_industry(total_pp, total_rp) and (((orb_gen_tech in research_queue_list[:2] or got_orb_gen) and state.have_gas_giant) or ((mgrav_prod_tech in research_queue_list[:2] or got_mgrav_prod) and state.have_asteroids)) and (not (len(AIstate.popCtrIDs) >= 12))) # get current industry production & Target owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) planets = map(universe.getPlanet, owned_planet_ids) target_rp = sum(map(lambda x: x.currentMeterValue(fo.meterType.targetResearch), planets)) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {}) style_index = foAI.foAIstate.character.preferred_research_cutoff([0, 1]) if foAI.foAIstate.character.may_maximize_research(): style_index += 1 cutoff_sets = [[25, 45, 70, 110], [30, 45, 70, 150], [25, 40, 80, 160]] cutoffs = cutoff_sets[style_index] settings = [[1.3, .7, .5, .4, .35], [1.4, 0.8, 0.6, 0.5, 0.35], [1.4, 0.8, 0.6, 0.5, 0.4]][style_index] if (current_turn < cutoffs[0]) or (not got_algo) or ((style_index == 0) and not got_orb_gen and (current_turn < cutoffs[1])): research_priority = settings[0] * industry_priority # high research at beginning of game to get easy gro tech and to get research booster Algotrithmic Elegance elif (not got_orb_gen) or (current_turn < cutoffs[1]): research_priority = settings[1] * industry_priority # med-high research elif current_turn < cutoffs[2]: research_priority = settings[2] * industry_priority # med-high industry elif current_turn < cutoffs[3]: research_priority = settings[3] * industry_priority # med-high industry else: research_queue = list(empire.researchQueue) research_priority = settings[4] * industry_priority # high industry , low research if len(research_queue) == 0: research_priority = 0 # done with research elif len(research_queue) < 5 and research_queue[-1].allocation > 0: research_priority = len(research_queue) * 0.01 * industry_priority # barely not done with research elif len(research_queue) < 10 and research_queue[-1].allocation > 0: research_priority = (4 + 2 * len(research_queue)) * 0.01 * industry_priority # almost done with research elif len(research_queue) < 20 and research_queue[int(len(research_queue) / 2)].allocation > 0: research_priority *= 0.7 # closing in on end of research if industry_surge: if galaxy_is_sparse and not any(enemies_sighted): research_priority *= 0.5 else: research_priority *= 0.8 if ((tech_is_complete("SHP_WEAPON_2_4") or tech_is_complete("SHP_WEAPON_4_1")) and tech_is_complete(AIDependencies.PROD_AUTO_NAME)): # industry_factor = [ [0.25, 0.2], [0.3, 0.25], [0.3, 0.25] ][style_index ] # researchPriority = min(researchPriority, industry_factor[got_solar_gen]*industryPriority) research_priority *= 0.9 if got_quant: research_priority = min(research_priority + 0.1 * industry_priority, research_priority * 1.3) research_priority = int(research_priority) print "Research Production (current/target) : ( %.1f / %.1f )" % (total_rp, target_rp) print "Priority for Research: %d (new target ~ %d RP)" % (research_priority, total_pp * research_priority / industry_priority) if len(enemies_sighted) < (2 + current_turn/20.0): # TODO: adjust for colonisation priority research_priority *= 1.2 if (current_turn > 20) and (len(recent_enemies) > 3): research_priority *= 0.8 return research_priority
def get_supply_tech_range(): return sum(_range for _tech, _range in AIDependencies.supply_range_techs.items() if tech_is_complete(_tech))
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 assess_protection_focus(pinfo): """Return True if planet should use Protection Focus.""" this_planet = pinfo.planet aistate = get_aistate() sys_status = aistate.systemStatus.get(this_planet.systemID, {}) threat_from_supply = ( 0.25 * aistate.empire_standard_enemy_rating * min(2, len(sys_status.get('enemies_nearly_supplied', [])))) debug("%s has regional+supply threat of %.1f", this_planet, threat_from_supply) regional_threat = sys_status.get('regional_threat', 0) + threat_from_supply if not regional_threat: # no need for protection if pinfo.current_focus == PROTECTION: debug( "Advising dropping Protection Focus at %s due to no regional threat", this_planet) return False cur_prod_val = weighted_sum_output(pinfo.current_output) target_prod_val = max( map(weighted_sum_output, [pinfo.possible_output[INDUSTRY], pinfo.possible_output[RESEARCH] ])) prot_prod_val = weighted_sum_output(pinfo.possible_output[PROTECTION]) local_production_diff = 0.8 * cur_prod_val + 0.2 * target_prod_val - prot_prod_val fleet_threat = sys_status.get('fleetThreat', 0) # TODO: relax the below rejection once the overall determination of PFocus is better tuned if not fleet_threat and local_production_diff > 8: if pinfo.current_focus == PROTECTION: debug( "Advising dropping Protection Focus at %s due to excessive productivity loss", this_planet) return False local_p_defenses = sys_status.get('mydefenses', {}).get('overall', 0) # TODO have adjusted_p_defenses take other in-system planets into account adjusted_p_defenses = local_p_defenses * ( 1.0 if pinfo.current_focus != PROTECTION else 0.5) local_fleet_rating = sys_status.get('myFleetRating', 0) combined_local_defenses = sys_status.get('all_local_defenses', 0) my_neighbor_rating = sys_status.get('my_neighbor_rating', 0) neighbor_threat = sys_status.get('neighborThreat', 0) safety_factor = 1.2 if pinfo.current_focus == PROTECTION else 0.5 cur_shield = this_planet.initialMeterValue(fo.meterType.shield) max_shield = this_planet.initialMeterValue(fo.meterType.maxShield) cur_troops = this_planet.initialMeterValue(fo.meterType.troops) max_troops = this_planet.initialMeterValue(fo.meterType.maxTroops) cur_defense = this_planet.initialMeterValue(fo.meterType.defense) max_defense = this_planet.initialMeterValue(fo.meterType.maxDefense) def_meter_pairs = [(cur_troops, max_troops), (cur_shield, max_shield), (cur_defense, max_defense)] use_protection = True reason = "" if (fleet_threat and # i.e., an enemy is sitting on us ( pinfo.current_focus != PROTECTION or # too late to start protection TODO: but maybe regen worth it # protection focus only useful here if it maintains an elevated level all([ AIDependencies.PROT_FOCUS_MULTIPLIER * a <= b for a, b in def_meter_pairs ]))): use_protection = False reason = "A" elif ((pinfo.current_focus != PROTECTION and cur_shield < max_shield - 2 and not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and (cur_defense < max_defense - 2 and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and (cur_troops < max_troops - 2)): use_protection = False reason = "B1" elif ( (pinfo.current_focus == PROTECTION and cur_shield * AIDependencies.PROT_FOCUS_MULTIPLIER < max_shield - 2 and not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and (cur_defense * AIDependencies.PROT_FOCUS_MULTIPLIER < max_defense - 2 and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and (cur_troops * AIDependencies.PROT_FOCUS_MULTIPLIER < max_troops - 2)): use_protection = False reason = "B2" elif max(max_shield, max_troops, max_defense) < 3: # joke defenses, don't bother with protection focus use_protection = False reason = "C" elif regional_threat and local_production_diff <= 2.0: use_protection = True reason = "D" elif safety_factor * regional_threat <= local_fleet_rating: use_protection = False reason = "E" elif (safety_factor * regional_threat <= combined_local_defenses and (pinfo.current_focus != PROTECTION or (0.5 * safety_factor * regional_threat <= local_fleet_rating and fleet_threat == 0 and neighbor_threat < combined_local_defenses and local_production_diff > 5))): use_protection = False reason = "F" elif (regional_threat <= CombatRatingsAI.combine_ratings( local_fleet_rating, adjusted_p_defenses) and safety_factor * regional_threat <= CombatRatingsAI.combine_ratings_list( [my_neighbor_rating, local_fleet_rating, adjusted_p_defenses]) and local_production_diff > 5): use_protection = False reason = "G" if use_protection or pinfo.current_focus == PROTECTION: debug( "Advising %sProtection Focus (reason %s) for planet %s, with local_prod_diff of %.1f, comb. local" " defenses %.1f, local fleet rating %.1f and regional threat %.1f, threat sources: %s", ["dropping ", ""][use_protection], reason, this_planet, local_production_diff, combined_local_defenses, local_fleet_rating, regional_threat, sys_status['regional_fleet_threats']) return use_protection
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 calc_max_pop(planet, species, detail): planet_size = planet.habitableSize planet_env = species.getPlanetEnvironment(planet.type) if planet_env == fo.planetEnvironment.uninhabitable: detail.append("Uninhabitable.") return 0 for bldg_id in planet.buildingIDs: building = fo.getUniverse().getBuilding(bldg_id) if not building: continue if building.buildingTypeName == "BLD_GATEWAY_VOID": detail.append("Gateway to the void: Uninhabitable.") return 0 tag_list = list(species.tags) if species else [] pop_tag_mod = AIDependencies.SPECIES_POPULATION_MODIFIER.get( get_species_tag_grade(species.name, Tags.POPULATION), 1.0) if planet.type == fo.planetType.gasGiant and "GASEOUS" in tag_list: gaseous_adjustment = AIDependencies.GASEOUS_POP_FACTOR detail.append("GASEOUS adjustment: %.2f" % gaseous_adjustment) else: gaseous_adjustment = 1.0 if planet.type != fo.planetType.asteroids and "MINDLESS_SPECIES" in tag_list: mindless_adjustment = AIDependencies.MINDLESS_POP_FACTOR detail.append("MINDLESS adjustment: %.2f" % mindless_adjustment) else: mindless_adjustment = 1.0 planet_specials = set(planet.specials) base_pop_modified_by_species = 0 base_pop_not_modified_by_species = 0 pop_const_mod = 0 # first, account for the environment environment_mod = POP_SIZE_MOD_MAP_MODIFIED_BY_SPECIES[ "environment_bonus"][planet_env] detail.append("Base environment: %d" % environment_mod) base_pop_modified_by_species += environment_mod # find all applicable modifiers for tech in POP_SIZE_MOD_MAP_MODIFIED_BY_SPECIES: if tech != "environment_bonus" and tech_is_complete(tech): base_pop_modified_by_species += POP_SIZE_MOD_MAP_MODIFIED_BY_SPECIES[ tech][planet_env] detail.append( "%s_PSM_early(%d)" % (tech, POP_SIZE_MOD_MAP_MODIFIED_BY_SPECIES[tech][planet_env])) for tech in POP_SIZE_MOD_MAP_NOT_MODIFIED_BY_SPECIES: if tech_is_complete(tech): base_pop_not_modified_by_species += POP_SIZE_MOD_MAP_NOT_MODIFIED_BY_SPECIES[ tech][planet_env] detail.append( "%s_PSM_late(%d)" % (tech, POP_SIZE_MOD_MAP_NOT_MODIFIED_BY_SPECIES[tech][planet_env])) for tech in POP_CONST_MOD_MAP: if tech_is_complete(tech): pop_const_mod += POP_CONST_MOD_MAP[tech][planet_env] detail.append("%s_PCM(%d)" % (tech, POP_CONST_MOD_MAP[tech][planet_env])) for _special in planet_specials.intersection( AIDependencies.POP_FIXED_MOD_SPECIALS): this_mod = AIDependencies.POP_FIXED_MOD_SPECIALS[_special] detail.append("%s_PCM(%d)" % (_special, this_mod)) pop_const_mod += this_mod for _special in planet_specials.intersection( AIDependencies.POP_PROPORTIONAL_MOD_SPECIALS): this_mod = AIDependencies.POP_PROPORTIONAL_MOD_SPECIALS[_special] detail.append("%s (maxPop%+.1f)" % (_special, this_mod)) base_pop_not_modified_by_species += this_mod # exobots and sly can't ever get to good environ so no gaia bonus, for others we'll assume they'll get there if "GAIA_SPECIAL" in planet_specials and species.name != "SP_EXOBOT" and species.name != "SP_SLY": base_pop_not_modified_by_species += 3 detail.append("Gaia_PSM_late(3)") if "SELF_SUSTAINING" in tag_list: if planet_env == fo.planetEnvironment.good: base_pop_not_modified_by_species += 3 detail.append("SelfSustaining_PSM_late(3)") applicable_boosts = set() for this_tag in [ tag for tag in tag_list if tag in AIDependencies.metabolismBoostMap ]: metab_boosts = AIDependencies.metabolismBoostMap.get(this_tag, []) for key in active_growth_specials.keys(): if len(active_growth_specials[key]) > 0 and key in metab_boosts: applicable_boosts.add(key) detail.append("%s boost active" % key) for boost in metab_boosts: if boost in planet_specials: applicable_boosts.add(boost) detail.append("%s boost present" % boost) n_boosts = len(applicable_boosts) if n_boosts: base_pop_not_modified_by_species += n_boosts detail.append("boosts_PSM(%d from %s)" % (n_boosts, applicable_boosts)) if planet.id in species.homeworlds: detail.append("Homeworld (2)") base_pop_not_modified_by_species += 2 if AIDependencies.TAG_LIGHT_SENSITIVE in tag_list: star_type = fo.getUniverse().getSystem(planet.systemID).starType star_pop_mod = AIDependencies.POP_MOD_LIGHTSENSITIVE_STAR_MAP.get( star_type, 0) base_pop_not_modified_by_species += star_pop_mod detail.append("Lightsensitive Star Bonus_PSM_late(%.1f)" % star_pop_mod) def max_pop_size(): species_effect = (pop_tag_mod - 1) * abs(base_pop_modified_by_species) gaseous_effect = (gaseous_adjustment - 1) * abs(base_pop_modified_by_species) mindless_effect = (mindless_adjustment - 1) * abs(base_pop_modified_by_species) base_pop = (base_pop_not_modified_by_species + base_pop_modified_by_species + species_effect + gaseous_effect + mindless_effect) return planet_size * base_pop + pop_const_mod target_pop = max_pop_size() if "PHOTOTROPHIC" in tag_list and target_pop > 0: star_type = fo.getUniverse().getSystem(planet.systemID).starType star_pop_mod = AIDependencies.POP_MOD_PHOTOTROPHIC_STAR_MAP.get( star_type, 0) base_pop_not_modified_by_species += star_pop_mod detail.append("Phototropic Star Bonus_PSM_late(%0.1f)" % star_pop_mod) target_pop = max_pop_size() detail.append( "max_pop = base + size*[psm_early + species_mod*abs(psm_early) + psm_late]" ) detail.append(" = %.2f + %d * [%.2f + %.2f*abs(%.2f) + %.2f]" % ( pop_const_mod, planet_size, base_pop_modified_by_species, (pop_tag_mod + gaseous_adjustment - 2), base_pop_modified_by_species, base_pop_not_modified_by_species, )) detail.append(" = %.2f" % target_pop) return target_pop
def generate_classic_research_orders(): """generate research orders""" empire = fo.getEmpire() empire_id = empire.empireID aistate = get_aistate() enemies_sighted = aistate.misc.get("enemies_sighted", {}) galaxy_is_sparse = ColonisationAI.galaxy_is_sparse() resource_production = empire.resourceProduction(fo.resourceType.research) completed_techs = sorted(list(get_completed_techs())) _print_reserch_order_header(resource_production, completed_techs) # # report techs currently at head of research queue # research_queue = empire.researchQueue research_queue_list = get_research_queue_techs() total_rp = empire.resourceProduction(fo.resourceType.research) tech_turns_left = {} if research_queue_list: debug("Techs currently at head of Research Queue:") for element in list(research_queue)[:10]: tech_turns_left[element.tech] = element.turnsLeft this_tech = fo.getTech(element.tech) if not this_tech: warning("Can't retrieve tech ", element.tech) continue missing_prereqs = [ preReq for preReq in this_tech.recursivePrerequisites(empire_id) if preReq not in completed_techs ] # unlocked_items = [(uli.name, uli.type) for uli in this_tech.unlocked_items] unlocked_items = [uli.name for uli in this_tech.unlockedItems] if not missing_prereqs: debug( " %25s allocated %6.2f RP (%d turns left)-- unlockable items: %s ", element.tech, element.allocation, tech_turns_left[element.tech], unlocked_items, ) else: debug( " %25s allocated %6.2f RP -- missing preReqs: %s -- unlockable items: %s ", element.tech, element.allocation, missing_prereqs, unlocked_items, ) debug("") # # set starting techs, or after turn 100 add any additional default techs # if (fo.currentTurn() <= 2) or ((total_rp - research_queue.totalSpent) > 0): research_index = get_research_index() if fo.currentTurn() == 1: # do only this one on first turn, to facilitate use of a turn-1 savegame for testing of alternate # research strategies new_tech = ["LRN_PHYS_BRAIN", "GRO_PLANET_ECOL"] else: new_tech = (TechsListsAI.sparse_galaxy_techs(research_index) if galaxy_is_sparse else TechsListsAI.primary_meta_techs(research_index)) debug("Empire %s (%d) is selecting research index %d", empire.name, empire_id, research_index) # techs_to_enqueue = (set(new_tech)-(set(completed_techs)|set(research_queue_list))) techs_to_enqueue = new_tech[:] tech_base = set(completed_techs + research_queue_list) techs_to_add = [] for tech in techs_to_enqueue: if tech not in tech_base: this_tech = fo.getTech(tech) if this_tech is None: error("Desired tech '%s' appears to not exist" % tech) continue missing_prereqs = [ preReq for preReq in this_tech.recursivePrerequisites(empire_id) if preReq not in tech_base ] techs_to_add.extend(missing_prereqs + [tech]) tech_base.update(missing_prereqs + [tech]) cum_cost = 0 debug(" Enqueued Tech: %20s \t\t %8s \t %s", "Name", "Cost", "CumulativeCost") for name in techs_to_add: try: enqueue_res = fo.issueEnqueueTechOrder(name, -1) if enqueue_res == 1: this_tech = fo.getTech(name) this_cost = 0 if this_tech: this_cost = this_tech.researchCost(empire_id) cum_cost += this_cost debug(" Enqueued Tech: %20s \t\t %8.0f \t %8.0f", name, this_cost, cum_cost) else: warning(" Failed attempt to enqueued Tech: " + name) except: # noqa: E722 warning(" Failed attempt to enqueued Tech: " + name, exc_info=True) debug("\n\nAll techs:") debug("=" * 20) alltechs = fo.techs() print_in_columns(sorted(fo.techs()), columns=3) debug("\n\nAll unqueued techs:") debug("=" * 20) # coveredTechs = new_tech+completed_techs print_in_columns([tn for tn in alltechs if tn not in tech_base], columns=3) debug("") if fo.currentTurn() == 1: return if True: research_queue_list = get_research_queue_techs() def_techs = TechsListsAI.defense_techs_1() for def_tech in def_techs: if (aistate.character.may_research_tech_classic(def_tech) and def_tech not in research_queue_list[:5] and not tech_is_complete(def_tech)): res = fo.issueEnqueueTechOrder( def_tech, min(3, len(research_queue_list))) debug( "Empire is very defensive, so attempted to fast-track %s, got result %d", def_tech, res) if False: # with current stats of Conc Camps, disabling this fast-track research_queue_list = get_research_queue_techs() if "CON_CONC_CAMP" in research_queue_list and aistate.character.may_research_tech_classic( "CON_CONC_CAMP"): insert_idx = min(40, research_queue_list.index("CON_CONC_CAMP")) else: insert_idx = max(0, min(40, len(research_queue_list) - 10)) if "SHP_DEFLECTOR_SHIELD" in research_queue_list and aistate.character.may_research_tech_classic( "SHP_DEFLECTOR_SHIELD"): insert_idx = min( insert_idx, research_queue_list.index("SHP_DEFLECTOR_SHIELD")) for cc_tech in ["CON_ARCH_PSYCH", "CON_CONC_CAMP"]: if (cc_tech not in research_queue_list[:insert_idx + 1] and not tech_is_complete(cc_tech) and aistate.character.may_research_tech_classic(cc_tech)): res = fo.issueEnqueueTechOrder(cc_tech, insert_idx) debug( "Empire is very aggressive, so attempted to fast-track %s, got result %d", cc_tech, res) elif fo.currentTurn() > 100: generate_default_research_order() research_queue_list = get_research_queue_techs() num_techs_accelerated = 1 # will ensure leading tech doesn't get dislodged got_ggg_tech = tech_is_complete("PRO_ORBITAL_GEN") got_sym_bio = tech_is_complete("GRO_SYMBIOTIC_BIO") got_xeno_gen = tech_is_complete("GRO_XENO_GENETICS") # # Consider accelerating techs; priority is # Supply/Detect range # xeno arch # ast / GG # gro xeno gen # distrib thought # quant net # pro sing gen # death ray 1 cleanup nest_tech = Dep.NEST_DOMESTICATION_TECH artif_minds = Dep.LRN_ARTIF_MINDS_1 if have_nest() and not tech_is_complete(nest_tech): if artif_minds in research_queue_list: insert_idx = 1 + research_queue_list.index(artif_minds) else: insert_idx = 1 res = fo.issueEnqueueTechOrder(nest_tech, insert_idx) num_techs_accelerated += 1 debug( "Have a monster nest, so attempted to fast-track %s, got result %d", nest_tech, res) research_queue_list = get_research_queue_techs() # # Supply range and detection range if False: # disabled for now, otherwise just to help with cold-folding / organization if len(aistate.colonisablePlanetIDs) == 0: best_colony_site_score = 0 else: best_colony_site_score = next( iter(aistate.colonisablePlanetIDs.items()))[1] if len(aistate.colonisableOutpostIDs) == 0: best_outpost_site_score = 0 else: best_outpost_site_score = next( iter(aistate.colonisableOutpostIDs.items()))[1] need_improved_scouting = best_colony_site_score < 150 or best_outpost_site_score < 200 if need_improved_scouting: if not tech_is_complete("CON_ORBITAL_CON"): num_techs_accelerated += 1 if ("CON_ORBITAL_CON" not in research_queue_list[:1 + num_techs_accelerated] ) and (tech_is_complete("PRO_FUSION_GEN") or ("PRO_FUSION_GEN" in research_queue_list[:1 + num_techs_accelerated])): res = fo.issueEnqueueTechOrder("CON_ORBITAL_CON", num_techs_accelerated) debug( "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d", "CON_ORBITAL_CON", res, ) elif not tech_is_complete("CON_CONTGRAV_ARCH"): num_techs_accelerated += 1 if ("CON_CONTGRAV_ARCH" not in research_queue_list[:1 + num_techs_accelerated] ) and (tech_is_complete("CON_METRO_INFRA")): for supply_tech in [ _s_tech for _s_tech in ["CON_ARCH_MONOFILS", "CON_CONTGRAV_ARCH"] if not tech_is_complete(_s_tech) ]: res = fo.issueEnqueueTechOrder(supply_tech, num_techs_accelerated) debug( "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d", supply_tech, res, ) else: pass research_queue_list = get_research_queue_techs() # could add more supply tech if False and not tech_is_complete( "SPY_DETECT_2"): # disabled for now, detect2 num_techs_accelerated += 1 if "SPY_DETECT_2" not in research_queue_list[:2 + num_techs_accelerated] and tech_is_complete( "PRO_FUSION_GEN" ): if "CON_ORBITAL_CON" not in research_queue_list[:1 + num_techs_accelerated]: res = fo.issueEnqueueTechOrder("SPY_DETECT_2", num_techs_accelerated) else: co_idx = research_queue_list.index("CON_ORBITAL_CON") res = fo.issueEnqueueTechOrder("SPY_DETECT_2", co_idx + 1) debug( "Empire has poor colony/outpost prospects, so attempted to fast-track %s, got result %d" "CON_ORBITAL_CON", res, ) research_queue_list = get_research_queue_techs() # # check to accelerate xeno_arch if True: # just to help with cold-folding / organization if (have_ruins() and not tech_is_complete("LRN_XENOARCH") and aistate.character.may_research_tech_classic("LRN_XENOARCH")): if artif_minds in research_queue_list: insert_idx = 7 + research_queue_list.index(artif_minds) elif "GRO_SYMBIOTIC_BIO" in research_queue_list: insert_idx = research_queue_list.index("GRO_SYMBIOTIC_BIO") + 1 else: insert_idx = num_techs_accelerated if "LRN_XENOARCH" not in research_queue_list[:insert_idx]: for xenoTech in [ "LRN_XENOARCH", "LRN_TRANSLING_THT", "LRN_PHYS_BRAIN", "LRN_ALGO_ELEGANCE" ]: if not tech_is_complete( xenoTech ) and xenoTech not in research_queue_list[:(insert_idx + 4)]: res = fo.issueEnqueueTechOrder(xenoTech, insert_idx) num_techs_accelerated += 1 debug( "ANCIENT_RUINS: have an ancient ruins, so attempted to fast-track %s to enable LRN_XENOARCH, got result %d", xenoTech, res, ) research_queue_list = get_research_queue_techs() if False and not enemies_sighted: # curently disabled # params = [ (tech, gate, target_slot, add_tech_list), ] params = [ ("GRO_XENO_GENETICS", "PRO_EXOBOTS", "PRO_EXOBOTS", ["GRO_GENETIC_MED", "GRO_XENO_GENETICS"]), ("PRO_EXOBOTS", "PRO_ADAPTIVE_AUTOMATION", "PRO_ADAPTIVE_AUTOMATION", ["PRO_EXOBOTS"]), ("PRO_ADAPTIVE_AUTOMATION", "PRO_NANOTECH_PROD", "PRO_NANOTECH_PROD", ["PRO_ADAPTIVE_AUTOMATION"]), ( "PRO_INDUSTRY_CENTER_I", "GRO_SYMBIOTIC_BIO", "GRO_SYMBIOTIC_BIO", [ "PRO_ROBOTIC_PROD", "PRO_FUSION_GEN", "PRO_INDUSTRY_CENTER_I" ], ), ("GRO_SYMBIOTIC_BIO", "SHP_ORG_HULL", "SHP_ZORTRIUM_PLATE", ["GRO_SYMBIOTIC_BIO"]), ] for (tech, gate, target_slot, add_tech_list) in params: if tech_is_complete(tech): break if tech_turns_left.get(gate, 0) not in [ 0, 1, 2, ]: # needs to exclude -1, the flag for no predicted completion continue if target_slot in research_queue_list: target_index = 1 + research_queue_list.index(target_slot) else: target_index = num_techs_accelerated for move_tech in add_tech_list: debug( "for tech %s, target_slot %s, target_index:%s ; num_techs_accelerated:%s", move_tech, target_slot, target_index, num_techs_accelerated, ) if tech_is_complete(move_tech): continue if target_index <= num_techs_accelerated: num_techs_accelerated += 1 if move_tech not in research_queue_list[:1 + target_index]: fo.issueEnqueueTechOrder(move_tech, target_index) debug( "Research: To prioritize %s, have advanced %s to slot %d", tech, move_tech, target_index) target_index += 1 # # check to accelerate asteroid or GG tech if True: # just to help with cold-folding / organization if have_asteroids(): insert_idx = (num_techs_accelerated if "GRO_SYMBIOTIC_BIO" not in research_queue_list else research_queue_list.index("GRO_SYMBIOTIC_BIO")) ast_tech = "PRO_MICROGRAV_MAN" if not (tech_is_complete(ast_tech) or ast_tech in research_queue_list[:(1 + insert_idx)]): res = fo.issueEnqueueTechOrder(ast_tech, insert_idx) num_techs_accelerated += 1 debug( "Asteroids: plan to colonize an asteroid belt, so attempted to fast-track %s , got result %d", ast_tech, res, ) research_queue_list = get_research_queue_techs() elif tech_is_complete("SHP_ZORTRIUM_PLATE"): insert_idx = ( (1 + insert_idx) if "LRN_FORCE_FIELD" not in research_queue_list else max( 1 + insert_idx, research_queue_list.index("LRN_FORCE_FIELD") - 1)) for ast_tech in [ "SHP_ASTEROID_HULLS", "SHP_IMPROVED_ENGINE_COUPLINGS" ]: if not tech_is_complete( ast_tech ) and ast_tech not in research_queue_list[:insert_idx + 1]: res = fo.issueEnqueueTechOrder(ast_tech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 debug( "Asteroids: plan to colonize an asteroid belt, so attempted to fast-track %s , got result %d", ast_tech, res, ) research_queue_list = get_research_queue_techs() if have_gas_giant() and not tech_is_complete("PRO_ORBITAL_GEN"): fusion_idx = (0 if "PRO_FUSION_GEN" not in research_queue_list else (1 + research_queue_list.index("PRO_FUSION_GEN"))) forcefields_idx = (0 if "LRN_FORCE_FIELD" not in research_queue_list else (1 + research_queue_list.index("LRN_FORCE_FIELD"))) insert_idx = max( fusion_idx, forcefields_idx) if enemies_sighted else fusion_idx if "PRO_ORBITAL_GEN" not in research_queue_list[:insert_idx + 1]: res = fo.issueEnqueueTechOrder("PRO_ORBITAL_GEN", insert_idx) num_techs_accelerated += 1 debug( "GasGiant: plan to colonize a gas giant, so attempted to fast-track %s, got result %d", "PRO_ORBITAL_GEN", res, ) research_queue_list = get_research_queue_techs() # # assess if our empire has any non-lousy colonizers, & boost gro_xeno_gen if we don't if True: # just to help with cold-folding / organization if got_ggg_tech and got_sym_bio and (not got_xeno_gen): most_adequate = 0 for specName in get_colony_builders(): environs = {} this_spec = fo.getSpecies(specName) if not this_spec: continue for ptype in [ fo.planetType.swamp, fo.planetType.radiated, fo.planetType.toxic, fo.planetType.inferno, fo.planetType.barren, fo.planetType.tundra, fo.planetType.desert, fo.planetType.terran, fo.planetType.ocean, fo.planetType.asteroids, ]: environ = this_spec.getPlanetEnvironment(ptype) environs.setdefault(environ, []).append(ptype) most_adequate = max( most_adequate, len(environs.get(fo.planetEnvironment.adequate, []))) if most_adequate == 0: insert_idx = num_techs_accelerated for xg_tech in ["GRO_XENO_GENETICS", "GRO_GENETIC_ENG"]: if (xg_tech not in research_queue_list[:1 + num_techs_accelerated] and not tech_is_complete(xg_tech) and aistate.character.may_research_tech_classic( xg_tech)): res = fo.issueEnqueueTechOrder(xg_tech, insert_idx) num_techs_accelerated += 1 debug( "Empire has poor colonizers, so attempted to fast-track %s, got result %d", xg_tech, res, ) research_queue_list = get_research_queue_techs() # # check to accelerate translinguistics if True: # just to help with cold-folding / organization # planet is needed to determine the cost. Without a capital we have bigger problems anyway... if not tech_is_complete("LRN_TRANSLING_THT") and translators_wanted(): insert_idx = num_techs_accelerated for dt_ech in [ "LRN_TRANSLING_THT", "LRN_PHYS_BRAIN", "LRN_ALGO_ELEGANCE" ]: if (dt_ech not in research_queue_list[:insert_idx + 2] and not tech_is_complete(dt_ech) and aistate.character.may_research_tech_classic(dt_ech)): res = fo.issueEnqueueTechOrder(dt_ech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 fmt_str = "Empire wants to build translators, so attempted to fast-track %s (got result %d)" fmt_str += " with current target_RP %.1f and current pop %.1f, on turn %d" debug(fmt_str, dt_ech, res, resource_production, empire.population(), fo.currentTurn()) research_queue_list = get_research_queue_techs() # # check to accelerate distrib thought if True: # just to help with cold-folding / organization if not tech_is_complete("LRN_DISTRIB_THOUGHT"): got_telepathy = False for specName in get_empire_planets_by_species(): this_spec = fo.getSpecies(specName) if this_spec and ("TELEPATHIC" in list(this_spec.tags)): got_telepathy = True break pop_threshold = 100 if got_telepathy else 300 if empire.population() > pop_threshold: insert_idx = num_techs_accelerated for dt_ech in [ "LRN_PHYS_BRAIN", "LRN_TRANSLING_THT", "LRN_PSIONICS", "LRN_DISTRIB_THOUGHT" ]: if (dt_ech not in research_queue_list[:insert_idx + 2] and not tech_is_complete(dt_ech) and aistate.character.may_research_tech_classic( dt_ech)): res = fo.issueEnqueueTechOrder(dt_ech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 fmt_str = "Empire has a telepathic race, so attempted to fast-track %s (got result %d)" fmt_str += " with current target_RP %.1f and current pop %.1f, on turn %d" debug(fmt_str, dt_ech, res, resource_production, empire.population(), fo.currentTurn()) research_queue_list = get_research_queue_techs() # # check to accelerate quant net if False: # disabled for now, otherwise just to help with cold-folding / organization if aistate.character.may_research_tech_classic("LRN_QUANT_NET") and ( population_with_research_focus() >= 40): if not tech_is_complete("LRN_QUANT_NET"): insert_idx = num_techs_accelerated # TODO determine min target slot if reenabling for qnTech in ["LRN_NDIM_SUBSPACE", "LRN_QUANT_NET"]: if qnTech not in research_queue_list[:insert_idx + 2] and not tech_is_complete( qnTech): res = fo.issueEnqueueTechOrder(qnTech, insert_idx) num_techs_accelerated += 1 insert_idx += 1 debug( "Empire has many researchers, so attempted to fast-track %s (got result %d) on turn %d", qnTech, res, fo.currentTurn(), ) research_queue_list = get_research_queue_techs() # # if we own a blackhole, accelerate sing_gen and conc camp if True: # just to help with cold-folding / organization if (fo.currentTurn() > 50 and len(AIstate.empireStars.get(fo.starType.blackHole, [])) != 0 and aistate.character.may_research_tech_classic("PRO_SINGULAR_GEN") and not tech_is_complete(Dep.PRO_SINGULAR_GEN) and tech_is_complete("LRN_EVERYTHING")): # sing_tech_list = [ "LRN_GRAVITONICS" , "PRO_SINGULAR_GEN"] # formerly also "CON_ARCH_PSYCH", "CON_CONC_CAMP", sing_gen_tech = fo.getTech(Dep.PRO_SINGULAR_GEN) sing_tech_list = [ pre_req for pre_req in sing_gen_tech.recursivePrerequisites(empire_id) if not tech_is_complete(pre_req) ] sing_tech_list += [Dep.PRO_SINGULAR_GEN] for singTech in sing_tech_list: if singTech not in research_queue_list[:num_techs_accelerated + 1]: res = fo.issueEnqueueTechOrder(singTech, num_techs_accelerated) num_techs_accelerated += 1 debug( "have a black hole star outpost/colony, so attempted to fast-track %s, got result %d", singTech, res, ) research_queue_list = get_research_queue_techs() # # if got deathray from Ruins, remove most prereqs from queue if True: # just to help with cold-folding / organization if tech_is_complete("SHP_WEAPON_4_1"): this_tech = fo.getTech("SHP_WEAPON_4_1") if this_tech: missing_prereqs = [ preReq for preReq in this_tech.recursivePrerequisites(empire_id) if preReq in research_queue_list ] if len(missing_prereqs ) > 2: # leave plasma 4 and 3 if up to them already for preReq in missing_prereqs: # sorted(missing_prereqs, reverse=True)[2:] if preReq in research_queue_list: fo.issueDequeueTechOrder(preReq) research_queue_list = get_research_queue_techs() if "SHP_WEAPON_4_2" in research_queue_list: # (should be) idx = research_queue_list.index("SHP_WEAPON_4_2") fo.issueEnqueueTechOrder("SHP_WEAPON_4_2", max(0, idx - 18)) # TODO: Remove the following example code # Example/Test code for the new ShipDesigner functionality techs = [ "SHP_WEAPON_4_2", "SHP_TRANSSPACE_DRIVE", "SHP_INTSTEL_LOG", "SHP_ASTEROID_HULLS", "" ] for tech in techs: this_tech = fo.getTech(tech) if not this_tech: debug("Invalid Tech specified") continue unlocked_items = this_tech.unlockedItems unlocked_hulls = [] unlocked_parts = [] for item in unlocked_items: if item.type == fo.unlockableItemType.shipPart: debug("Tech %s unlocks a ShipPart: %s", tech, item.name) unlocked_parts.append(item.name) elif item.type == fo.unlockableItemType.shipHull: debug("Tech %s unlocks a ShipHull: %s", tech, item.name) unlocked_hulls.append(item.name) if not (unlocked_parts or unlocked_hulls): debug("No new ship parts/hulls unlocked by tech %s", tech) continue old_designs = ShipDesignAI.WarShipDesigner().optimize_design( consider_fleet_count=False) new_designs = ShipDesignAI.WarShipDesigner().optimize_design( additional_hulls=unlocked_hulls, additional_parts=unlocked_parts, consider_fleet_count=False) if not (old_designs and new_designs): # AI is likely defeated; don't bother with logging error message continue old_rating, old_pid, old_design_id, old_cost, old_stats = old_designs[ 0] old_design = fo.getShipDesign(old_design_id) new_rating, new_pid, new_design_id, new_cost, new_stats = new_designs[ 0] new_design = fo.getShipDesign(new_design_id) if new_rating > old_rating: debug("Tech %s gives access to a better design!", tech) debug("old best design: Rating %.5f", old_rating) debug("old design specs: %s - %s", old_design.hull, list(old_design.parts)) debug("new best design: Rating %.5f", new_rating) debug("new design specs: %s - %s", new_design.hull, list(new_design.parts)) else: debug( "Tech %s gives access to new parts or hulls but there seems to be no military advantage.", tech)
def assess_protection_focus(pid): """Return True if planet should use Protection Focus""" this_planet = planetMap[pid] sys_status = foAI.foAIstate.systemStatus.get(this_planet.systemID, {}) threat_from_supply = (0.25 * foAI.foAIstate.empire_standard_enemy_rating * min(2, len(sys_status.get('enemies_nearly_supplied', [])))) print "Planet %s has regional+supply threat of %.1f" % ('P_%d<%s>'%(pid, this_planet.name), threat_from_supply) regional_threat = sys_status.get('regional_threat', 0) + threat_from_supply if not regional_threat: # no need for protection if currentFocus[pid] == PFocus: print "Advising dropping Protection Focus at %s due to no regional threat" % this_planet return False cur_prod_val = weighted_sum_output([currentOutput[pid][IFocus], currentOutput[pid][RFocus]]) target_prod_val = max(map(weighted_sum_output, [newTargets[pid][IFocus], newTargets[pid][RFocus]])) prot_prod_val = weighted_sum_output(newTargets[pid][PFocus]) local_production_diff = 0.8 * cur_prod_val + 0.2 * target_prod_val - prot_prod_val fleet_threat = sys_status.get('fleetThreat', 0) # TODO: relax the below rejection once the overall determination of PFocus is better tuned if not fleet_threat and local_production_diff > 8: if currentFocus[pid] == PFocus: print "Advising dropping Protection Focus at %s due to excessive productivity loss" % this_planet return False local_p_defenses = sys_status.get('mydefenses', {}).get('overall',0) # TODO have adjusted_p_defenses take other in-system planets into account adjusted_p_defenses = local_p_defenses * (1.0 if currentFocus[pid] != PFocus else 0.5) local_fleet_rating = sys_status.get('myFleetRating', 0) combined_local_defenses = sys_status.get('all_local_defenses', 0) my_neighbor_rating = sys_status.get('my_neighbor_rating', 0) neighbor_threat = sys_status.get('neighborThreat', 0) safety_factor = 1.2 if currentFocus[pid] == PFocus else 0.5 cur_shield = this_planet.currentMeterValue(fo.meterType.shield) max_shield = this_planet.currentMeterValue(fo.meterType.maxShield) cur_troops = this_planet.currentMeterValue(fo.meterType.troops) max_troops = this_planet.currentMeterValue(fo.meterType.maxTroops) cur_defense = this_planet.currentMeterValue(fo.meterType.defense) max_defense = this_planet.currentMeterValue(fo.meterType.maxDefense) def_meter_pairs = [(cur_troops, max_troops), (cur_shield, max_shield), (cur_defense, max_defense)] use_protection = True reason = "" if (fleet_threat and # i.e., an enemy is sitting on us (currentFocus[pid] != PFocus or # too late to start protection TODO: but maybe regen worth it # protection forcus only useful here if it maintains an elevated level all([AIDependencies.PROT_FOCUS_MULTIPLIER * a <= b for a,b in def_meter_pairs]))): use_protection = False reason = "A" elif ((currentFocus[pid] != PFocus and cur_shield < max_shield - 2 and not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and (cur_defense < max_defense - 2 and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and (cur_troops < max_troops - 2)): use_protection = False reason = "B1" elif ((currentFocus[pid] == PFocus and cur_shield*AIDependencies.PROT_FOCUS_MULTIPLIER < max_shield-2 and not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and (cur_defense*AIDependencies.PROT_FOCUS_MULTIPLIER < max_defense-2 and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and (cur_troops*AIDependencies.PROT_FOCUS_MULTIPLIER < max_troops-2)): use_protection = False reason = "B2" elif max(max_shield, max_troops, max_defense) < 3: # joke defenses, don't bother with protection focus use_protection = False reason = "C" elif regional_threat and local_production_diff <= 2.0: reason = "D" pass # i.e., use_protection = True elif safety_factor * regional_threat <= local_fleet_rating: use_protection = False reason = "E" elif (safety_factor * regional_threat <= combined_local_defenses and (currentFocus[pid] != PFocus or (0.5 * safety_factor * regional_threat <= local_fleet_rating and fleet_threat == 0 and neighbor_threat < combined_local_defenses and local_production_diff > 5))): use_protection = False reason = "F" elif (regional_threat <= FleetUtilsAI.combine_ratings(local_fleet_rating, adjusted_p_defenses) and safety_factor * regional_threat <= FleetUtilsAI.combine_ratings_list([my_neighbor_rating, local_fleet_rating, adjusted_p_defenses]) and local_production_diff > 5): use_protection = False reason = "G" if use_protection or currentFocus[pid] == PFocus: print ("Advising %sProtection Focus (reason %s) for planet %s, with local_prod_diff of %.1f, comb. local" " defenses %.1f, local fleet rating %.1f and regional threat %.1f, threat sources: %s") % ( ["dropping ", ""][use_protection], reason, this_planet, local_production_diff, combined_local_defenses, local_fleet_rating, regional_threat, sys_status['regional_fleet_threats']) return use_protection
def set_planet_industry_and_research_foci(focus_manager, priority_ratio): """Adjust planet's industry versus research focus while targeting the given ratio and avoiding penalties from changing focus.""" print "\n-----------------------------------------" print "Making Planet Focus Change Determinations\n" ratios = [] # for each planet, calculate RP:PP value ratio at which industry focus and # research focus would have the same total value, & sort by that include a # bias to slightly discourage changing foci target_pp = 0.001 target_rp = 0.001 resource_timer.start("Loop") # loop has_force = tech_is_complete("CON_FRC_ENRG_STRC") # cumulative all industry focus cumulative_pp, cumulative_rp = 0, 0 # Handle presets which only have possible output for preset focus for pid, info in focus_manager.baked_planet_info.items(): future_pp, future_rp = info.possible_output[info.future_focus] target_pp += future_pp target_rp += future_rp cumulative_pp += future_pp cumulative_rp += future_rp # tally max Industry for pid, info in focus_manager.raw_planet_info.items(): i_pp, i_rp = info.possible_output[INDUSTRY] cumulative_pp += i_pp cumulative_rp += i_rp if RESEARCH not in info.planet.availableFoci: focus_manager.bake_future_focus(pid, info.current_focus, False) # smallest possible ratio of research to industry with an all industry focus maxi_ratio = cumulative_rp / max(0.01, cumulative_pp) for adj_round in [2, 3, 4]: for pid, info in focus_manager.raw_planet_info.items(): ii, tr = info.possible_output[INDUSTRY] ri, rr = info.possible_output[RESEARCH] ci, cr = info.current_output research_penalty = (info.current_focus != RESEARCH) # calculate factor F at which ii + F * tr == ri + F * rr =====> F = ( ii-ri ) / (rr-tr) factor = (ii - ri) / max( 0.01, rr - tr ) # don't let denominator be zero for planets where focus doesn't change RP planet = info.planet if adj_round == 2: # take research at planets with very cheap research if (maxi_ratio < priority_ratio) and ( target_rp < priority_ratio * cumulative_pp) and (factor <= 1.0): target_pp += ri target_rp += rr focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 3: # take research at planets where can do reasonable balance if has_force or foAI.foAIstate.character.may_dither_focus_to_gain_research( ) or (target_rp >= priority_ratio * cumulative_pp): continue pop = planet.currentMeterValue(fo.meterType.population) t_pop = planet.currentMeterValue(fo.meterType.targetPopulation) # if AI is aggressive+, and this planet in range where temporary Research focus can get an additional RP at cost of 1 PP, and still need some RP, then do it if pop < t_pop - 5: continue if (ci > ii + 8) or (((rr > ii) or ((rr - cr) >= 1 + 2 * research_penalty)) and ((rr - tr) >= 3) and ((cr - tr) >= 0.7 * ((ii - ci) * (1 + 0.1 * research_penalty)))): target_pp += ci - 1 - research_penalty target_rp += cr + 1 focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 4: # assume default IFocus target_pp += ii # icurTargets initially calculated by Industry focus, which will be our default focus target_rp += tr ratios.append((factor, pid, info)) ratios.sort() printed_header = False got_algo = tech_is_complete("LRN_ALGO_ELEGANCE") for ratio, pid, info in ratios: if priority_ratio < (target_rp / (target_pp + 0.0001)): # we have enough RP if ratio < 1.1 and foAI.foAIstate.character.may_research_heavily( ): # but wait, RP is still super cheap relative to PP, maybe will take more RP if priority_ratio < 1.5 * ( target_rp / (target_pp + 0.0001) ): # yeah, really a glut of RP, stop taking RP break else: # RP not super cheap & we have enough, stop taking it break ii, tr = info.possible_output[INDUSTRY] ri, rr = info.possible_output[RESEARCH] # if focus_manager.current_focus[pid] == MFocus: # ii = max( ii, focus_manager.possible_output[MFocus][0] ) if ( (ratio > 2.0 and target_pp < 15 and got_algo) or (ratio > 2.5 and target_pp < 25 and ii > 5 and got_algo) or (ratio > 3.0 and target_pp < 40 and ii > 5 and got_algo) or (ratio > 4.0 and target_pp < 100 and ii > 10) or ((target_rp + rr - tr) / max(0.001, target_pp - ii + ri) > 2 * priority_ratio) ): # we already have algo elegance and more RP would be too expensive, or overkill if not printed_header: printed_header = True print "Rejecting further Research Focus choices as too expensive:" print "%34s|%20s|%15s |%15s|%15s |%15s |%15s" % ( " Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " rejectedFocus ", " rejected target RP/PP ", "rejected RP-PP EQF") old_focus = info.current_focus c_pp, c_rp = info.current_output ot_pp, ot_rp = info.possible_output[old_focus] nt_pp, nt_rp = info.possible_output[RESEARCH] print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f | cF: %8s | nF: %8s | cT: %5.1f / %5.1f | %.2f" % ( pid, info.planet.name, c_rp, c_pp, ot_rp, ot_pp, _focus_names.get(old_focus, 'unknown'), _focus_names[RESEARCH], nt_rp, nt_pp, ratio) continue # RP is getting too expensive, but might be willing to still allocate from a planet with less PP to lose # if focus_manager.planet_map[pid].currentMeterValue(fo.meterType.targetPopulation) >0: #only set to research if pop won't die out focus_manager.bake_future_focus(pid, RESEARCH, False) target_rp += (rr - tr) target_pp -= (ii - ri) # Any planet in the ratios list and still raw is set to industry for ratio, pid, info in ratios: if pid in focus_manager.raw_planet_info: focus_manager.bake_future_focus(pid, INDUSTRY, False)
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 evaluate_invasion_planet(planet_id): """Return the invasion value (score, troops) of a planet.""" universe = fo.getUniverse() empire_id = fo.empireID() detail = [] planet = universe.getPlanet(planet_id) if planet is None: debug("Invasion AI couldn't access any info for planet id %d" % planet_id) return [0, 0] system_id = planet.systemID # by using the following instead of simply relying on stealth meter reading, # can (sometimes) plan ahead even if planet is temporarily shrouded by an ion storm predicted_detectable = EspionageAI.colony_detectable_by_empire( planet_id, empire=fo.empireID(), default_result=False) if not predicted_detectable: if get_partial_visibility_turn(planet_id) < fo.currentTurn(): debug("InvasionAI predicts planet id %d to be stealthed" % planet_id) return [0, 0] else: debug( "InvasionAI predicts planet id %d to be stealthed" % planet_id + ", but somehow have current visibility anyway, will still consider as target" ) # Check if the target planet was extra-stealthed somehow its system was last viewed # this test below may augment the tests above, # but can be thrown off by temporary combat-related sighting system_last_seen = get_partial_visibility_turn(planet_id) planet_last_seen = get_partial_visibility_turn(system_id) if planet_last_seen < system_last_seen: # TODO: track detection strength, order new scouting when it goes up debug( "Invasion AI considering planet id %d (stealthed at last view), still proceeding." % planet_id) # get a baseline evaluation of the planet as determined by ColonisationAI species_name = planet.speciesName species = fo.getSpecies(species_name) empire_research_list = tuple(element.tech for element in fo.getEmpire().researchQueue) if not species or AIDependencies.TAG_DESTROYED_ON_CONQUEST in species.tags: # this call iterates over this Empire's available species with which it could colonize after an invasion planet_eval = ColonisationAI.assign_colonisation_values( [planet_id], MissionType.INVASION, None, detail) colony_base_value = max( 0.75 * planet_eval.get(planet_id, [0])[0], calculate_planet_colonization_rating. calculate_planet_colonization_rating( planet_id=planet_id, mission_type=MissionType.OUTPOST, spec_name=None, detail=detail, empire_research_list=empire_research_list, ), ) else: colony_base_value = calculate_planet_colonization_rating.calculate_planet_colonization_rating( planet_id=planet_id, mission_type=MissionType.INVASION, spec_name=species_name, detail=detail, empire_research_list=empire_research_list, ) # Add extra score for all buildings on the planet building_values = { "BLD_IMPERIAL_PALACE": 1000, "BLD_CULTURE_ARCHIVES": 1000, "BLD_AUTO_HISTORY_ANALYSER": 100, "BLD_SHIPYARD_BASE": 100, "BLD_SHIPYARD_ORG_ORB_INC": 200, "BLD_SHIPYARD_ORG_XENO_FAC": 200, "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200, "BLD_SHIPYARD_CON_NANOROBO": 300, "BLD_SHIPYARD_CON_GEOINT": 400, "BLD_SHIPYARD_CON_ADV_ENGINE": 1000, "BLD_SHIPYARD_AST": 300, "BLD_SHIPYARD_AST_REF": 1000, "BLD_SHIPYARD_ENRG_SOLAR": 1500, "BLD_INDUSTRY_CENTER": 500, "BLD_GAS_GIANT_GEN": 200, "BLD_SOL_ORB_GEN": 800, "BLD_BLACK_HOLE_POW_GEN": 2000, "BLD_ENCLAVE_VOID": 500, "BLD_NEUTRONIUM_EXTRACTOR": 2000, "BLD_NEUTRONIUM_SYNTH": 2000, "BLD_NEUTRONIUM_FORGE": 1000, "BLD_CONC_CAMP": 100, "BLD_BIOTERROR_PROJECTOR": 1000, "BLD_SHIPYARD_ENRG_COMP": 3000, } bld_tally = 0 for bldType in [ universe.getBuilding(bldg).buildingTypeName for bldg in planet.buildingIDs ]: bval = building_values.get(bldType, 50) bld_tally += bval detail.append("%s: %d" % (bldType, bval)) # Add extra score for unlocked techs when we conquer the species tech_tally = 0 value_per_pp = 4 for unlocked_tech in AIDependencies.SPECIES_TECH_UNLOCKS.get( species_name, []): if not tech_is_complete(unlocked_tech): rp_cost = fo.getTech(unlocked_tech).researchCost(empire_id) tech_value = value_per_pp * rp_cost tech_tally += tech_value detail.append("%s: %d" % (unlocked_tech, tech_value)) least_jumps_path, max_jumps = _get_path_from_capital(planet) clear_path = True aistate = get_aistate() system_status = aistate.systemStatus.get(system_id, {}) system_fleet_treat = system_status.get("fleetThreat", 1000) system_monster_threat = system_status.get("monsterThreat", 0) sys_total_threat = system_fleet_treat + system_monster_threat + system_status.get( "planetThreat", 0) max_path_threat = system_fleet_treat mil_ship_rating = MilitaryAI.cur_best_mil_ship_rating() for path_sys_id in least_jumps_path: path_leg_status = aistate.systemStatus.get(path_sys_id, {}) path_leg_threat = path_leg_status.get( "fleetThreat", 1000) + path_leg_status.get("monsterThreat", 0) if path_leg_threat > 0.5 * mil_ship_rating: clear_path = False if path_leg_threat > max_path_threat: max_path_threat = path_leg_threat pop = planet.currentMeterValue(fo.meterType.population) target_pop = planet.currentMeterValue(fo.meterType.targetPopulation) troops = planet.currentMeterValue(fo.meterType.troops) troop_regen = planet.currentMeterValue( fo.meterType.troops) - planet.initialMeterValue(fo.meterType.troops) max_troops = planet.currentMeterValue(fo.meterType.maxTroops) # TODO: refactor troop determination into function for use in mid-mission updates and also consider defender techs max_troops += AIDependencies.TROOPS_PER_POP * (target_pop - pop) this_system = universe.getSystem(system_id) secure_targets = [system_id] + list(this_system.planetIDs) system_secured = False secure_fleets = get_aistate().get_fleet_missions_with_any_mission_types( [MissionType.SECURE, MissionType.MILITARY]) for mission in secure_fleets: secure_fleet_id = mission.fleet.id s_fleet = universe.getFleet(secure_fleet_id) if not s_fleet or s_fleet.systemID != system_id: continue if mission.type in [MissionType.SECURE, MissionType.MILITARY]: target_obj = mission.target.get_object() if target_obj is not None and target_obj.id in secure_targets: system_secured = True break system_secured = system_secured and system_status.get("myFleetRating", 0) debug( "Invasion eval of %s\n" " - maxShields: %.1f\n" " - sysFleetThreat: %.1f\n" " - sysMonsterThreat: %.1f", planet, planet.currentMeterValue(fo.meterType.maxShield), system_fleet_treat, system_monster_threat, ) enemy_val = 0 if planet.owner != -1: # value in taking this away from an enemy enemy_val = 20 * ( planet.currentMeterValue(fo.meterType.targetIndustry) + 2 * planet.currentMeterValue(fo.meterType.targetResearch)) # devalue invasions that would require too much military force preferred_max_portion = MilitaryAI.get_preferred_max_military_portion_for_single_battle( ) total_max_mil_rating = MilitaryAI.get_concentrated_tot_mil_rating() threat_exponent = 2 # TODO: make this a character trait; higher aggression with a lower exponent threat_factor = min( 1, preferred_max_portion * total_max_mil_rating / (sys_total_threat + 0.001))**threat_exponent design_id, _, locs = get_best_ship_info(PriorityType.PRODUCTION_INVASION) if not locs or not universe.getPlanet(locs[0]): # We are in trouble anyway, so just calculate whatever approximation... build_time = 4 planned_troops = troops if system_secured else min( troops + troop_regen * (max_jumps + build_time), max_troops) planned_troops += 0.01 # we must attack with more troops than there are defenders troop_cost = math.ceil((planned_troops + _TROOPS_SAFETY_MARGIN) / 6.0) * 20 * FleetUtilsAI.get_fleet_upkeep() else: loc = locs[0] species_here = universe.getPlanet(loc).speciesName design = fo.getShipDesign(design_id) cost_per_ship = design.productionCost(empire_id, loc) build_time = design.productionTime(empire_id, loc) troops_per_ship = CombatRatingsAI.weight_attack_troops( design.troopCapacity, get_species_tag_grade(species_here, Tags.ATTACKTROOPS)) planned_troops = troops if system_secured else min( troops + troop_regen * (max_jumps + build_time), max_troops) planned_troops += 0.01 # we must attack with more troops than there are defenders ships_needed = math.ceil( (planned_troops + _TROOPS_SAFETY_MARGIN) / float(troops_per_ship)) troop_cost = ships_needed * cost_per_ship # fleet upkeep is already included in query from server # apply some bias to expensive operations normalized_cost = float(troop_cost) / max(fo.getEmpire().productionPoints, 1) normalized_cost = max(1.0, normalized_cost) cost_score = (normalized_cost**2 / 50.0) * troop_cost base_score = colony_base_value + bld_tally + tech_tally + enemy_val - cost_score # If the AI does have enough total military to attack this target, and the target is more than minimally valuable, # don't let the threat_factor discount the adjusted value below MIN_INVASION_SCORE +1, so that if there are no # other targets the AI could still pursue this one. Otherwise, scoring pressure from # MilitaryAI.get_preferred_max_military_portion_for_single_battle might prevent the AI from attacking heavily # defended but still defeatable targets even if it has no softer targets available. if total_max_mil_rating > sys_total_threat and base_score > 2 * MIN_INVASION_SCORE: threat_factor = max(threat_factor, (MIN_INVASION_SCORE + 1) / base_score) planet_score = retaliation_risk_factor(planet.owner) * threat_factor * max( 0, base_score) if clear_path: planet_score *= 1.5 debug( " - planet score: %.2f\n" " - planned troops: %.2f\n" " - projected troop cost: %.1f\n" " - threat factor: %s\n" " - planet detail: %s\n" " - popval: %.1f\n" " - bldval: %s\n" " - enemyval: %s", planet_score, planned_troops, troop_cost, threat_factor, detail, colony_base_value, bld_tally, enemy_val, ) debug(" - system secured: %s" % system_secured) return [planet_score, planned_troops]
def 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 set_planet_industry_and_research_foci(focus_manager, priority_ratio): """Adjust planet's industry versus research focus while targeting the given ratio and avoiding penalties from changing focus.""" debug("\n-----------------------------------------") debug("Making Planet Focus Change Determinations\n") ratios = [] # for each planet, calculate RP:PP value ratio at which industry focus and # research focus would have the same total value, & sort by that include a # bias to slightly discourage changing foci target_pp = 0.001 target_rp = 0.001 resource_timer.start("Loop") # loop has_force = tech_is_complete("CON_FRC_ENRG_STRC") # cumulative all industry focus cumulative_pp, cumulative_rp = 0, 0 # Handle presets which only have possible output for preset focus for pid, pinfo in focus_manager.baked_planet_info.items(): future_pp, future_rp = pinfo.possible_output[pinfo.future_focus] target_pp += future_pp target_rp += future_rp cumulative_pp += future_pp cumulative_rp += future_rp # tally max Industry for pid, pinfo in focus_manager.raw_planet_info.items(): i_pp, i_rp = pinfo.possible_output[INDUSTRY] cumulative_pp += i_pp cumulative_rp += i_rp if RESEARCH not in pinfo.planet.availableFoci: if focus_manager.bake_future_focus(pid, INDUSTRY, False): target_pp += i_pp target_rp += i_rp # smallest possible ratio of research to industry with an all industry focus maxi_ratio = cumulative_rp / max(0.01, cumulative_pp) aistate = get_aistate() for adj_round in [1, 2, 3, 4]: for pid, pinfo in focus_manager.raw_planet_info.items(): ii, tr = pinfo.possible_output[INDUSTRY] ri, rr = pinfo.possible_output[RESEARCH] ci, cr = pinfo.current_output research_penalty = AIDependencies.FOCUS_CHANGE_PENALTY if ( pinfo.current_focus != RESEARCH) else 0 # calculate factor F at which ii + F * tr == ri + F * rr =====> F = ( ii-ri ) / (rr-tr) factor = (ii - ri) / max(0.01, rr - tr) planet = pinfo.planet if adj_round == 1: # take research at planets that can not use industry focus if INDUSTRY not in pinfo.planet.availableFoci: target_pp += ri target_rp += rr focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 2: # take research at planets with very cheap research if (maxi_ratio < priority_ratio) and ( target_rp < priority_ratio * cumulative_pp) and (factor <= 1.0): target_pp += ri target_rp += rr focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 3: # take research at planets where can do reasonable balance # if this planet in range where temporary Research focus ("research dithering") can get some additional # RP at a good PP cost, and still need some RP, then consider doing it. # Won't really work if AI has researched Force Energy Structures (meters fall too fast) # TODO: add similar decision points by which research-rich planets # might possibly choose to dither for industry points if any( (has_force, not aistate.character.may_dither_focus_to_gain_research(), target_rp >= priority_ratio * cumulative_pp)): continue pop = planet.initialMeterValue(fo.meterType.population) t_pop = planet.initialMeterValue(fo.meterType.targetPopulation) # let pop stabilize before trying to dither; the calculations that determine whether dithering will be # advantageous assume a stable population, so a larger gap means a less reliable decision MAX_DITHER_POP_GAP = 5 # some smallish number if pop < t_pop - MAX_DITHER_POP_GAP: continue # if gap between R-focus and I-focus target research levels is too narrow, don't research dither. # A gap of 1 would provide a single point of RP, at a cost of 3 PP; a gap of 2 the cost is 2.7 PP/RP; # a gap of 3 at 2.1 PP/RP; a gap of 4, 1.8 PP/RP; a gap of 5, 1.7 PP/RP. The bigger the gap, the # better; a gap of 10 would provide RP at a cost of 1.3 PP/RP (or even less if the target PP gap # were smaller). MIN_DITHER_TARGET_RESEARCH_GAP = 3 if (rr - tr) < MIN_DITHER_TARGET_RESEARCH_GAP: continue # could double check that planet even has Industry Focus available, but no harm even if not # So at this point we have determined the planet has research targets compatible with employing focus # dither. The research focus phase will last until current research reaches the Research-Focus # research target, determined by the 'research_capped' indicator, at which point the focus is # changed to Industry (currently left to be handled by standard focus code later). The research_capped # indicator, though, is a spot indicator whose value under normal dither operation would revert on the # next turn, so we need another indicator to maintain the focus change until the Industry meter has # recovered to its max target level; the indicator to keep the research phase turned off needs to have # some type of hysteresis component or otherwise be sensitive to the direction of meter change; in the # indicator below this is accomplished primarily by comparing a difference of changes on both the # research and industry side, the 'research_penalty' adjustment in the industry_recovery_phase # calculation prevents the indicator from stopping recovery mode one turn too early. research_capped = (rr - cr) <= 0.5 industry_recovery_phase = (ii - ci) - ( cr - tr ) > AIDependencies.FOCUS_CHANGE_PENALTY - research_penalty if not (research_capped or industry_recovery_phase): target_pp += ci - 1 - research_penalty target_rp += cr + 1 focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 4: # assume default IFocus target_pp += ii # icurTargets initially calculated by Industry focus, which will be our default focus target_rp += tr ratios.append((factor, pid, pinfo)) ratios.sort() printed_header = False got_algo = tech_is_complete("LRN_ALGO_ELEGANCE") for ratio, pid, pinfo in ratios: if priority_ratio < (target_rp / (target_pp + 0.0001)): # we have enough RP if ratio < 1.1 and aistate.character.may_research_heavily(): # but wait, RP is still super cheap relative to PP, maybe will take more RP if priority_ratio < 1.5 * (target_rp / (target_pp + 0.0001)): # yeah, really a glut of RP, stop taking RP break else: # RP not super cheap & we have enough, stop taking it break ii, tr = pinfo.possible_output[INDUSTRY] ri, rr = pinfo.possible_output[RESEARCH] if ((ratio > 2.0 and target_pp < 15 and got_algo) or (ratio > 2.5 and target_pp < 25 and ii > 5 and got_algo) or (ratio > 3.0 and target_pp < 40 and ii > 5 and got_algo) or (ratio > 4.0 and target_pp < 100 and ii > 10) or ((target_rp + rr - tr) / max(0.001, target_pp - ii + ri) > 2 * priority_ratio)): # we already have algo elegance and more RP would be too expensive, or overkill if not printed_header: printed_header = True debug( "Rejecting further Research Focus choices as too expensive:" ) debug("%34s|%20s|%15s |%15s|%15s |%15s |%15s", " Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " rejectedFocus ", " rejected target RP/PP ", "rejected RP-PP EQF") old_focus = pinfo.current_focus c_pp, c_rp = pinfo.current_output ot_pp, ot_rp = pinfo.possible_output[old_focus] nt_pp, nt_rp = pinfo.possible_output[RESEARCH] debug( "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f" " | cF: %8s | nF: %8s | cT: %5.1f / %5.1f | %.2f", pid, pinfo.planet.name, c_rp, c_pp, ot_rp, ot_pp, _focus_names.get(old_focus, 'unknown'), _focus_names[RESEARCH], nt_rp, nt_pp, ratio) # RP is getting too expensive, but might be willing to still allocate from a planet with less PP to lose continue focus_manager.bake_future_focus(pid, RESEARCH, False) target_rp += (rr - tr) target_pp -= (ii - ri) # Any planet still raw is set to industry for pid in focus_manager.raw_planet_info.keys(): focus_manager.bake_future_focus(pid, INDUSTRY, False)
def set_planet_industry_and_research_foci(focus_manager, priority_ratio): """Adjust planet's industry versus research focus while targeting the given ratio and avoiding penalties from changing focus.""" print "\n-----------------------------------------" print "Making Planet Focus Change Determinations\n" ratios = [] # for each planet, calculate RP:PP value ratio at which industry/Mining focus and research focus would have the same total value, & sort by that # include a bias to slightly discourage changing foci target_pp = 0.001 target_rp = 0.001 resource_timer.start("Loop") # loop has_force = tech_is_complete("CON_FRC_ENRG_STRC") # cumulative all industry focus cumulative_pp, cumulative_rp = 0, 0 # Handle presets which only have possible output for preset focus for pid, info in focus_manager.baked_planet_info.items(): future_pp, future_rp = info.possible_output[info.future_focus] target_pp += future_pp target_rp += future_rp cumulative_pp += future_pp cumulative_rp += future_rp # tally max Industry for pid, info in focus_manager.raw_planet_info.items(): i_pp, i_rp = info.possible_output[INDUSTRY] cumulative_pp += i_pp cumulative_rp += i_rp if RESEARCH not in info.planet.availableFoci: focus_manager.bake_future_focus(pid, info.current_focus, False) # smallest possible ratio of research to industry with an all industry focus maxi_ratio = cumulative_rp / max(0.01, cumulative_pp) for adj_round in [2, 3, 4]: for pid, info in focus_manager.raw_planet_info.items(): ii, tr = info.possible_output[INDUSTRY] ri, rr = info.possible_output[RESEARCH] ci, cr = info.current_output research_penalty = (info.current_focus != RESEARCH) # calculate factor F at which ii + F * tr == ri + F * rr =====> F = ( ii-ri ) / (rr-tr) factor = (ii - ri) / max(0.01, rr - tr) # don't let denominator be zero for planets where focus doesn't change RP planet = info.planet if adj_round == 2: # take research at planets with very cheap research if (maxi_ratio < priority_ratio) and (target_rp < priority_ratio * cumulative_pp) and (factor <= 1.0): target_pp += ri target_rp += rr focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 3: # take research at planets where can do reasonable balance if has_force or (foAI.foAIstate.aggression < fo.aggression.aggressive) or (target_rp >= priority_ratio * cumulative_pp): continue pop = planet.currentMeterValue(fo.meterType.population) t_pop = planet.currentMeterValue(fo.meterType.targetPopulation) # if AI is aggressive+, and this planet in range where temporary Research focus can get an additional RP at cost of 1 PP, and still need some RP, then do it if pop < t_pop - 5: continue if (ci > ii + 8) or (((rr > ii) or ((rr - cr) >= 1 + 2 * research_penalty)) and ((rr - tr) >= 3) and ((cr - tr) >= 0.7 * ((ii - ci) * (1 + 0.1 * research_penalty)))): target_pp += ci - 1 - research_penalty target_rp += cr + 1 focus_manager.bake_future_focus(pid, RESEARCH, False) continue if adj_round == 4: # assume default IFocus target_pp += ii # icurTargets initially calculated by Industry focus, which will be our default focus target_rp += tr ratios.append((factor, pid, info)) ratios.sort() printed_header = False got_algo = tech_is_complete("LRN_ALGO_ELEGANCE") for ratio, pid, info in ratios: if priority_ratio < (target_rp / (target_pp + 0.0001)): # we have enough RP if ratio < 1.1 and foAI.foAIstate.aggression > fo.aggression.cautious: # but wait, RP is still super cheap relative to PP, maybe will take more RP if priority_ratio < 1.5 * (target_rp / (target_pp + 0.0001)): # yeah, really a glut of RP, stop taking RP break else: # RP not super cheap & we have enough, stop taking it break ii, tr = info.possible_output[INDUSTRY] ri, rr = info.possible_output[RESEARCH] # if focus_manager.current_focus[pid] == MFocus: # ii = max( ii, focus_manager.possible_output[MFocus][0] ) if ((ratio > 2.0 and target_pp < 15 and got_algo) or (ratio > 2.5 and target_pp < 25 and ii > 5 and got_algo) or (ratio > 3.0 and target_pp < 40 and ii > 5 and got_algo) or (ratio > 4.0 and target_pp < 100 and ii > 10) or ((target_rp + rr - tr) / max(0.001, target_pp - ii + ri) > 2 * priority_ratio)): # we already have algo elegance and more RP would be too expensive, or overkill if not printed_header: printed_header = True print "Rejecting further Research Focus choices as too expensive:" print "%34s|%20s|%15s |%15s|%15s |%15s |%15s" % (" Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", " rejectedFocus ", " rejected target RP/PP ", "rejected RP-PP EQF") old_focus = info.current_focus c_pp, c_rp = info.current_output ot_pp, ot_rp = info.possible_output[old_focus] nt_pp, nt_rp = info.possible_output[RESEARCH] print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f | cF: %8s | nF: %8s | cT: %5.1f / %5.1f | %.2f" % (pid, info.planet.name, c_rp, c_pp, ot_rp, ot_pp, _focus_names.get(old_focus, 'unknown'), _focus_names[RESEARCH], nt_rp, nt_pp, ratio) continue # RP is getting too expensive, but might be willing to still allocate from a planet with less PP to lose # if focus_manager.planet_map[pid].currentMeterValue(fo.meterType.targetPopulation) >0: #only set to research if pop won't die out focus_manager.bake_future_focus(pid, RESEARCH, False) target_rp += (rr - tr) target_pp -= (ii - ri) # Any planet in the ratios list and still raw is set to industry for ratio, pid, info in ratios: if pid in focus_manager.raw_planet_info: focus_manager.bake_future_focus(pid, INDUSTRY, False)