order pairing draws with war ranking
This commit is contained in:
parent
188f5256fb
commit
2505573250
3 changed files with 65 additions and 25 deletions
|
|
@ -86,6 +86,7 @@ class RoundController:
|
|||
rnd,
|
||||
part.id,
|
||||
)
|
||||
# TODO clarify status icons
|
||||
if alloc.priority != ChoiceStatus.NONE:
|
||||
priority_icon = QIcon(
|
||||
Icons.get_pixmap(IconName[alloc.priority.name])
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
from __future__ import annotations
|
||||
from typing import Dict, Tuple
|
||||
|
||||
from warchron.model.campaign import Campaign
|
||||
|
||||
|
||||
class CampaignHistory:
|
||||
|
||||
@staticmethod
|
||||
def build_match_count(campaign: Campaign) -> Dict[Tuple[str, str], int]:
|
||||
counts: Dict[Tuple[str, str], int] = {}
|
||||
for rnd in campaign.rounds:
|
||||
for battle in rnd.battles.values():
|
||||
p1 = battle.player_1_id
|
||||
p2 = battle.player_2_id
|
||||
if not p1 or not p2:
|
||||
continue
|
||||
key = (p1, p2) if p1 < p2 else (p2, p1)
|
||||
counts[key] = counts.get(key, 0) + 1
|
||||
return counts
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
from typing import Dict, List, Tuple
|
||||
from typing import Dict, List, Tuple, TYPE_CHECKING
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from uuid import uuid4
|
||||
|
||||
|
|
@ -18,7 +19,9 @@ from warchron.model.scoring import ScoreComputer
|
|||
from warchron.model.tiebreaking import TieBreaker, TieContext, ResolveTiesCallback
|
||||
from warchron.model.war_event import TieResolved
|
||||
from warchron.model.scoring import ParticipantScore
|
||||
from warchron.model.history import CampaignHistory
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from warchron.model.campaign import Campaign
|
||||
|
||||
ScoredBattle = Tuple[int, Battle]
|
||||
|
||||
|
|
@ -260,15 +263,58 @@ class Pairing:
|
|||
context.participants,
|
||||
)
|
||||
ordered: List[str] = []
|
||||
for group in ranked_groups:
|
||||
refined_groups = Pairing._refine_with_war_ranking(
|
||||
war,
|
||||
ranked_groups,
|
||||
)
|
||||
for group in refined_groups:
|
||||
shuffled_group = list(group)
|
||||
# TODO improve tie break with history parsing
|
||||
random.shuffle(shuffled_group)
|
||||
ordered.extend(
|
||||
campaign.war_to_campaign_part_id(pid) for pid in shuffled_group
|
||||
)
|
||||
return ordered[:places]
|
||||
|
||||
@staticmethod
|
||||
def _refine_with_war_ranking(
|
||||
war: War,
|
||||
ranked_groups: List[List[str]],
|
||||
) -> List[List[str]]:
|
||||
from warchron.model.checking import ResultChecker
|
||||
|
||||
scores = ScoreComputer.compute_scores(
|
||||
war,
|
||||
ContextType.WAR,
|
||||
war.id,
|
||||
)
|
||||
|
||||
def value_getter(score: ParticipantScore) -> int:
|
||||
return score.victory_points
|
||||
|
||||
war_ranking = ResultChecker.get_effective_ranking(
|
||||
war,
|
||||
ContextType.WAR,
|
||||
war.id,
|
||||
ScoreKind.VP,
|
||||
scores,
|
||||
value_getter,
|
||||
)
|
||||
rank_map: Dict[str, int] = {}
|
||||
for rank, group, _ in war_ranking:
|
||||
for pid in group:
|
||||
rank_map[pid] = rank
|
||||
refined: List[List[str]] = []
|
||||
for group in ranked_groups:
|
||||
if len(group) <= 1:
|
||||
refined.append(group)
|
||||
continue
|
||||
buckets: Dict[int, List[str]] = defaultdict(list)
|
||||
for pid in group:
|
||||
buckets[rank_map.get(pid, 10**9)].append(pid)
|
||||
for rank in sorted(buckets.keys()):
|
||||
refined.append(buckets[rank])
|
||||
return refined
|
||||
|
||||
@staticmethod
|
||||
def _assign_fallback(
|
||||
war: War,
|
||||
|
|
@ -278,7 +324,7 @@ class Pairing:
|
|||
campaign = war.get_campaign_by_round(round.id)
|
||||
if campaign is None:
|
||||
raise DomainError("Campaign not found")
|
||||
match_counts = CampaignHistory.build_match_count(campaign)
|
||||
match_counts = Pairing.build_match_count(campaign)
|
||||
random.shuffle(remaining)
|
||||
for pid in list(remaining):
|
||||
available = round.get_battles_with_places()
|
||||
|
|
@ -316,6 +362,19 @@ class Pairing:
|
|||
rematch_penalty += match_counts.get(key, 0)
|
||||
return rematch_penalty * rematch_weight + occupancy_penalty * occupancy_weight
|
||||
|
||||
@staticmethod
|
||||
def build_match_count(campaign: Campaign) -> Dict[Tuple[str, str], int]:
|
||||
counts: Dict[Tuple[str, str], int] = {}
|
||||
for rnd in campaign.rounds:
|
||||
for battle in rnd.battles.values():
|
||||
p1 = battle.player_1_id
|
||||
p2 = battle.player_2_id
|
||||
if not p1 or not p2:
|
||||
continue
|
||||
key = (p1, p2) if p1 < p2 else (p2, p1)
|
||||
counts[key] = counts.get(key, 0) + 1
|
||||
return counts
|
||||
|
||||
@staticmethod
|
||||
def get_allocation_kind(
|
||||
war: War,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue