"""Stochastic quarterly events that create cross-quarter cascading effects.""" from __future__ import annotations import random from dataclasses import dataclass from typing import List, Optional from hr_env.server.company import Company @dataclass class Event: """A stochastic event that affects the company.""" name: str description: str probability: float affected_department: Optional[str] = None # None = company-wide def apply(self, company: Company, rng: random.Random) -> str: """Apply the event's effects. Returns description of what happened.""" raise NotImplementedError class MarketDownturn(Event): def __init__(self): super().__init__( name="market_downturn", description="Economic downturn reduces market demand", probability=0.10, ) def apply(self, company: Company, rng: random.Random) -> str: company.market_modifier *= 0.88 # Revenue -12% # Increase flight risk for top performers (they have options) for emp in company.all_active_employees: if emp.performance_score >= 4.0: emp.flight_risk = min(0.95, emp.flight_risk + 0.05) return "Market downturn: revenue reduced by 12%. Top performer flight risk increased." class CompetitorPoaching(Event): def __init__(self): super().__init__( name="competitor_poaching", description="Competitor aggressively recruiting from one department", probability=0.12, ) def apply(self, company: Company, rng: random.Random) -> str: dept_name = rng.choice(list(company.departments.keys())) self.affected_department = dept_name dept = company.departments[dept_name] for emp in dept.active_employees: emp.flight_risk = min(0.95, emp.flight_risk + 0.25) emp.update_engagement(-5) return f"Competitor poaching in {dept_name}: flight risk +25% for all department employees." class ProductLaunch(Event): def __init__(self): super().__init__( name="product_launch", description="Major product launch increases workload", probability=0.10, ) def apply(self, company: Company, rng: random.Random) -> str: for dept_name in ["Engineering", "Sales"]: dept = company.departments.get(dept_name) if dept: for emp in dept.active_employees: emp.update_engagement(-8) # Stress from overwork # But productivity boost emp.performance_score = min(5.0, emp.performance_score + 0.1) company.market_modifier *= 1.08 # Revenue boost from launch return "Product launch: Engineering/Sales workload +25%, engagement decreased, but revenue +8%." class ExecDeparture(Event): def __init__(self): super().__init__( name="exec_departure", description="Senior executive departs suddenly", probability=0.07, ) def apply(self, company: Company, rng: random.Random) -> str: dept_name = rng.choice(list(company.departments.keys())) self.affected_department = dept_name dept = company.departments[dept_name] # Remove highest-level employee seniors = sorted(dept.active_employees, key=lambda e: (-e.level, -e.tenure_months)) if seniors: seniors[0].is_active = False # Leadership gap: engagement drops for emp in dept.active_employees: emp.update_engagement(-12) return f"Executive departure in {dept_name}: leadership gap, engagement -12% department-wide." class MinWageIncrease(Event): def __init__(self): super().__init__( name="min_wage_increase", description="Regulatory minimum wage increase", probability=0.06, ) def apply(self, company: Company, rng: random.Random) -> str: floor_increase = 0.06 affected = 0 for emp in company.all_active_employees: if emp.salary < 50000: emp.salary *= (1 + floor_increase) affected += 1 return f"Minimum wage increase: {affected} employees received 6% salary floor adjustment." class EmployerAward(Event): def __init__(self): super().__init__( name="employer_award", description="Company wins employer award, boosting brand", probability=0.06, ) def apply(self, company: Company, rng: random.Random) -> str: # Boost engagement and reduce flight risk for emp in company.all_active_employees: emp.update_engagement(5) emp.flight_risk = max(0.02, emp.flight_risk - 0.05) return "Employer award: company brand boost, engagement +5, flight risk reduced." class BudgetCut(Event): def __init__(self): super().__init__( name="budget_cut", description="Corporate budget reduction for HR", probability=0.08, ) def apply(self, company: Company, rng: random.Random) -> str: reduction = company.hr_budget_remaining * 0.20 company.hr_budget_remaining -= reduction return f"Budget cut: HR budget reduced by 20% (${reduction:,.0f} lost)." class TrainingBreakthrough(Event): def __init__(self): super().__init__( name="training_breakthrough", description="Training program yields exceptional results in one department", probability=0.05, ) def apply(self, company: Company, rng: random.Random) -> str: dept_name = rng.choice(list(company.departments.keys())) self.affected_department = dept_name dept = company.departments[dept_name] for emp in dept.active_employees: emp.performance_score = min(5.0, emp.performance_score + 0.3) emp.update_engagement(5) return f"Training breakthrough in {dept_name}: productivity +8%, performance improved." ALL_EVENTS: List[Event] = [ MarketDownturn(), CompetitorPoaching(), ProductLaunch(), ExecDeparture(), MinWageIncrease(), EmployerAward(), BudgetCut(), TrainingBreakthrough(), ] def draw_quarterly_events(rng: random.Random, max_events: int = 2) -> List[Event]: """Draw 0-2 random events for a quarter based on probabilities.""" triggered = [] for event in ALL_EVENTS: if rng.random() < event.probability: triggered.append(event) # Cap at max_events if len(triggered) > max_events: triggered = rng.sample(triggered, max_events) return triggered def apply_events(company: Company, rng: random.Random) -> List[str]: """Draw and apply quarterly events. Returns list of event descriptions.""" events = draw_quarterly_events(rng) descriptions = [] for event in events: desc = event.apply(company, rng) descriptions.append(desc) return descriptions