"""Religion system for the Village Simulation. Creates diverse religious beliefs that affect agent behavior: - Each agent has a religion (or atheist) - Faith level affects actions and decisions - Same-religion agents cooperate better - Different religions can create conflict - High faith agents become zealots """ import random from dataclasses import dataclass, field from enum import Enum from typing import Optional class ReligionType(Enum): """Types of religions in the simulation. These represent different belief systems with unique characteristics. """ ATHEIST = "atheist" # No religion - neutral SOLARIS = "solaris" # Sun worshippers - value energy and activity AQUARIUS = "aquarius" # Water worshippers - value water and peace TERRANUS = "terranus" # Earth worshippers - value resources and hoarding IGNIS = "ignis" # Fire worshippers - value heat and trade NATURIS = "naturis" # Nature worshippers - value gathering and sustainability # Religion characteristics RELIGION_TRAITS = { ReligionType.ATHEIST: { "description": "No religious belief", "bonus_action": None, "preferred_resource": None, "aggression": 0.0, "conversion_resistance": 0.3, }, ReligionType.SOLARIS: { "description": "Worshippers of the Sun", "bonus_action": "hunt", # Sun gives strength to hunt "preferred_resource": "meat", "aggression": 0.4, # Moderate aggression "conversion_resistance": 0.6, }, ReligionType.AQUARIUS: { "description": "Worshippers of Water", "bonus_action": "get_water", "preferred_resource": "water", "aggression": 0.1, # Peaceful religion "conversion_resistance": 0.7, }, ReligionType.TERRANUS: { "description": "Worshippers of the Earth", "bonus_action": "gather", "preferred_resource": "berries", "aggression": 0.2, "conversion_resistance": 0.8, }, ReligionType.IGNIS: { "description": "Worshippers of Fire", "bonus_action": "trade", # Fire of commerce "preferred_resource": "wood", "aggression": 0.5, # Hot-tempered "conversion_resistance": 0.5, }, ReligionType.NATURIS: { "description": "Worshippers of Nature", "bonus_action": "gather", "preferred_resource": "berries", "aggression": 0.15, # Peaceful "conversion_resistance": 0.75, }, } @dataclass class ReligiousBeliefs: """An agent's religious beliefs and faith state.""" religion: ReligionType = ReligionType.ATHEIST faith: int = 50 # 0-100, loaded from config # Historical conversion tracking times_converted: int = 0 last_prayer_turn: int = -1 # Zealot state is_zealot: bool = False # Religious influence converts_made: int = 0 sermons_given: int = 0 def __post_init__(self): self._update_zealot_status() def _update_zealot_status(self) -> None: """Update zealot status based on faith level.""" from backend.config import get_config config = get_config() threshold = int(config.religion.zealot_threshold * 100) self.is_zealot = self.faith >= threshold @property def traits(self) -> dict: """Get traits for current religion.""" return RELIGION_TRAITS.get(self.religion, RELIGION_TRAITS[ReligionType.ATHEIST]) @property def description(self) -> str: """Get religion description.""" return self.traits["description"] @property def is_religious(self) -> bool: """Check if agent has a religion.""" return self.religion != ReligionType.ATHEIST @property def conversion_resistance(self) -> float: """Get resistance to conversion.""" base = self.traits.get("conversion_resistance", 0.5) # Higher faith = harder to convert faith_modifier = self.faith / 100 * 0.3 return min(0.95, base + faith_modifier) def gain_faith(self, amount: int) -> None: """Increase faith level.""" self.faith = min(100, self.faith + amount) self._update_zealot_status() def lose_faith(self, amount: int) -> None: """Decrease faith level.""" self.faith = max(0, self.faith - amount) self._update_zealot_status() def apply_decay(self) -> None: """Apply faith decay per turn (if not recently prayed).""" from backend.config import get_config decay = get_config().agent_stats.faith_decay self.faith = max(0, self.faith - decay) self._update_zealot_status() def convert_to(self, new_religion: ReligionType, faith_level: int = 30) -> bool: """Attempt to convert to a new religion.""" if new_religion == self.religion: return False # Check conversion resistance if random.random() < self.conversion_resistance: return False self.religion = new_religion self.faith = faith_level self.times_converted += 1 self._update_zealot_status() return True def record_prayer(self, turn: int) -> None: """Record that prayer was performed.""" self.last_prayer_turn = turn def record_conversion(self) -> None: """Record a successful conversion made.""" self.converts_made += 1 def record_sermon(self) -> None: """Record a sermon given.""" self.sermons_given += 1 def get_trade_modifier(self, other: "ReligiousBeliefs") -> float: """Get trade modifier when dealing with another agent's religion.""" from backend.config import get_config config = get_config() if self.religion == ReligionType.ATHEIST or other.religion == ReligionType.ATHEIST: return 1.0 # No modifier for atheists if self.religion == other.religion: # Same religion bonus bonus = config.religion.same_religion_bonus # Zealots get extra bonus if self.is_zealot and other.is_zealot: bonus *= 1.5 return 1.0 + bonus else: # Different religion penalty penalty = config.religion.different_religion_penalty # Zealots are more hostile to other religions if self.is_zealot: penalty *= 1.5 return 1.0 - penalty def should_convert_other(self, other: "ReligiousBeliefs") -> bool: """Check if agent should try to convert another agent.""" if not self.is_religious: return False if self.religion == other.religion: return False # Zealots always want to convert if self.is_zealot: return True # High faith agents sometimes want to convert return random.random() < (self.faith / 100) * 0.5 def is_hostile_to(self, other: "ReligiousBeliefs") -> bool: """Check if religiously hostile to another agent.""" if not self.is_religious or not other.is_religious: return False if self.religion == other.religion: return False from backend.config import get_config config = get_config() # Only zealots are hostile if not self.is_zealot: return False # Check if faith is high enough for holy war if self.faith >= config.religion.holy_war_threshold * 100: return True return random.random() < self.traits.get("aggression", 0.0) def to_dict(self) -> dict: """Convert to dictionary for serialization.""" return { "religion": self.religion.value, "faith": self.faith, "is_zealot": self.is_zealot, "times_converted": self.times_converted, "converts_made": self.converts_made, "description": self.description, } def generate_random_religion(archetype: Optional[str] = None) -> ReligiousBeliefs: """Generate random religious beliefs for an agent. Args: archetype: Optional personality archetype that influences religion """ from backend.config import get_config config = get_config() # Get available religions religions = list(ReligionType) # Weight by archetype weights = [1.0] * len(religions) if archetype == "hunter": # Hunters favor Solaris (sun/strength) weights[religions.index(ReligionType.SOLARIS)] = 3.0 weights[religions.index(ReligionType.IGNIS)] = 2.0 elif archetype == "gatherer": # Gatherers favor Naturis/Terranus weights[religions.index(ReligionType.NATURIS)] = 3.0 weights[religions.index(ReligionType.TERRANUS)] = 2.0 elif archetype == "trader": # Traders favor Ignis (commerce) weights[religions.index(ReligionType.IGNIS)] = 3.0 weights[religions.index(ReligionType.AQUARIUS)] = 2.0 # Water trade routes elif archetype == "woodcutter": weights[religions.index(ReligionType.TERRANUS)] = 2.0 weights[religions.index(ReligionType.NATURIS)] = 1.5 # Atheists are uncommon - lower base weight weights[religions.index(ReligionType.ATHEIST)] = 0.2 # Weighted random selection total = sum(weights) r = random.random() * total cumulative = 0 chosen_religion = ReligionType.ATHEIST for i, (religion, weight) in enumerate(zip(religions, weights)): cumulative += weight if r <= cumulative: chosen_religion = religion break # Starting faith varies if chosen_religion == ReligionType.ATHEIST: starting_faith = random.randint(0, 20) else: starting_faith = random.randint(30, 70) return ReligiousBeliefs( religion=chosen_religion, faith=starting_faith, ) def get_religion_compatibility(religion1: ReligionType, religion2: ReligionType) -> float: """Get compatibility score between two religions (0-1).""" if religion1 == religion2: return 1.0 if religion1 == ReligionType.ATHEIST or religion2 == ReligionType.ATHEIST: return 0.7 # Atheists are neutral # Compatible pairs compatible_pairs = [ (ReligionType.NATURIS, ReligionType.AQUARIUS), # Nature and water (ReligionType.TERRANUS, ReligionType.NATURIS), # Earth and nature (ReligionType.SOLARIS, ReligionType.IGNIS), # Sun and fire ] # Hostile pairs hostile_pairs = [ (ReligionType.AQUARIUS, ReligionType.IGNIS), # Water vs fire (ReligionType.SOLARIS, ReligionType.AQUARIUS), # Sun vs water ] pair = (religion1, religion2) reverse_pair = (religion2, religion1) if pair in compatible_pairs or reverse_pair in compatible_pairs: return 0.8 if pair in hostile_pairs or reverse_pair in hostile_pairs: return 0.3 return 0.5 # Neutral def get_religion_action_bonus(religion: ReligionType, action_type: str) -> float: """Get action bonus/penalty for a religion performing an action.""" traits = RELIGION_TRAITS.get(religion, {}) bonus_action = traits.get("bonus_action") if bonus_action == action_type: return 1.15 # 15% bonus for favored action return 1.0