Source code for pyscoundrel.game.state

"""Game state management for PyScoundrel."""

from dataclasses import dataclass, field
from enum import Enum
from typing import List, Optional

from ..models import Card, Deck, Player, Room


[docs] class GamePhase(Enum): """Current phase of the game.""" SETUP = "setup" DRAW_ROOM = "draw_room" DECIDE_AVOID = "decide_avoid" FACE_CARDS = "face_cards" TURN_COMPLETE = "turn_complete" GAME_OVER = "game_over"
[docs] @dataclass class GameState: """ Complete state of a Scoundrel game. This class represents a snapshot of the game at any point in time. """ player: Player deck: Deck current_room: Optional[Room] = None discard_pile: List[Card] = field(default_factory=list) phase: GamePhase = GamePhase.SETUP turn_number: int = 0 rooms_avoided_consecutively: int = 0 last_card_was_potion: bool = False game_over: bool = False victory: bool = False @property def can_avoid_room(self) -> bool: """Check if player can avoid the current room.""" return self.rooms_avoided_consecutively < 1 @property def score(self) -> int: """ Calculate the current score. If alive and deck is empty: positive score = health If dead: negative score = sum of remaining monster damage """ if self.game_over: if self.victory: # Bonus for finishing with max health and last card being a potion if self.player.health == self.player.max_health and self.last_card_was_potion: # Would need to track last potion value - simplified here return self.player.health return self.player.health else: # Player died - negative score from remaining monsters remaining_damage = sum( card.value for card in self.deck.cards if card.card_type.name == "MONSTER" ) return -(remaining_damage) return 0
[docs] def discard(self, cards: List[Card]) -> None: """ Add cards to the discard pile. Args: cards: Cards to discard """ self.discard_pile.extend(cards)
[docs] def start_new_turn(self) -> None: """Start a new turn.""" self.turn_number += 1 self.player.reset_turn_state() self.last_card_was_potion = False self.phase = GamePhase.DRAW_ROOM
[docs] def end_turn(self) -> None: """Mark the current turn as complete.""" self.phase = GamePhase.TURN_COMPLETE
[docs] def mark_quit(self) -> None: """Mark the game as quit by user (counts as defeat).""" self.game_over = True self.victory = False self.phase = GamePhase.GAME_OVER
[docs] def check_game_over(self) -> bool: """ Check if the game is over. Game ends when: - Player's health reaches 0 (loss) - Deck is empty (victory) """ if self.player.is_dead: self.game_over = True self.victory = False self.phase = GamePhase.GAME_OVER return True if self.deck.is_empty: self.game_over = True self.victory = True self.phase = GamePhase.GAME_OVER return True return False
def __str__(self) -> str: return ( f"Turn {self.turn_number} | {self.player} | " f"Deck: {self.deck.remaining} | Phase: {self.phase.value}" ) def __repr__(self) -> str: return ( f"GameState(turn={self.turn_number}, phase={self.phase.value}, " f"player_hp={self.player.health}, deck={self.deck.remaining})" )