fix mismatch part_id in choice event + fix revert events on sector/participant removal
This commit is contained in:
parent
42ad708e77
commit
db78c6dacc
6 changed files with 145 additions and 53 deletions
|
|
@ -23,6 +23,12 @@ class Campaign:
|
|||
self.is_over = False
|
||||
self._war: War | None = None # private link
|
||||
|
||||
@property
|
||||
def war(self) -> "War":
|
||||
if self._war is None:
|
||||
raise RuntimeError("Campaign is not linked to a War")
|
||||
return self._war
|
||||
|
||||
def set_id(self, new_id: str) -> None:
|
||||
self.id = new_id
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class Model:
|
|||
self.players[player.id] = player
|
||||
for w in data.get("wars", []):
|
||||
war = War.fromDict(w)
|
||||
war.relink()
|
||||
self.wars[war.id] = war
|
||||
except json.JSONDecodeError:
|
||||
raise RuntimeError("Data file is corrupted")
|
||||
|
|
|
|||
|
|
@ -186,12 +186,13 @@ class Pairing:
|
|||
campaign = war.get_campaign_by_round(round.id)
|
||||
if campaign is None:
|
||||
raise DomainError("Campaign not found for round {round.id}")
|
||||
war_participants = [
|
||||
campaign.campaign_to_war_part_id(pid) for pid in participants
|
||||
]
|
||||
context = TieContext(
|
||||
context_type=ContextType.CHOICE,
|
||||
context_id=round.id,
|
||||
participants=[
|
||||
campaign.campaign_to_war_part_id(pid) for pid in participants
|
||||
],
|
||||
participants=war_participants,
|
||||
score_value=score_value,
|
||||
score_kind=ScoreKind.VP,
|
||||
sector_id=sector_id,
|
||||
|
|
@ -212,6 +213,7 @@ class Pairing:
|
|||
score_kind=context.score_kind,
|
||||
sector_id=context.sector_id,
|
||||
)
|
||||
# natural or unbreakable draw
|
||||
if not TieResolver.can_tie_be_resolved(
|
||||
war, context, current_context.participants
|
||||
):
|
||||
|
|
@ -220,7 +222,7 @@ class Pairing:
|
|||
None,
|
||||
context.context_type,
|
||||
context.context_id,
|
||||
participants,
|
||||
participants=context.participants,
|
||||
tie_id=tie_id,
|
||||
score_value=score_value,
|
||||
sector_id=sector_id,
|
||||
|
|
@ -254,7 +256,6 @@ class Pairing:
|
|||
for group in ranked_groups:
|
||||
shuffled_group = list(group)
|
||||
# TODO improve tie break with history parsing
|
||||
# TODO avoid rematch
|
||||
random.shuffle(shuffled_group)
|
||||
ordered.extend(
|
||||
campaign.war_to_campaign_part_id(pid) for pid in shuffled_group
|
||||
|
|
@ -266,6 +267,7 @@ class Pairing:
|
|||
round: Round,
|
||||
remaining: List[str],
|
||||
) -> None:
|
||||
# TODO avoid rematch
|
||||
for pid in list(remaining):
|
||||
available = round.get_battles_with_places()
|
||||
if not available:
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from typing import Any, Dict, List, TYPE_CHECKING
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from warchron.model.campaign import Campaign
|
||||
from warchron.model.war import War
|
||||
from warchron.model.exception import ForbiddenOperation
|
||||
from warchron.model.choice import Choice
|
||||
from warchron.model.battle import Battle
|
||||
|
|
@ -17,6 +18,16 @@ class Round:
|
|||
self.is_over: bool = False
|
||||
self._campaign: Campaign | None = None # private link
|
||||
|
||||
@property
|
||||
def campaign(self) -> "Campaign":
|
||||
if self._campaign is None:
|
||||
raise RuntimeError("Round is not linked to a Campaign")
|
||||
return self._campaign
|
||||
|
||||
@property
|
||||
def war(self) -> "War":
|
||||
return self.campaign.war
|
||||
|
||||
def set_id(self, new_id: str) -> None:
|
||||
self.id = new_id
|
||||
|
||||
|
|
@ -91,7 +102,6 @@ class Round:
|
|||
choice.set_secondary(secondary_sector_id)
|
||||
choice.set_comment(comment)
|
||||
|
||||
# FIXME remove corresponding InfluenceSpent and TieResolved
|
||||
def clear_sector_references(self, sector_id: str) -> None:
|
||||
for choice in self.choices.values():
|
||||
trigger_revert_ties = False
|
||||
|
|
@ -102,17 +112,18 @@ class Round:
|
|||
choice.secondary_sector_id = None
|
||||
trigger_revert_ties = True
|
||||
if trigger_revert_ties:
|
||||
if self._campaign and self._campaign._war:
|
||||
self._campaign._war.revert_choice_ties(self.id, sector_id=sector_id)
|
||||
self.war.revert_choice_ties(self.id, sector_id=sector_id)
|
||||
|
||||
def remove_choice(self, participant_id: str) -> None:
|
||||
if participant_id not in self.choices:
|
||||
return
|
||||
if self.is_over:
|
||||
# TODO catch me if you can (inner)
|
||||
raise ForbiddenOperation("Can't remove choice in a closed round.")
|
||||
# TODO prevent if battles already assigned
|
||||
if self._campaign and self._campaign._war:
|
||||
self._campaign._war.revert_choice_ties(
|
||||
self.id, participants=[participant_id]
|
||||
self.war.revert_choice_ties(
|
||||
self.id,
|
||||
participants=[self.campaign.campaign_to_war_part_id(participant_id)],
|
||||
)
|
||||
del self.choices[participant_id]
|
||||
|
||||
|
|
@ -187,10 +198,16 @@ class Round:
|
|||
if battle.winner_id == participant_id:
|
||||
battle.winner_id = None
|
||||
if trigger_revert_ties:
|
||||
if self._campaign and self._campaign._war:
|
||||
self._campaign._war.revert_battle_ties(battle.sector_id)
|
||||
self.war.revert_battle_ties(
|
||||
self.id,
|
||||
participants=[
|
||||
self.campaign.campaign_to_war_part_id(participant_id)
|
||||
],
|
||||
)
|
||||
|
||||
def remove_battle(self, sector_id: str) -> None:
|
||||
if sector_id not in self.battles:
|
||||
return
|
||||
if self.is_over:
|
||||
# TODO catch me if you can
|
||||
raise ForbiddenOperation("Can't remove battle in a closed round.")
|
||||
|
|
@ -198,6 +215,5 @@ class Round:
|
|||
if bat and bat.is_finished():
|
||||
# TODO catch me if you can
|
||||
raise ForbiddenOperation("Can't remove finished battle.")
|
||||
if self._campaign and self._campaign._war:
|
||||
self._campaign._war.revert_battle_ties(sector_id)
|
||||
self.war.revert_battle_ties(self.id, sector_id=sector_id)
|
||||
del self.battles[sector_id]
|
||||
|
|
|
|||
|
|
@ -161,6 +161,12 @@ class War:
|
|||
war.set_state(data.get("is_over", False))
|
||||
return war
|
||||
|
||||
def relink(self) -> None:
|
||||
for campaign in self.campaigns:
|
||||
campaign._war = self
|
||||
for rnd in campaign.rounds:
|
||||
rnd._campaign = campaign
|
||||
|
||||
# Objective methods
|
||||
|
||||
def add_objective(self, name: str, description: str | None) -> Objective:
|
||||
|
|
@ -552,12 +558,60 @@ class War:
|
|||
)
|
||||
return gained - spent
|
||||
|
||||
def get_events_by_ties_session(self, tie_id: str) -> List[WarEvent]:
|
||||
def get_events_by_tie_id(self, tie_id: str) -> List[WarEvent]:
|
||||
return [ev for ev in self.events if ev.tie_id == tie_id]
|
||||
|
||||
def remove_ties_session(self, tie_id: str) -> None:
|
||||
def remove_events_by_tie_id(self, tie_id: str) -> None:
|
||||
self.events = [ev for ev in self.events if ev.tie_id != tie_id]
|
||||
|
||||
def find_ties(
|
||||
self,
|
||||
*,
|
||||
context_type: str,
|
||||
context_id: str,
|
||||
sector_id: str | None = None,
|
||||
participants: List[str] | None = None,
|
||||
) -> Set[str]:
|
||||
ties = set()
|
||||
for ev in self.events:
|
||||
if not isinstance(ev, TieResolved):
|
||||
continue
|
||||
if ev.context_type != context_type:
|
||||
continue
|
||||
if ev.context_id != context_id:
|
||||
continue
|
||||
if sector_id and ev.sector_id != sector_id:
|
||||
continue
|
||||
if participants and not any(p in ev.participants for p in participants):
|
||||
continue
|
||||
if ev.tie_id:
|
||||
ties.add(ev.tie_id)
|
||||
return ties
|
||||
|
||||
def get_draws(
|
||||
self,
|
||||
*,
|
||||
context_type: str,
|
||||
context_id: str,
|
||||
sector_id: str | None = None,
|
||||
participants: List[str] | None = None,
|
||||
) -> List[WarEvent]:
|
||||
draws: List[WarEvent] = list()
|
||||
for ev in self.events:
|
||||
if not isinstance(ev, TieResolved):
|
||||
continue
|
||||
if ev.context_type != context_type:
|
||||
continue
|
||||
if ev.context_id != context_id:
|
||||
continue
|
||||
if sector_id and ev.sector_id != sector_id:
|
||||
continue
|
||||
if participants and not any(p in ev.participants for p in participants):
|
||||
continue
|
||||
if ev.tie_id is None:
|
||||
draws.append(ev)
|
||||
return draws
|
||||
|
||||
def revert_choice_ties(
|
||||
self,
|
||||
round_id: str,
|
||||
|
|
@ -565,33 +619,45 @@ class War:
|
|||
sector_id: str | None = None,
|
||||
participants: List[str] | None = None,
|
||||
) -> None:
|
||||
removed_ties: Set[str] = set()
|
||||
for ev in self.events:
|
||||
if (
|
||||
isinstance(ev, TieResolved)
|
||||
and ev.context_type == ContextType.CHOICE
|
||||
and ev.context_id == round_id
|
||||
):
|
||||
if (
|
||||
sector_id is None
|
||||
or ev.sector_id == sector_id
|
||||
or participants is None
|
||||
or any(p in ev.participants for p in participants)
|
||||
):
|
||||
if ev.tie_id:
|
||||
removed_ties.add(ev.tie_id)
|
||||
self.events = [ev for ev in self.events if ev.tie_id not in removed_ties]
|
||||
|
||||
def revert_battle_ties(self, sector_id: str) -> None:
|
||||
removed_ties = {
|
||||
ev.tie_id
|
||||
removed_ties = self.find_ties(
|
||||
context_type=ContextType.CHOICE,
|
||||
context_id=round_id,
|
||||
sector_id=sector_id,
|
||||
participants=participants,
|
||||
)
|
||||
draws = self.get_draws(
|
||||
context_type=ContextType.CHOICE,
|
||||
context_id=round_id,
|
||||
sector_id=sector_id,
|
||||
participants=participants,
|
||||
)
|
||||
self.events = [
|
||||
ev
|
||||
for ev in self.events
|
||||
if isinstance(ev, TieResolved)
|
||||
and ev.context_type == ContextType.BATTLE
|
||||
and ev.context_id == sector_id
|
||||
and ev.tie_id
|
||||
}
|
||||
self.events = [ev for ev in self.events if ev.tie_id not in removed_ties]
|
||||
if ev.tie_id not in removed_ties and ev not in draws
|
||||
]
|
||||
|
||||
def revert_tie(self, tie_id: str) -> None:
|
||||
self.events = [ev for ev in self.events if ev.tie_id != tie_id]
|
||||
def revert_battle_ties(
|
||||
self,
|
||||
round_id: str,
|
||||
*,
|
||||
sector_id: str | None = None,
|
||||
participants: List[str] | None = None,
|
||||
) -> None:
|
||||
removed_ties = self.find_ties(
|
||||
context_type=ContextType.BATTLE,
|
||||
context_id=round_id,
|
||||
sector_id=sector_id,
|
||||
participants=participants,
|
||||
)
|
||||
draws = self.get_draws(
|
||||
context_type=ContextType.BATTLE,
|
||||
context_id=round_id,
|
||||
sector_id=sector_id,
|
||||
participants=participants,
|
||||
)
|
||||
self.events = [
|
||||
ev
|
||||
for ev in self.events
|
||||
if ev.tie_id not in removed_ties and ev not in draws
|
||||
]
|
||||
|
|
|
|||
|
|
@ -325,25 +325,26 @@
|
|||
"events": [
|
||||
{
|
||||
"type": "InfluenceGained",
|
||||
"id": "8f20b587-0ff3-42c9-9965-88112e2a4e74",
|
||||
"id": "096802e2-5833-4c6d-941d-c91d98957fc1",
|
||||
"participant_id": "accc25f2-43a0-4d41-804f-3c8aec853c97",
|
||||
"context_type": "battle",
|
||||
"context_id": "4548997e-50e5-493a-b751-483f5bcccc00",
|
||||
"timestamp": "2026-02-26T16:10:54.125407",
|
||||
"timestamp": "2026-03-18T09:25:03.746744",
|
||||
"tie_id": null,
|
||||
"amount": 1
|
||||
},
|
||||
{
|
||||
"type": "TieResolved",
|
||||
"id": "c83a9d4e-4620-47de-93c8-d29d6e119c59",
|
||||
"id": "ec6b8932-b24f-4042-a8f4-566a42d0857d",
|
||||
"participant_id": null,
|
||||
"context_type": "battle",
|
||||
"context_id": "79accf7c-2d93-4ac3-b747-e7092bfe3feb",
|
||||
"timestamp": "2026-03-18T09:25:08.578539",
|
||||
"tie_id": "07aa41e5-1696-4b9f-931b-2540e213c7ef",
|
||||
"participants": [
|
||||
"602e2eaf-297e-490b-b0e9-efec818e466a",
|
||||
"1f6b4e7c-b1e4-4a2e-9aea-7bb75b20b4de"
|
||||
"accc25f2-43a0-4d41-804f-3c8aec853c97",
|
||||
"179bdab6-8630-4a92-8fb6-d2637562c66c"
|
||||
],
|
||||
"timestamp": "2026-02-26T16:11:44.346337",
|
||||
"tie_id": null,
|
||||
"score_value": null,
|
||||
"objective_id": null,
|
||||
"sector_id": null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue