from typing import List, Dict from warchron.constants import ContextType from warchron.model.exception import ForbiddenOperation from warchron.model.war import War from warchron.model.round import Round from warchron.model.battle import Battle from warchron.model.war_event import InfluenceSpent, TieResolved class TieResolver: @staticmethod def find_round_ties(round: Round, war: War) -> List[Battle]: ties = [] for battle in round.battles.values(): if not battle.is_draw(): continue resolved = any( isinstance(e, TieResolved) and e.context_type == ContextType.BATTLE and e.context_id == battle.sector_id for e in war.events ) if not resolved: ties.append(battle) return ties @staticmethod def apply_bids( war: War, context_type: ContextType, context_id: str, bids: Dict[str, bool], # war_participant_id -> spend? ) -> None: for war_part_id, spend in bids.items(): if not spend: continue if war.get_influence_tokens(war_part_id) < 1: raise ForbiddenOperation("Not enough tokens") war.events.append( InfluenceSpent( participant_id=war_part_id, amount=1, context_type=context_type, ) ) @staticmethod def try_tie_break( war: War, context_type: ContextType, context_id: str, participants: List[str], # war_participant_ids ) -> bool: spent: Dict[str, int] = {} for war_part_id in participants: spent[war_part_id] = sum( e.amount for e in war.events if isinstance(e, InfluenceSpent) and e.participant_id == war_part_id and e.context_type == context_type ) values = set(spent.values()) if values == {0}: # no bid = confirmed draw war.events.append( TieResolved( participant_id=None, context_type=context_type, context_id=context_id, ) ) return True if len(values) == 1: # tie again, continue return False winner = max(spent.items(), key=lambda item: item[1])[0] war.events.append( TieResolved( participant_id=winner, context_type=context_type, context_id=context_id, ) ) return True @staticmethod def can_tie_be_resolved(war: War, participants: List[str]) -> bool: return any(war.get_influence_tokens(pid) > 0 for pid in participants) @staticmethod def get_effective_winner_id( war: War, context_type: ContextType, context_id: str, base_winner_id: str | None, ) -> str | None: if base_winner_id is not None: return base_winner_id for ev in reversed(war.events): if ( isinstance(ev, TieResolved) and ev.context_type == context_type and ev.context_id == context_id ): return ev.participant_id # None if confirmed draw return None