Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
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
Пример #4
0
 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,
     ])
Пример #5
0
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
Пример #6
0
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
Пример #7
0
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
Пример #8
0
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
Пример #9
0
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
Пример #10
0
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
Пример #11
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
Пример #12
0
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
Пример #13
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)
        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:"
Пример #14
0
def get_completed_techs():
    """Get completed and available for use techs."""
    return [tech for tech in fo.techs() if tech_is_complete(tech)]
Пример #15
0
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)
Пример #16
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:"
Пример #17
0
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
Пример #18
0
 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])
Пример #19
0
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)
Пример #20
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]
Пример #21
0
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))
Пример #22
0
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))
Пример #23
0
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
Пример #24
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]
Пример #25
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
Пример #26
0
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
Пример #27
0
def get_completed_techs():
    """get completed and available for use techs"""
    return [tech for tech in fo.techs() if tech_is_complete(tech)]
Пример #28
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."""
    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)
Пример #29
0
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
Пример #30
0
def get_supply_tech_range():
    return sum(_range for _tech, _range in AIDependencies.supply_range_techs.items() if tech_is_complete(_tech))
Пример #31
0
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)
Пример #32
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
Пример #33
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]
Пример #34
0
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
Пример #35
0
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)
Пример #36
0
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
Пример #37
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."""
    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)
Пример #38
0
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
Пример #39
0
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]
Пример #40
0
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)
Пример #41
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)
Пример #42
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."""
    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)