Beispiel #1
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:
        print "Invasion AI couldn't access any info for planet id %d" % planet_id
        return [0, 0]

    system_id = planet.systemID

    # by using the following instead of simply relying on stealth meter reading, can (sometimes) plan ahead even if
    # planet is temporarily shrouded by an ion storm
    predicted_detectable = EspionageAI.colony_detectable_by_empire(
        planet_id, empire_id=fo.empireID(), default_result=False)
    if not predicted_detectable:
        if get_partial_visibility_turn(planet_id) < fo.currentTurn():
            debug("InvasionAI predicts planet id %d to be stealthed" %
                  planet_id)
            return [0, 0]
        else:
            debug(
                "InvasionAI predicts planet id %d to be stealthed" %
                planet_id +
                ", but somehow have current visibity anyway, will still consider as target"
            )

    # Check if the target planet was extra-stealthed somehow its system was last viewed
    # this test below may augment the tests above, but can be thrown off by temporary combat-related sighting
    system_last_seen = get_partial_visibility_turn(planet_id)
    planet_last_seen = get_partial_visibility_turn(system_id)
    if planet_last_seen < system_last_seen:
        # TODO: track detection strength, order new scouting when it goes up
        debug(
            "Invasion AI considering planet id %d (stealthed at last view), still proceeding."
            % planet_id)

    # get a baseline evaluation of the planet as determined by ColonisationAI
    species_name = planet.speciesName
    species = fo.getSpecies(species_name)
    if not species or AIDependencies.TAG_DESTROYED_ON_CONQUEST in species.tags:
        # this call iterates over this Empire's available species with which it could colonize after an invasion
        planet_eval = ColonisationAI.assign_colonisation_values(
            [planet_id], MissionType.INVASION, None, detail)
        colony_base_value = max(
            0.75 * planet_eval.get(planet_id, [0])[0],
            ColonisationAI.evaluate_planet(planet_id, MissionType.OUTPOST,
                                           None, detail))
    else:
        colony_base_value = ColonisationAI.evaluate_planet(
            planet_id, MissionType.INVASION, species_name, detail)

    # Add extra score for all buildings on the planet
    building_values = {
        "BLD_IMPERIAL_PALACE": 1000,
        "BLD_CULTURE_ARCHIVES": 1000,
        "BLD_AUTO_HISTORY_ANALYSER": 100,
        "BLD_SHIPYARD_BASE": 100,
        "BLD_SHIPYARD_ORG_ORB_INC": 200,
        "BLD_SHIPYARD_ORG_XENO_FAC": 200,
        "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200,
        "BLD_SHIPYARD_CON_NANOROBO": 300,
        "BLD_SHIPYARD_CON_GEOINT": 400,
        "BLD_SHIPYARD_CON_ADV_ENGINE": 1000,
        "BLD_SHIPYARD_AST": 300,
        "BLD_SHIPYARD_AST_REF": 1000,
        "BLD_SHIPYARD_ENRG_SOLAR": 1500,
        "BLD_INDUSTRY_CENTER": 500,
        "BLD_GAS_GIANT_GEN": 200,
        "BLD_SOL_ORB_GEN": 800,
        "BLD_BLACK_HOLE_POW_GEN": 2000,
        "BLD_ENCLAVE_VOID": 500,
        "BLD_NEUTRONIUM_EXTRACTOR": 2000,
        "BLD_NEUTRONIUM_SYNTH": 2000,
        "BLD_NEUTRONIUM_FORGE": 1000,
        "BLD_CONC_CAMP": 100,
        "BLD_BIOTERROR_PROJECTOR": 1000,
        "BLD_SHIPYARD_ENRG_COMP": 3000,
    }
    bld_tally = 0
    for bldType in [
            universe.getBuilding(bldg).buildingTypeName
            for bldg in planet.buildingIDs
    ]:
        bval = building_values.get(bldType, 50)
        bld_tally += bval
        detail.append("%s: %d" % (bldType, bval))

    # Add extra score for unlocked techs when we conquer the species
    tech_tally = 0
    value_per_pp = 4
    for unlocked_tech in AIDependencies.SPECIES_TECH_UNLOCKS.get(
            species_name, []):
        if not tech_is_complete(unlocked_tech):
            rp_cost = fo.getTech(unlocked_tech).researchCost(empire_id)
            tech_value = value_per_pp * rp_cost
            tech_tally += tech_value
            detail.append("%s: %d" % (unlocked_tech, tech_value))

    max_jumps = 8
    capitol_id = PlanetUtilsAI.get_capital()
    least_jumps_path = []
    clear_path = True
    if capitol_id:
        homeworld = universe.getPlanet(capitol_id)
        if homeworld and homeworld.systemID != INVALID_ID and system_id != INVALID_ID:
            least_jumps_path = list(
                universe.leastJumpsPath(homeworld.systemID, system_id,
                                        empire_id))
            max_jumps = len(least_jumps_path)
    system_status = foAI.foAIstate.systemStatus.get(system_id, {})
    system_fleet_treat = system_status.get('fleetThreat', 1000)
    system_monster_threat = system_status.get('monsterThreat', 0)
    sys_total_threat = system_fleet_treat + system_monster_threat + system_status.get(
        'planetThreat', 0)
    max_path_threat = system_fleet_treat
    mil_ship_rating = MilitaryAI.cur_best_mil_ship_rating()
    for path_sys_id in least_jumps_path:
        path_leg_status = foAI.foAIstate.systemStatus.get(path_sys_id, {})
        path_leg_threat = path_leg_status.get(
            'fleetThreat', 1000) + path_leg_status.get('monsterThreat', 0)
        if path_leg_threat > 0.5 * mil_ship_rating:
            clear_path = False
            if path_leg_threat > max_path_threat:
                max_path_threat = path_leg_threat

    pop = planet.currentMeterValue(fo.meterType.population)
    target_pop = planet.currentMeterValue(fo.meterType.targetPopulation)
    troops = planet.currentMeterValue(fo.meterType.troops)
    troop_regen = planet.currentMeterValue(
        fo.meterType.troops) - planet.initialMeterValue(fo.meterType.troops)
    max_troops = planet.currentMeterValue(fo.meterType.maxTroops)
    # TODO: refactor troop determination into function for use in mid-mission updates and also consider defender techs
    max_troops += AIDependencies.TROOPS_PER_POP * (target_pop - pop)

    this_system = universe.getSystem(system_id)
    secure_targets = [system_id] + list(this_system.planetIDs)
    system_secured = False
    for mission in secure_fleet_missions:
        if system_secured:
            break
        secure_fleet_id = mission.fleet.id
        s_fleet = universe.getFleet(secure_fleet_id)
        if not s_fleet or s_fleet.systemID != system_id:
            continue
        if mission.type == MissionType.SECURE:
            target_obj = mission.target.get_object()
            if target_obj is not None and target_obj.id in secure_targets:
                system_secured = True
                break
    system_secured = system_secured and system_status.get('myFleetRating', 0)

    if verbose:
        print("Invasion eval of %s\n"
              " - maxShields: %.1f\n"
              " - sysFleetThreat: %.1f\n"
              " - sysMonsterThreat: %.1f") % (
                  planet, planet.currentMeterValue(fo.meterType.maxShield),
                  system_fleet_treat, system_monster_threat)
    enemy_val = 0
    if planet.owner != -1:  # value in taking this away from an enemy
        enemy_val = 20 * (
            planet.currentMeterValue(fo.meterType.targetIndustry) +
            2 * planet.currentMeterValue(fo.meterType.targetResearch))

    # devalue invasions that would require too much military force
    preferred_max_portion = MilitaryAI.get_preferred_max_military_portion_for_single_battle(
    )
    total_max_mil_rating = MilitaryAI.get_concentrated_tot_mil_rating()
    threat_exponent = 2  # TODO: make this a character trait; higher aggression with a lower exponent
    threat_factor = min(
        1, preferred_max_portion * total_max_mil_rating /
        (sys_total_threat + 0.001))**threat_exponent

    design_id, _, locs = ProductionAI.get_best_ship_info(
        PriorityType.PRODUCTION_INVASION)
    if not locs or not universe.getPlanet(locs[0]):
        # We are in trouble anyway, so just calculate whatever approximation...
        build_time = 4
        planned_troops = troops if system_secured else min(
            troops + troop_regen * (max_jumps + build_time), max_troops)
        planned_troops += .01  # we must attack with more troops than there are defenders
        troop_cost = math.ceil((planned_troops + _TROOPS_SAFETY_MARGIN) /
                               6.0) * 20 * FleetUtilsAI.get_fleet_upkeep()
    else:
        loc = locs[0]
        species_here = universe.getPlanet(loc).speciesName
        design = fo.getShipDesign(design_id)
        cost_per_ship = design.productionCost(empire_id, loc)
        build_time = design.productionTime(empire_id, loc)
        troops_per_ship = CombatRatingsAI.weight_attack_troops(
            design.troopCapacity,
            CombatRatingsAI.get_species_troops_grade(species_here))
        planned_troops = troops if system_secured else min(
            troops + troop_regen * (max_jumps + build_time), max_troops)
        planned_troops += .01  # we must attack with more troops than there are defenders
        ships_needed = math.ceil(
            (planned_troops + _TROOPS_SAFETY_MARGIN) / float(troops_per_ship))
        troop_cost = ships_needed * cost_per_ship  # fleet upkeep is already included in query from server

    # apply some bias to expensive operations
    normalized_cost = float(troop_cost) / max(fo.getEmpire().productionPoints,
                                              1)
    normalized_cost = max(1., normalized_cost)
    cost_score = (normalized_cost**2 / 50.0) * troop_cost

    base_score = colony_base_value + bld_tally + tech_tally + enemy_val - cost_score
    # If the AI does have enough total miltary to attack this target, and the target is more than minimally valuable,
    # don't let the threat_factor discount the adjusted value below MIN_INVASION_SCORE +1, so that if there are no
    # other targets the AI could still pursue this one.  Otherwise, scoring pressure from
    # MilitaryAI.get_preferred_max_military_portion_for_single_battle might prevent the AI from attacking heavily
    # defended but still defeatable targets even if it has no softer targets available.
    if total_max_mil_rating > sys_total_threat and base_score > 2 * MIN_INVASION_SCORE:
        threat_factor = max(threat_factor,
                            (MIN_INVASION_SCORE + 1) / base_score)
    planet_score = retaliation_risk_factor(planet.owner) * threat_factor * max(
        0, base_score)
    if clear_path:
        planet_score *= 1.5
    if verbose:
        print(' - planet score: %.2f\n'
              ' - planned troops: %.2f\n'
              ' - projected troop cost: %.1f\n'
              ' - threat factor: %s\n'
              ' - planet detail: %s\n'
              ' - popval: %.1f\n'
              ' - bldval: %s\n'
              ' - enemyval: %s') % (planet_score, planned_troops, troop_cost,
                                    threat_factor, detail, colony_base_value,
                                    bld_tally, enemy_val)
    return [planet_score, planned_troops]
Beispiel #2
0
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]
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
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]
Beispiel #6
0
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]