1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
import freeOrionAIInterface as fo
import AIDependencies
from buildings import BuildingType
from colonization.colony_score import debug_rating
from freeorion_tools import get_named_real, get_species_industry, tech_soon_available
from freeorion_tools.bonus_calculation import Bonus
from freeorion_tools.caching import cache_for_current_turn
from turn_state import have_honeycomb
def calculate_production(planet: fo.planet, species: fo.species, max_population: float, stability: float) -> float:
"""
Calculate how much PP the planet's population could generate with industry focus.
This only considers values that actually rely on industry focus, those that do not are handled by
calculate_planet_colonization_rating._rate_focus_independent.
"""
if stability <= 0.0:
return 0.0
bonus_modified = _get_production_bonus_modified(planet, stability)
skill_multiplier = get_species_industry(species.name)
bonus_by_policy = _get_production_bonus_mod_by_policy(stability)
policy_multiplier = _get_policy_multiplier(stability)
bonus_unmodified = _get_production_bonus_unmodified(planet, stability)
bonus_flat = _get_production_flat(planet, stability)
per_population = (
(AIDependencies.INDUSTRY_PER_POP + bonus_modified) * skill_multiplier + bonus_by_policy
) * policy_multiplier + bonus_unmodified
result = max_population * per_population + bonus_flat
debug_rating(
f"calculate_production pop={max_population:.2f}, st={stability:.2f}, b1={bonus_modified:.2f}, "
f"m1={skill_multiplier:.2f}, b2={bonus_by_policy:.2f}, m2={policy_multiplier:.2f}, "
f"b3={bonus_unmodified:.2f}, flat={bonus_flat:.2f} -> {result:.2f}"
)
return result
@cache_for_current_turn
def _get_modified_industry_bonuses() -> list[Bonus]:
"""
Get list of per-population bonuses which are added before multiplication with the species skill value.
"""
# TBD check connection!?
have_center = bool(BuildingType.INDUSTRY_CENTER.built_or_queued_at())
centre_bonus1 = get_named_real("BLD_INDUSTRY_CENTER_1_TARGET_INDUSTRY_PERPOP")
centre_bonus2 = get_named_real("BLD_INDUSTRY_CENTER_2_TARGET_INDUSTRY_PERPOP")
centre_bonus3 = get_named_real("BLD_INDUSTRY_CENTER_3_TARGET_INDUSTRY_PERPOP")
return [
Bonus(
tech_soon_available("PRO_FUSION_GEN", 3),
get_named_real("PRO_FUSION_GEN_MIN_STABILITY"),
get_named_real("PRO_FUSION_GEN_TARGET_INDUSTRY_PERPOP"),
),
Bonus(
tech_soon_available("GRO_ENERGY_META", 3),
get_named_real("GRO_ENERGY_META_MIN_STABILITY"),
get_named_real("GRO_ENERGY_META_TARGET_INDUSTRY_PERPOP"),
),
# special case: these are not cumulative
Bonus(
have_center and tech_soon_available("PRO_INDUSTRY_CENTER_III", 1), # expensive research
get_named_real("BLD_INDUSTRY_CENTER_3_MIN_STABILITY"),
centre_bonus3 - centre_bonus2,
),
Bonus(
have_center and tech_soon_available("PRO_INDUSTRY_CENTER_II", 2),
get_named_real("BLD_INDUSTRY_CENTER_2_MIN_STABILITY"),
centre_bonus2 - centre_bonus1,
),
Bonus(
# normally we have the tech when we have the building, but we could have conquered one
have_center and tech_soon_available("PRO_INDUSTRY_CENTER_II", 3),
get_named_real("BLD_INDUSTRY_CENTER_1_MIN_STABILITY"),
centre_bonus1,
),
]
def _get_production_bonus_modified(planet: fo.planet, stability: float) -> float:
"""
Calculate bonus production per population which would be added before multiplication with the species skill value.
"""
specials_bonus = sum(
AIDependencies.INDUSTRY_PER_POP for s in planet.specials if s in AIDependencies.industry_boost_specials_modified
)
return specials_bonus + sum(bonus.get_bonus(stability) for bonus in _get_modified_industry_bonuses())
def _get_policy_multiplier(stability) -> float:
if fo.getEmpire().policyAdopted("INDUSTRIALISM") and stability >= get_named_real("PLC_INDUSTRIALISM_MIN_STABILITY"):
return 1.0 + get_named_real("PLC_INDUSTRIALISM_TARGET_INDUSTRY_PERCENT")
return 1.0
def _get_production_bonus_mod_by_policy(stability: float) -> float:
"""
Calculate bonus production per population which we would get independent of the species production skill,
but still affected by industrialism.
"""
# TBD: check connections?
bonuses = [
Bonus(
bool(BuildingType.BLACK_HOLE_POW_GEN.built_or_queued_at()),
get_named_real("BLD_BLACK_HOLE_POW_GEN_MIN_STABILITY"),
get_named_real("BLD_BLACK_HOLE_POW_GEN_TARGET_INDUSTRY_PERPOP"),
),
]
return sum(bonus.get_bonus(stability) for bonus in bonuses)
def _get_production_bonus_unmodified(planet: fo.planet, stability: float) -> float:
"""
Calculate bonus production per population which we would get independent of the species production skill.
"""
specials_bonus = sum(
# growth.macros: STANDARD_INDUSTRY_BOOST
AIDependencies.INDUSTRY_PER_POP
for s in planet.specials
if s in AIDependencies.industry_boost_specials_unmodified
)
# TBD: check connections?
bonuses = [
Bonus(
bool(BuildingType.SOL_ORB_GEN.built_or_queued_at()), # TBD: check star type
get_named_real("BLD_SOL_ORB_GEN_MIN_STABILITY"),
get_named_real("BLD_SOL_ORB_GEN_BRIGHT_TARGET_INDUSTRY_PERPOP"),
),
Bonus(
have_honeycomb(),
0.0,
get_named_real("HONEYCOMB_TARGET_INDUSTRY_PERPOP"),
),
# TBD: Collective Thought Network? Ignored for now, AI doesn't know how to handle it.
# It shouldn't make a big difference for selecting which planets to colonise anyway.
]
return specials_bonus + sum(bonus.get_bonus(stability) for bonus in bonuses)
def _get_production_flat(planet: fo.planet, stability: float) -> float:
"""
Calculate population independent production bonus.
"""
value = _get_asteroid_and_ggg_value(planet, stability)
# this is a rather expensive one, so consider it only if it is on top of the list
if tech_soon_available(AIDependencies.PRO_AUTO_2, 1):
value += get_named_real("PRO_SENTIENT_AUTO_TARGET_INDUSTRY_FLAT")
return value
def _get_asteroid_and_ggg_value(planet: fo.planet, stability: float) -> float:
"""
Calculate an estimate of the bonus we may get from asteroids (microgravity) and gas giant generators.
"""
universe = fo.getUniverse()
system = universe.getSystem(planet.systemID)
ast_min_stability = get_named_real("PRO_MICROGRAV_MAN_MIN_STABILITY")
count_asteroids = ast_min_stability <= stability and tech_soon_available(AIDependencies.PRO_MICROGRAV_MAN, 5)
ggg_min_stability = get_named_real("BLD_GAS_GIANT_GEN_MIN_STABILITY")
count_ggg = ggg_min_stability <= stability and tech_soon_available(AIDependencies.PRO_ORBITAL_GEN, 5)
asteroid_value = 0.0
ggg_value = 0.0
for pid in system.planetIDs:
p2 = universe.getPlanet(pid)
if p2:
if count_asteroids and p2.size == fo.planetSize.asteroids:
full_bonus = get_named_real("PRO_MICROGRAV_MAN_TARGET_INDUSTRY_FLAT")
if p2.owner == fo.empireID or pid == planet.id:
asteroid_value = full_bonus
# If we cannot see it, we cannot put an outpost on it, although in case of asteroids it likely
# only hidden temporarily.
elif universe.getVisibility(pid, fo.empireID()) and p2.unowned:
asteroid_value = max(asteroid_value, 0.5 * full_bonus)
# TODO prospective_invasion_targets?
if count_ggg:
ggg_value = max(ggg_value, _ggg_value(planet, p2))
debug_rating(
f"_get_asteroid_and_ggg_value amin={ast_min_stability:.1f}->{count_asteroids:.1f}, "
f"gmin={ggg_min_stability:.1f}->{count_ggg:.1f}, aval={asteroid_value:.1f}, gval={ggg_value:.1f}"
)
return asteroid_value + ggg_value
def _ggg_value(planet: fo.planet, p2: fo.planet) -> float:
"""
Check if p2 not planet and is a gas giant. If determines a value for the potential GGG on p2.
"""
universe = fo.getUniverse()
if p2.id != planet.id and p2.size == fo.planetSize.gasGiant:
ggg_colony_flat = get_named_real("BLD_GAS_GIANT_GEN_COLONY_TARGET_INDUSTRY_FLAT")
ggg_outpost_flat = get_named_real("BLD_GAS_GIANT_GEN_OUTPOST_TARGET_INDUSTRY_FLAT")
# species living on a GG do not like a GGG, so we ignore the planet itself here
if p2.owner == fo.empireID():
if not p2.speciesName:
return ggg_outpost_flat
if p2.id in BuildingType.GAS_GIANT_GEN.built_or_queued_at():
return ggg_colony_flat
else:
return 0.4 * ggg_colony_flat # we could build one, but inhabitants won't like it
# If we cannot see it, we cannot put an outpost on it, most likely it is not really unowned
if universe.getVisibility(p2.id, fo.empireID()) and p2.unowned:
return 0.5 * ggg_colony_flat # unowned means we could probably get it easily
# TODO prospective_invasion_targets?
# So far we do not consider owned gas giant for planets here
return 0.0
|