Source code for babylon.formulas.state_ai

"""State AI formulas (Feature 039).

Provides the faction shift calculation that adjusts FactionBalance weights
based on player heat and events. Heat drives Security-State weight upward;
dropping heat allows reversion toward Finance-Capital dominance.

See Also:
    ``specs/039-state-apparatus-ai/contracts/faction-balance.md``: F-01 through F-05.
    :class:`babylon.config.defines.StateApparatusAIDefines`: Shift limits.
"""

from __future__ import annotations

from babylon.config.defines import StateApparatusAIDefines
from babylon.models.entities.state_apparatus_ai import FactionBalance


[docs] def calculate_faction_shift( heat: float, current_balance: FactionBalance, defines: StateApparatusAIDefines, ) -> FactionBalance: """Calculate faction balance shift driven by player heat. Higher heat increases Security-State weight at the expense of Finance-Capital and Settler-Populist. The shift per tick is clamped to ``defines.max_faction_shift_per_tick``. When heat is low (< 0.3), a gradual reversion toward the default equilibrium occurs. Args: heat: Player threat level [0.0, 1.0]. current_balance: Current FactionBalance. defines: State AI configuration with shift limits. Returns: New FactionBalance with shifted weights. """ max_shift = defines.max_faction_shift_per_tick fc = current_balance.finance_capital ss = current_balance.security_state sp = current_balance.settler_populist if heat > 0.5: # High heat: Security-State gains at expense of others shift_amount = min(max_shift, (heat - 0.5) * 0.2) ss_new = min(1.0, ss + shift_amount) # Distribute loss proportionally between FC and SP loss = ss_new - ss fc_share = fc / (fc + sp) if (fc + sp) > 0 else 0.5 fc_new = max(0.0, fc - loss * fc_share) sp_new = max(0.0, sp - loss * (1.0 - fc_share)) elif heat < 0.3: # Low heat: gradual reversion toward equilibrium shift_amount = min(max_shift, (0.3 - heat) * 0.1) ss_new = max(0.0, ss - shift_amount * 0.5) gain = ss - ss_new fc_new = fc + gain * 0.6 # FC recovers faster sp_new = sp + gain * 0.4 else: # Moderate heat: no shift return current_balance # Normalize to sum to 1.0 total = fc_new + ss_new + sp_new if total > 0: fc_new /= total ss_new /= total sp_new /= total else: fc_new = 1.0 / 3.0 ss_new = 1.0 / 3.0 sp_new = 1.0 / 3.0 return FactionBalance( finance_capital=round(fc_new, 6), security_state=round(ss_new, 6), settler_populist=round(sp_new, 6), stability=current_balance.stability, legitimacy=current_balance.legitimacy, )
[docs] def is_fascist_convergence( balance: FactionBalance, settler_ci: float, consecutive_ticks: int, defines: StateApparatusAIDefines, ) -> bool: """Detect fascist convergence using the three-pillar model. All three conditions must hold simultaneously for at least ``convergence_confirmation_ticks`` consecutive ticks: 1. Security-State dominance: SS > fascist_security_threshold (0.4) 2. Settler-Populist mass base: settler CI > fascist_settler_ci_threshold (0.6) 3. Finance-Capital acquiescence: FC < fascist_finance_ceiling (0.25) Args: balance: Current FactionBalance. settler_ci: Settler collective identity level [0.0, 1.0]. consecutive_ticks: Number of consecutive ticks conditions have held. defines: State AI configuration with fascist thresholds. Returns: True if fascist convergence is confirmed. """ # Strict inequalities — values exactly at thresholds do not qualify ss_pillar = balance.security_state > defines.fascist_security_threshold ci_pillar = settler_ci > defines.fascist_settler_ci_threshold fc_pillar = balance.finance_capital < defines.fascist_finance_ceiling if not (ss_pillar and ci_pillar and fc_pillar): return False # Confirmation window: conditions must hold for enough consecutive ticks return consecutive_ticks >= defines.convergence_confirmation_ticks
[docs] def check_fascist_reversion( balance: FactionBalance, settler_ci: float, defines: StateApparatusAIDefines, ) -> bool: """Check whether conditions are met to exit fascist mode. Fascist mode is a near-absorbing state with asymmetric exit thresholds that are substantially harder to reach than entry thresholds: - Entry: SS > 0.4, settler CI > 0.6, FC < 0.25 - Exit: SS < 0.25 AND settler CI < 0.30 Both exit conditions must hold simultaneously. Args: balance: Current FactionBalance. settler_ci: Settler collective identity level [0.0, 1.0]. defines: State AI configuration with reversion thresholds. Returns: True if fascist mode should be exited (reversion achieved). """ # Strict inequalities — values exactly at thresholds do not qualify ss_below = balance.security_state < defines.reversion_ss_threshold ci_below = settler_ci < defines.reversion_ci_threshold return ss_below and ci_below
__all__ = [ "calculate_faction_shift", "check_fascist_reversion", "is_fascist_convergence", ]