warchron_app/src/warchron/model/result_checker.py

71 lines
2.3 KiB
Python
Raw Normal View History

from __future__ import annotations
from typing import List, Tuple, Dict, TYPE_CHECKING
from collections import defaultdict
2026-02-20 11:01:25 +01:00
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
2026-02-20 11:01:25 +01:00
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