display pairing results in choice table
This commit is contained in:
parent
9e602e8ca4
commit
4396b15c3a
9 changed files with 194 additions and 12 deletions
|
|
@ -71,6 +71,9 @@ class IconName(StrEnum):
|
|||
NP3RDDRAW = auto()
|
||||
NP3RDBREAK = auto()
|
||||
NP3RDTIEDRAW = auto()
|
||||
ALLOCATED = auto()
|
||||
ALLOCATEDTOKEN = auto()
|
||||
FALLBACK = auto()
|
||||
|
||||
|
||||
VP_RANK_TO_ICON = {
|
||||
|
|
@ -125,6 +128,8 @@ class Icons:
|
|||
IconName.NP2ND: "medal-silver.png",
|
||||
IconName.NP3RD: "medal-bronze.png",
|
||||
IconName.WARCHRONBACK: "warchron_background.png",
|
||||
IconName.ALLOCATED: "map.png",
|
||||
IconName.FALLBACK: "cross-script.png",
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
|
@ -255,6 +260,11 @@ class Icons:
|
|||
cls.get_pixmap(IconName.DRAW),
|
||||
cls.get_pixmap(IconName.TOKEN),
|
||||
)
|
||||
elif name == IconName.ALLOCATEDTOKEN:
|
||||
pix = cls._compose(
|
||||
cls.get_pixmap(IconName.ALLOCATED),
|
||||
cls.get_pixmap(IconName.TOKEN),
|
||||
)
|
||||
else:
|
||||
path = RESOURCES_DIR / cls._paths[name]
|
||||
pix = QPixmap(path.as_posix())
|
||||
|
|
@ -313,3 +323,16 @@ class ContextType(StrEnum):
|
|||
class ScoreKind(Enum):
|
||||
VP = auto()
|
||||
NP = auto()
|
||||
|
||||
|
||||
class ChoiceStatus(StrEnum):
|
||||
NONE = auto()
|
||||
TOKEN = auto()
|
||||
ALLOCATED = auto()
|
||||
ALLOCATEDTOKEN = auto()
|
||||
|
||||
|
||||
class AllocationType(Enum):
|
||||
PRIORITY = auto()
|
||||
SECONDARY = auto()
|
||||
FALLBACK = auto()
|
||||
|
|
|
|||
|
|
@ -88,6 +88,9 @@ class ChoiceDTO:
|
|||
priority_sector: str | None
|
||||
secondary_sector: str | None
|
||||
comment: str | None
|
||||
priority_icon: QIcon | None = None
|
||||
secondary_icon: QIcon | None = None
|
||||
fallback_icon: QIcon | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
|
|
@ -103,8 +106,6 @@ class BattleDTO:
|
|||
state_icon: QIcon | None
|
||||
player1_icon: QIcon | None
|
||||
player2_icon: QIcon | None
|
||||
player1_tooltip: str | None = None
|
||||
player2_tooltip: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,14 @@ from PyQt6.QtWidgets import QDialog
|
|||
from PyQt6.QtWidgets import QMessageBox
|
||||
from PyQt6.QtGui import QIcon
|
||||
|
||||
from warchron.constants import ItemType, RefreshScope, Icons, IconName, ContextType
|
||||
from warchron.constants import (
|
||||
ItemType,
|
||||
RefreshScope,
|
||||
Icons,
|
||||
IconName,
|
||||
ContextType,
|
||||
ChoiceStatus,
|
||||
)
|
||||
from warchron.model.exception import (
|
||||
AbortedOperation,
|
||||
DomainError,
|
||||
|
|
@ -12,6 +19,7 @@ from warchron.model.exception import (
|
|||
)
|
||||
from warchron.model.tie_manager import TieResolver, TieContext
|
||||
from warchron.model.result_checker import ResultChecker
|
||||
from warchron.model.pairing import Pairing
|
||||
from warchron.model.round import Round
|
||||
from warchron.model.war import War
|
||||
|
||||
|
|
@ -62,6 +70,20 @@ class RoundController:
|
|||
if choice.secondary_sector_id is not None
|
||||
else ""
|
||||
)
|
||||
priority_icon = None
|
||||
secondary_icon = None
|
||||
fallback_icon = None
|
||||
alloc = Pairing.get_round_allocation(
|
||||
war,
|
||||
rnd,
|
||||
part.id,
|
||||
)
|
||||
if alloc.priority != ChoiceStatus.NONE:
|
||||
priority_icon = QIcon(Icons.get_pixmap(IconName[alloc.priority.name]))
|
||||
if alloc.secondary != ChoiceStatus.NONE:
|
||||
secondary_icon = QIcon(Icons.get_pixmap(IconName[alloc.secondary.name]))
|
||||
if alloc.fallback:
|
||||
fallback_icon = QIcon(Icons.get_pixmap(IconName.FALLBACK))
|
||||
choices_for_display.append(
|
||||
ChoiceDTO(
|
||||
id=choice.participant_id,
|
||||
|
|
@ -71,9 +93,11 @@ class RoundController:
|
|||
priority_sector=priority_name,
|
||||
secondary_sector=secondary_name,
|
||||
comment=choice.comment,
|
||||
priority_icon=priority_icon,
|
||||
secondary_icon=secondary_icon,
|
||||
fallback_icon=fallback_icon,
|
||||
)
|
||||
)
|
||||
# TODO display allocated sectors and used token
|
||||
self.app.view.display_round_choices(choices_for_display)
|
||||
battles_for_display: List[BattleDTO] = []
|
||||
for sect in sectors:
|
||||
|
|
@ -111,8 +135,7 @@ class RoundController:
|
|||
winner_name = ""
|
||||
p1_icon = None
|
||||
p2_icon = None
|
||||
p1_tooltip = None
|
||||
p2_tooltip = None
|
||||
# TODO use uniform draw/tie icon logic with choice, war, campaign...
|
||||
if battle.is_draw():
|
||||
p1_icon = Icons.get(IconName.DRAW)
|
||||
p2_icon = Icons.get(IconName.DRAW)
|
||||
|
|
@ -131,10 +154,8 @@ class RoundController:
|
|||
pixmap = Icons.get_pixmap(IconName.TIEBREAK_TOKEN)
|
||||
if effective_winner == p1_war:
|
||||
p1_icon = QIcon(pixmap)
|
||||
p1_tooltip = "Won by tie-break"
|
||||
else:
|
||||
p2_icon = QIcon(pixmap)
|
||||
p2_tooltip = "Won by tie-break"
|
||||
elif battle.winner_id:
|
||||
if battle.winner_id == battle.player_1_id:
|
||||
p1_icon = Icons.get(IconName.WIN)
|
||||
|
|
@ -153,8 +174,6 @@ class RoundController:
|
|||
state_icon=state_icon,
|
||||
player1_icon=p1_icon,
|
||||
player2_icon=p2_icon,
|
||||
player1_tooltip=p1_tooltip,
|
||||
player2_tooltip=p2_tooltip,
|
||||
)
|
||||
)
|
||||
self.app.view.display_round_battles(battles_for_display)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from __future__ import annotations
|
||||
from typing import Dict, List, Callable, Tuple
|
||||
from dataclasses import dataclass
|
||||
from uuid import uuid4
|
||||
|
||||
import random
|
||||
|
||||
from warchron.constants import ContextType, ScoreKind
|
||||
from warchron.constants import ContextType, ScoreKind, ChoiceStatus, AllocationType
|
||||
from warchron.model.exception import (
|
||||
DomainError,
|
||||
ForbiddenOperation,
|
||||
|
|
@ -15,7 +16,7 @@ from warchron.model.round import Round
|
|||
from warchron.model.battle import Battle
|
||||
from warchron.model.score_service import ScoreService
|
||||
from warchron.model.tie_manager import TieResolver, TieContext
|
||||
from warchron.model.war_event import TieResolved
|
||||
from warchron.model.war_event import TieResolved, InfluenceSpent
|
||||
from warchron.model.score_service import ParticipantScore
|
||||
|
||||
ResolveTiesCallback = Callable[
|
||||
|
|
@ -24,6 +25,13 @@ ResolveTiesCallback = Callable[
|
|||
]
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class AllocationResult:
|
||||
priority: ChoiceStatus
|
||||
secondary: ChoiceStatus
|
||||
fallback: bool
|
||||
|
||||
|
||||
class Pairing:
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -218,6 +226,10 @@ class Pairing:
|
|||
)
|
||||
or len(active) <= places
|
||||
):
|
||||
print(
|
||||
f"Natural or acceptable draw for sector {sector_id} with participants:\n",
|
||||
context.participants,
|
||||
)
|
||||
war.events.append(
|
||||
TieResolved(
|
||||
None,
|
||||
|
|
@ -234,6 +246,10 @@ class Pairing:
|
|||
bids = bids_map[current_context.key()]
|
||||
# confirmed draw if current bids are 0
|
||||
if bids is not None and not any(bids.values()):
|
||||
print(
|
||||
f"Confirmed draw for sector {sector_id} with participants:\n",
|
||||
context.participants,
|
||||
)
|
||||
war.events.append(
|
||||
TieResolved(
|
||||
None,
|
||||
|
|
@ -278,3 +294,100 @@ class Pairing:
|
|||
remaining.remove(pid)
|
||||
continue
|
||||
raise DomainError(f"Ambiguous fallback for participant {pid}")
|
||||
|
||||
@staticmethod
|
||||
def get_allocation_kind(
|
||||
war: War,
|
||||
round_id: str,
|
||||
participant_id: str,
|
||||
sector_id: str,
|
||||
) -> AllocationType:
|
||||
round = war.get_round(round_id)
|
||||
choice = round.choices.get(participant_id)
|
||||
if not choice:
|
||||
raise DomainError(f"No choice found for participant {participant_id}")
|
||||
if choice.priority_sector_id == sector_id:
|
||||
return AllocationType.PRIORITY
|
||||
if choice.secondary_sector_id == sector_id:
|
||||
return AllocationType.SECONDARY
|
||||
return AllocationType.FALLBACK
|
||||
|
||||
@staticmethod
|
||||
def participant_spent_token(
|
||||
war: War,
|
||||
round_id: str,
|
||||
sector_id: str | None,
|
||||
war_participant_id: str,
|
||||
) -> bool:
|
||||
if sector_id is None:
|
||||
return False
|
||||
for ev in war.events:
|
||||
if not isinstance(ev, InfluenceSpent):
|
||||
continue
|
||||
if ev.context_type != ContextType.CHOICE:
|
||||
continue
|
||||
if ev.context_id != round_id:
|
||||
continue
|
||||
if ev.sector_id != sector_id:
|
||||
continue
|
||||
if ev.participant_id == war_participant_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_round_allocation(
|
||||
war: War,
|
||||
round: Round,
|
||||
campaign_participant_id: str,
|
||||
) -> AllocationResult:
|
||||
choice = round.choices[campaign_participant_id]
|
||||
campaign = war.get_campaign_by_round(round.id)
|
||||
if campaign is None:
|
||||
raise DomainError(f"No campaign found for round {round.id}")
|
||||
war_pid = campaign.campaign_to_war_part_id(campaign_participant_id)
|
||||
|
||||
token_priority = Pairing.participant_spent_token(
|
||||
war,
|
||||
round.id,
|
||||
choice.priority_sector_id,
|
||||
war_pid,
|
||||
)
|
||||
token_secondary = Pairing.participant_spent_token(
|
||||
war,
|
||||
round.id,
|
||||
choice.secondary_sector_id,
|
||||
war_pid,
|
||||
)
|
||||
battle = round.get_battle_for_participant(campaign_participant_id)
|
||||
allocation = AllocationType.FALLBACK
|
||||
if battle:
|
||||
allocation = Pairing.get_allocation_kind(
|
||||
war,
|
||||
round.id,
|
||||
campaign_participant_id,
|
||||
battle.sector_id,
|
||||
)
|
||||
priority_status = ChoiceStatus.NONE
|
||||
secondary_status = ChoiceStatus.NONE
|
||||
fallback = allocation == AllocationType.FALLBACK
|
||||
if allocation == AllocationType.PRIORITY:
|
||||
priority_status = (
|
||||
ChoiceStatus.ALLOCATEDTOKEN
|
||||
if token_priority
|
||||
else ChoiceStatus.ALLOCATED
|
||||
)
|
||||
elif token_priority:
|
||||
priority_status = ChoiceStatus.TOKEN
|
||||
if allocation == AllocationType.SECONDARY:
|
||||
secondary_status = (
|
||||
ChoiceStatus.ALLOCATEDTOKEN
|
||||
if token_secondary
|
||||
else ChoiceStatus.ALLOCATED
|
||||
)
|
||||
elif token_secondary:
|
||||
secondary_status = ChoiceStatus.TOKEN
|
||||
return AllocationResult(
|
||||
priority=priority_status,
|
||||
secondary=secondary_status,
|
||||
fallback=fallback,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -132,6 +132,18 @@ class Round:
|
|||
def get_battle(self, sector_id: str) -> Battle | None:
|
||||
return self.battles.get(sector_id)
|
||||
|
||||
def get_battle_for_participant(
|
||||
self,
|
||||
campaign_participant_id: str,
|
||||
) -> Battle | None:
|
||||
for battle in self.battles.values():
|
||||
if (
|
||||
battle.player_1_id == campaign_participant_id
|
||||
or battle.player_2_id == campaign_participant_id
|
||||
):
|
||||
return battle
|
||||
return None
|
||||
|
||||
def has_battle_with_sector(self, sector_id: str) -> bool:
|
||||
return any(bat.sector_id == sector_id for bat in self.battles.values())
|
||||
|
||||
|
|
|
|||
|
|
@ -327,6 +327,7 @@ class TieResolver:
|
|||
context_id=context.context_id,
|
||||
tie_id=tie_id,
|
||||
objective_id=context.objective_id,
|
||||
sector_id=context.sector_id,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -418,6 +419,7 @@ class TieResolver:
|
|||
tie_id=tie_id,
|
||||
score_value=context.score_value,
|
||||
objective_id=context.objective_id,
|
||||
sector_id=context.sector_id,
|
||||
)
|
||||
)
|
||||
return
|
||||
|
|
@ -432,6 +434,7 @@ class TieResolver:
|
|||
tie_id=tie_id,
|
||||
score_value=context.score_value,
|
||||
objective_id=context.objective_id,
|
||||
sector_id=context.sector_id,
|
||||
)
|
||||
)
|
||||
return
|
||||
|
|
|
|||
BIN
src/warchron/view/resources/cross-script.png
Normal file
BIN
src/warchron/view/resources/cross-script.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 663 B |
BIN
src/warchron/view/resources/map.png
Normal file
BIN
src/warchron/view/resources/map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -603,15 +603,26 @@ class View(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||
table = self.choicesTable
|
||||
table.setSortingEnabled(False)
|
||||
table.clearContents()
|
||||
table.setColumnCount(4)
|
||||
table.setHorizontalHeaderLabels(["Participant", "Priority", "Secondary", ""])
|
||||
table.setRowCount(len(participants))
|
||||
table.setIconSize(QSize(32, 16))
|
||||
for row, choice in enumerate(participants):
|
||||
participant_item = QtWidgets.QTableWidgetItem(choice.participant_name)
|
||||
priority_item = QtWidgets.QTableWidgetItem(choice.priority_sector)
|
||||
if choice.priority_icon:
|
||||
priority_item.setIcon(choice.priority_icon)
|
||||
secondary_item = QtWidgets.QTableWidgetItem(choice.secondary_sector)
|
||||
if choice.secondary_icon:
|
||||
secondary_item.setIcon(choice.secondary_icon)
|
||||
status_item = QtWidgets.QTableWidgetItem()
|
||||
if choice.fallback_icon:
|
||||
status_item.setIcon(choice.fallback_icon)
|
||||
participant_item.setData(Qt.ItemDataRole.UserRole, choice.id)
|
||||
table.setItem(row, 0, participant_item)
|
||||
table.setItem(row, 1, priority_item)
|
||||
table.setItem(row, 2, secondary_item)
|
||||
table.setItem(row, 3, status_item)
|
||||
table.setSortingEnabled(True)
|
||||
table.resizeColumnsToContents()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue