feat: add hand detail API and enrich hand summary fields
- HandSummary: add hole_cards, starting_stacks, ending_stacks, pot_contributions - Engine: capture all players' hole cards (not just showdown), pre/post hand stacks, per-level pot contributions - Server: new GET /game/<game_id>/hands/<hand_number> route - Service: add get_hand_state() method - Tests: add ServerTests for new endpoint, update existing tests - Existing GET /game/<game_id> auto-inherits new fields via shared to_dict()
This commit is contained in:
@@ -178,6 +178,10 @@ class HandSummary:
|
||||
# on the summary (rather than only on the game) guarantees historical
|
||||
# hands remain self-describing even after the blinds are raised later.
|
||||
blinds: BlindLevel | None = None
|
||||
hole_cards: dict[str, list[Card]] = field(default_factory=dict)
|
||||
starting_stacks: dict[str, int] = field(default_factory=dict)
|
||||
ending_stacks: dict[str, int] = field(default_factory=dict)
|
||||
pot_contributions: list[dict[str, Any]] = field(default_factory=list)
|
||||
showdown_hands: dict[str, list[Card]] = field(default_factory=dict)
|
||||
started_at: float = field(default_factory=time)
|
||||
finished_at: float = field(default_factory=time)
|
||||
@@ -191,6 +195,16 @@ class HandSummary:
|
||||
"board": [str(card) for card in self.board],
|
||||
"actions": [record.to_dict() for record in self.actions],
|
||||
"awards": [award.to_dict() for award in self.awards],
|
||||
"hole_cards": {
|
||||
player_id: [str(card) for card in cards]
|
||||
for player_id, cards in self.hole_cards.items()
|
||||
},
|
||||
"starting_stacks": dict(self.starting_stacks),
|
||||
"ending_stacks": dict(self.ending_stacks),
|
||||
"pot_contributions": [
|
||||
self._pot_contribution_to_dict(contribution)
|
||||
for contribution in self.pot_contributions
|
||||
],
|
||||
# ``showdown_hands`` is only populated when more than one player
|
||||
# remained eligible for a pot; empty dict means the hand ended
|
||||
# without a showdown (e.g. everyone folded but the winner).
|
||||
@@ -201,3 +215,26 @@ class HandSummary:
|
||||
"started_at": self.started_at,
|
||||
"finished_at": self.finished_at,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _pot_contribution_to_dict(contribution: dict[str, Any]) -> dict[str, object]:
|
||||
hand_value = contribution.get("hand_value")
|
||||
if isinstance(hand_value, HandValue):
|
||||
hand_value = hand_value.to_dict()
|
||||
elif isinstance(hand_value, dict):
|
||||
hand_value = dict(hand_value)
|
||||
|
||||
raw_contributors = contribution.get("contributors") or {}
|
||||
contributors = {
|
||||
str(player_id): int(amount)
|
||||
for player_id, amount in dict(raw_contributors).items()
|
||||
}
|
||||
return {
|
||||
"amount": int(contribution.get("amount") or 0),
|
||||
"contributors": contributors,
|
||||
"winners": [
|
||||
str(player_id)
|
||||
for player_id in contribution.get("winners", [])
|
||||
],
|
||||
"hand_value": hand_value,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user