from __future__ import annotations from typing import List, Tuple, Dict, TYPE_CHECKING from collections import defaultdict from warchron.constants import ContextType from warchron.model.war import War from warchron.model.war_event import TieResolved from warchron.model.tie_manager import TieResolver if TYPE_CHECKING: from warchron.model.score_service import ParticipantScore class ResultChecker: @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 @staticmethod def get_effective_ranking( war: War, context_type: ContextType, context_id: str, scores: Dict[str, ParticipantScore], ) -> List[Tuple[int, List[str]]]: vp_buckets: Dict[int, List[str]] = defaultdict(list) for pid, score in scores.items(): vp_buckets[score.victory_points].append(pid) sorted_vps = sorted(vp_buckets.keys(), reverse=True) ranking: List[Tuple[int, List[str]]] = [] current_rank = 1 for vp in sorted_vps: participants = vp_buckets[vp] # no tie if len(participants) == 1: ranking.append((current_rank, participants)) current_rank += 1 continue tie_id = f"{context_id}:score:{vp}" # tie unresolved → shared rank (theoretically impossible) if not TieResolver.is_tie_resolved(war, context_type, tie_id): ranking.append((current_rank, participants)) current_rank += 1 continue # apply token ranking groups = TieResolver.rank_by_tokens( war, context_type, tie_id, participants, ) for group in groups: ranking.append((current_rank, group)) current_rank += 1 return ranking