Example #1
0
def dislike_factor() -> float:
    """Returns multiplier for dislike effects."""
    # See happiness.macros
    has_liberty = fo.getEmpire().policyAdopted("PLC_LIBERTY")
    # conformance not used yet
    return fo.getNamedValue(
        "PLC_LIBERTY_DISLIKE_FACTOR") if has_liberty else 1.0
def _evaluate_policies(species: fo.species) -> float:
    empire = fo.getEmpire()
    like_value = AIDependencies.STABILITY_PER_LIKED_FOCUS
    dislike_value = like_value * dislike_factor()
    result = sum(like_value for p in empire.adoptedPolicies
                 if p in species.likes)
    result -= sum(dislike_value for p in empire.adoptedPolicies
                  if p in species.dislikes)
    if PolicyAI.bureaucracy in empire.adoptedPolicies:
        result += fo.getNamedValue("PLC_BUREAUCRACY_STABILITY_FLAT")
    # TBD: add conformance, indoctrination, etc. when the AI learns to use them
    return result
Example #3
0
 def _rate_diversity(self) -> float:
     """Rate diversity."""
     if diversity not in self._empire.availablePolicies:
         return 0.0
     # diversity affects stability, but also gives a bonus to research-focused planets and a little influence,
     diversity_value = len(get_empire_planets_by_species()
                           ) - fo.getNamedValue("PLC_DIVERSITY_THRESHOLD")
     diversity_scaling = fo.getNamedValue("PLC_DIVERSITY_SCALING")
     # Research bonus goes to research-focused planets only. With priority there are usually none.
     research_priority = self._aistate.get_priority(
         PriorityType.RESOURCE_RESEARCH)
     research_bonus = max(0, research_priority - 20)
     # + 4 for global influence
     global_influence_bonus = 4
     rating = self._rate_opinion(
         diversity) + diversity_scaling * diversity_value * (
             self._num_populated + global_influence_bonus + research_bonus)
     debug(
         f"_rate_diversity: rating={rating}. diversity_value={diversity_value}, "
         f"research priority={research_priority}")
     return rating
def _evaluate_xenophobia(planet, species) -> float:
    if AIDependencies.Tags.XENOPHOBIC not in species.tags:
        return 0.0
    colonies = get_colonized_planets()
    relevant_systems = within_n_jumps(planet.systemID, 5) & set(
        colonies.keys())
    result = 0.0
    universe = fo.getUniverse()
    value = fo.getNamedValue("XENOPHOBIC_SELF_TARGET_HAPPINESS_COUNT")
    for sys_id in relevant_systems:
        for pid in colonies[sys_id]:
            planet_species = universe.getPlanet(pid).speciesName
            if planet_species not in ("SP_EXOBOT", species.name, ""):
                result += value  # value is negative
    return result
Example #5
0
def get_named_real(name: str) -> float:
    """
    Returns a NamedReal from FOCS.
    If the value does not exist, reports an error and returns 1.0.
    Note that we do not raise and exception so that the AI can continue, as good as it can, with outdated information.
    This is also why we return 1, returning 0 could cause followup errors if the value is used as divisor.
    """
    value = fo.getNamedValue(name)
    if value is None:
        error(f"Requested NamedReal {name}, which doesn't exist!")
        value = 1.0
    elif not isinstance(value, float):
        error(f"Requested value {name} of type float got {type(value)}!")
        value = 1.0
    return value
Example #6
0
    def _rate_artisan_planet(self, pid: PlanetId,
                             species_name: SpeciesName) -> float:
        focus_bonus = fo.getNamedValue("ARTISANS_INFLUENCE_FLAT_FOCUS")
        focus_minimum = fo.getNamedValue("ARTISANS_MIN_STABILITY_FOCUS")
        species_focus_bonus = focus_bonus * get_species_tag_value(
            species_name, Tags.INFLUENCE)
        planet = self._universe.getPlanet(pid)
        stability = planet.currentMeterValue(fo.meterType.targetHappiness)
        # First check whether the planet would currently get the focus bonus.
        if planet.focus == FocusType.FOCUS_INFLUENCE:
            return 3 * species_focus_bonus if stability >= focus_minimum else 0.0

        # Planet does not have influence focus...
        # Check for the non-focus bonus. Since we would get this "for free", rate it higher
        non_focus_bonus = fo.getNamedValue("ARTISANS_INFLUENCE_FLAT_NO_FOCUS")
        non_focus_minimum = fo.getNamedValue("ARTISANS_MIN_STABILITY_NO_FOCUS")
        rating = 0.0
        if stability >= non_focus_minimum:
            rating += 4 * non_focus_bonus
        # Check whether this planet would get the focus bonus, if we'd switch it to influence.
        if PlanetUtilsAI.stability_with_focus(
                planet, FocusType.FOCUS_INFLUENCE) >= focus_minimum:
            rating += species_focus_bonus
        return rating
Example #7
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