fix choice tiebreak loop and cancel tiebreak lost tokens
This commit is contained in:
parent
a3b9f5a943
commit
42ad708e77
13 changed files with 333 additions and 129 deletions
|
|
@ -1,5 +1,5 @@
|
|||
from typing import List, Dict, DefaultDict
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Dict, DefaultDict, Tuple
|
||||
from dataclasses import dataclass
|
||||
from collections import defaultdict
|
||||
|
||||
from warchron.constants import ContextType, ScoreKind
|
||||
|
|
@ -13,41 +13,70 @@ from warchron.model.score_service import ScoreService, ParticipantScore
|
|||
class TieContext:
|
||||
context_type: ContextType
|
||||
context_id: str
|
||||
participants: List[str] = field(default_factory=list) # war_participant_ids
|
||||
participants: List[str]
|
||||
score_value: int | None = None
|
||||
score_kind: ScoreKind | None = None
|
||||
objective_id: str | None = None
|
||||
sector_id: str | None = None
|
||||
|
||||
def key(self) -> tuple[str, str, int | None]:
|
||||
return (self.context_type, self.context_id, self.score_value)
|
||||
def key(self) -> Tuple[str, str, int | None, str | None, str | None]:
|
||||
return (
|
||||
self.context_type,
|
||||
self.context_id,
|
||||
self.score_value,
|
||||
self.objective_id,
|
||||
self.sector_id,
|
||||
)
|
||||
|
||||
|
||||
class TieResolver:
|
||||
|
||||
@staticmethod
|
||||
def find_active_tie_id(
|
||||
war: War,
|
||||
context: TieContext,
|
||||
) -> str | None:
|
||||
for ev in reversed(war.events):
|
||||
if (
|
||||
isinstance(ev, InfluenceSpent)
|
||||
and ev.context_type == context.context_type
|
||||
and ev.context_id == context.context_id
|
||||
and ev.objective_id == context.objective_id
|
||||
and ev.sector_id == context.sector_id
|
||||
and ev.participant_id in context.participants
|
||||
):
|
||||
return ev.tie_id
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def find_battle_ties(war: War, round_id: str) -> List[TieContext]:
|
||||
round = war.get_round(round_id)
|
||||
campaign = war.get_campaign_by_round(round_id)
|
||||
ties = []
|
||||
for battle in round.battles.values():
|
||||
if not battle.is_draw():
|
||||
continue
|
||||
context: TieContext = TieContext(
|
||||
ContextType.BATTLE,
|
||||
battle.sector_id,
|
||||
)
|
||||
if TieResolver.is_tie_resolved(war, context):
|
||||
continue
|
||||
if campaign is None:
|
||||
raise DomainError("No campaign for this battle tie")
|
||||
if battle.player_1_id is None or battle.player_2_id is None:
|
||||
raise DomainError("Missing player(s) in this battle context.")
|
||||
p1_id = campaign.campaign_to_war_part_id(battle.player_1_id)
|
||||
p2_id = campaign.campaign_to_war_part_id(battle.player_2_id)
|
||||
if not battle.is_draw():
|
||||
continue
|
||||
context: TieContext = TieContext(
|
||||
ContextType.BATTLE, battle.sector_id, [p1_id, p2_id]
|
||||
)
|
||||
if TieResolver.is_tie_resolved(war, context):
|
||||
continue
|
||||
tie_id = TieResolver.find_active_tie_id(war, context)
|
||||
if not TieResolver.can_tie_be_resolved(war, context, [p1_id, p2_id]):
|
||||
war.events.append(
|
||||
TieResolved(None, ContextType.BATTLE, battle.sector_id)
|
||||
TieResolved(
|
||||
participant_id=None,
|
||||
context_type=ContextType.BATTLE,
|
||||
context_id=battle.sector_id,
|
||||
participants=[p1_id, p2_id],
|
||||
tie_id=tie_id,
|
||||
)
|
||||
)
|
||||
continue
|
||||
ties.append(
|
||||
|
|
@ -80,13 +109,16 @@ class TieResolver:
|
|||
)
|
||||
if TieResolver.is_tie_resolved(war, context):
|
||||
continue
|
||||
tie_id = TieResolver.find_active_tie_id(war, context)
|
||||
if not TieResolver.can_tie_be_resolved(war, context, participants):
|
||||
war.events.append(
|
||||
TieResolved(
|
||||
None,
|
||||
ContextType.CAMPAIGN,
|
||||
campaign_id,
|
||||
score_value,
|
||||
participant_id=None,
|
||||
context_type=ContextType.CAMPAIGN,
|
||||
context_id=campaign_id,
|
||||
participants=participants,
|
||||
tie_id=tie_id,
|
||||
score_value=score_value,
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
|
@ -103,9 +135,7 @@ class TieResolver:
|
|||
|
||||
@staticmethod
|
||||
def find_campaign_objective_ties(
|
||||
war: War,
|
||||
campaign_id: str,
|
||||
objective_id: str,
|
||||
war: War, campaign_id: str, objective_id: str
|
||||
) -> List[TieContext]:
|
||||
scores = ScoreService.compute_scores(
|
||||
war,
|
||||
|
|
@ -131,6 +161,7 @@ class TieResolver:
|
|||
)
|
||||
if TieResolver.is_tie_resolved(war, context):
|
||||
continue
|
||||
tie_id = TieResolver.find_active_tie_id(war, context)
|
||||
if not TieResolver.can_tie_be_resolved(
|
||||
war,
|
||||
context,
|
||||
|
|
@ -138,7 +169,13 @@ class TieResolver:
|
|||
):
|
||||
war.events.append(
|
||||
TieResolved(
|
||||
None, ContextType.CAMPAIGN, context_id, np_value, objective_id
|
||||
participant_id=None,
|
||||
context_type=ContextType.CAMPAIGN,
|
||||
context_id=context_id,
|
||||
participants=participants,
|
||||
tie_id=tie_id,
|
||||
score_value=np_value,
|
||||
objective_id=objective_id,
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
|
@ -181,9 +218,17 @@ class TieResolver:
|
|||
)
|
||||
if TieResolver.is_tie_resolved(war, context):
|
||||
continue
|
||||
tie_id = TieResolver.find_active_tie_id(war, context)
|
||||
if not TieResolver.can_tie_be_resolved(war, context, group):
|
||||
war.events.append(
|
||||
TieResolved(None, ContextType.WAR, war.id, score_value)
|
||||
TieResolved(
|
||||
participant_id=None,
|
||||
context_type=ContextType.WAR,
|
||||
context_id=war.id,
|
||||
participants=group,
|
||||
tie_id=tie_id,
|
||||
score_value=score_value,
|
||||
)
|
||||
)
|
||||
continue
|
||||
ties.append(
|
||||
|
|
@ -198,10 +243,7 @@ class TieResolver:
|
|||
return ties
|
||||
|
||||
@staticmethod
|
||||
def find_war_objective_ties(
|
||||
war: War,
|
||||
objective_id: str,
|
||||
) -> List[TieContext]:
|
||||
def find_war_objective_ties(war: War, objective_id: str) -> List[TieContext]:
|
||||
from warchron.model.result_checker import ResultChecker
|
||||
|
||||
scores = ScoreService.compute_scores(
|
||||
|
|
@ -235,13 +277,22 @@ class TieResolver:
|
|||
context,
|
||||
):
|
||||
continue
|
||||
tie_id = TieResolver.find_active_tie_id(war, context)
|
||||
if not TieResolver.can_tie_be_resolved(
|
||||
war,
|
||||
context,
|
||||
group,
|
||||
):
|
||||
war.events.append(
|
||||
TieResolved(None, ContextType.WAR, war.id, np_value, objective_id)
|
||||
TieResolved(
|
||||
participant_id=None,
|
||||
context_type=ContextType.WAR,
|
||||
context_id=war.id,
|
||||
participants=group,
|
||||
tie_id=tie_id,
|
||||
score_value=np_value,
|
||||
objective_id=objective_id,
|
||||
)
|
||||
)
|
||||
continue
|
||||
ties.append(
|
||||
|
|
@ -260,6 +311,7 @@ class TieResolver:
|
|||
def apply_bids(
|
||||
war: War,
|
||||
context: TieContext,
|
||||
tie_id: str,
|
||||
bids: Dict[str, bool], # war_participant_id -> spend?
|
||||
) -> None:
|
||||
for war_part_id, spend in bids.items():
|
||||
|
|
@ -273,6 +325,7 @@ class TieResolver:
|
|||
amount=1,
|
||||
context_type=context.context_type,
|
||||
context_id=context.context_id,
|
||||
tie_id=tie_id,
|
||||
objective_id=context.objective_id,
|
||||
)
|
||||
)
|
||||
|
|
@ -287,12 +340,7 @@ class TieResolver:
|
|||
for ev in war.events
|
||||
if not (
|
||||
(
|
||||
isinstance(ev, InfluenceSpent)
|
||||
and ev.context_type == context.context_type
|
||||
and ev.context_id == context.context_id
|
||||
)
|
||||
or (
|
||||
isinstance(ev, TieResolved)
|
||||
(isinstance(ev, InfluenceSpent) or isinstance(ev, TieResolved))
|
||||
and ev.context_type == context.context_type
|
||||
and ev.context_id == context.context_id
|
||||
)
|
||||
|
|
@ -356,25 +404,9 @@ class TieResolver:
|
|||
def resolve_tie_state(
|
||||
war: War,
|
||||
context: TieContext,
|
||||
bids: dict[str, bool] | None = None,
|
||||
tie_id: str,
|
||||
bids: Dict[str, bool] | None = None,
|
||||
) -> None:
|
||||
active = TieResolver.get_active_participants(
|
||||
war,
|
||||
context,
|
||||
context.participants,
|
||||
)
|
||||
# confirmed draw if none had bid
|
||||
if not active:
|
||||
war.events.append(
|
||||
TieResolved(
|
||||
None,
|
||||
context.context_type,
|
||||
context.context_id,
|
||||
score_value=context.score_value,
|
||||
objective_id=context.objective_id,
|
||||
)
|
||||
)
|
||||
return
|
||||
# confirmed draw if current bids are 0
|
||||
if bids is not None and not any(bids.values()):
|
||||
war.events.append(
|
||||
|
|
@ -382,12 +414,13 @@ class TieResolver:
|
|||
None,
|
||||
context.context_type,
|
||||
context.context_id,
|
||||
participants=context.participants,
|
||||
tie_id=tie_id,
|
||||
score_value=context.score_value,
|
||||
objective_id=context.objective_id,
|
||||
)
|
||||
)
|
||||
return
|
||||
# else rank_by_tokens
|
||||
groups = TieResolver.rank_by_tokens(war, context, context.participants)
|
||||
if len(groups[0]) == 1:
|
||||
war.events.append(
|
||||
|
|
@ -395,8 +428,10 @@ class TieResolver:
|
|||
groups[0][0],
|
||||
context.context_type,
|
||||
context.context_id,
|
||||
context.score_value,
|
||||
context.objective_id,
|
||||
participants=context.participants,
|
||||
tie_id=tie_id,
|
||||
score_value=context.score_value,
|
||||
objective_id=context.objective_id,
|
||||
)
|
||||
)
|
||||
return
|
||||
|
|
@ -427,11 +462,24 @@ class TieResolver:
|
|||
|
||||
@staticmethod
|
||||
def is_tie_resolved(war: War, context: TieContext) -> bool:
|
||||
return any(
|
||||
isinstance(ev, TieResolved)
|
||||
and ev.context_type == context.context_type
|
||||
and ev.context_id == context.context_id
|
||||
and ev.score_value == context.score_value
|
||||
and ev.objective_id == context.objective_id
|
||||
for ev in war.events
|
||||
)
|
||||
for ev in war.events:
|
||||
if not isinstance(ev, TieResolved):
|
||||
continue
|
||||
if ev.context_type != context.context_type:
|
||||
continue
|
||||
if ev.context_id != context.context_id:
|
||||
continue
|
||||
if (
|
||||
context.score_value is not None
|
||||
and ev.score_value != context.score_value
|
||||
):
|
||||
continue
|
||||
if (
|
||||
context.objective_id is not None
|
||||
and ev.objective_id != context.objective_id
|
||||
):
|
||||
continue
|
||||
if context.sector_id is not None and ev.sector_id != context.sector_id:
|
||||
continue
|
||||
return True
|
||||
return False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue