parameters: (planet_id)
return_statement: return [planet_score, planned_troops]
docstring: Return the invasion value (score, troops) of a planet.
docstring_summary: Return the invasion value (score, troops) of a planet.
docstring_tokens: Return the invasion value ( score troops ) of a planet .
function: 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]
function_tokens: def evaluate_invasion_planet ( planet_id ) : 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 ]