Spaces:
Sleeping
Sleeping
| """ | |
| Data models for Cluedo Custom game. | |
| Defines the structure of players, cards, and game state. | |
| """ | |
| from typing import List, Optional, Dict | |
| from pydantic import BaseModel, Field | |
| from enum import Enum | |
| import random | |
| import string | |
| class NarrativeTone(str, Enum): | |
| """Narrative tone options for the game.""" | |
| SERIOUS = "🕵️ Sérieuse" | |
| PARODY = "😂 Parodique" | |
| FANTASY = "🧙♂️ Fantastique" | |
| THRILLER = "🎬 Thriller" | |
| HORROR = "👻 Horreur" | |
| class CardType(str, Enum): | |
| """Types of cards in the game.""" | |
| CHARACTER = "character" | |
| WEAPON = "weapon" | |
| ROOM = "room" | |
| class Card(BaseModel): | |
| """Represents a single card in the game.""" | |
| name: str | |
| card_type: CardType | |
| class Player(BaseModel): | |
| """Represents a player in the game.""" | |
| id: str | |
| name: str | |
| cards: List[Card] = Field(default_factory=list) | |
| is_active: bool = True | |
| current_room_index: int = 0 # Position on the board | |
| has_rolled: bool = False # Track if player rolled dice this turn | |
| class GameStatus(str, Enum): | |
| """Status of a game.""" | |
| WAITING = "waiting" # Waiting for players to join | |
| IN_PROGRESS = "in_progress" # Game is running | |
| FINISHED = "finished" # Game has ended | |
| class Turn(BaseModel): | |
| """Represents a turn action in the game.""" | |
| player_id: str | |
| player_name: str | |
| action: str # "move", "suggest", "accuse", "pass" | |
| details: Optional[str] = None | |
| ai_comment: Optional[str] = None # Desland's sarcastic comment | |
| timestamp: str | |
| class Solution(BaseModel): | |
| """The secret solution to the mystery.""" | |
| character: Card | |
| weapon: Card | |
| room: Card | |
| class InvestigationNote(BaseModel): | |
| """Player's notes on the investigation.""" | |
| player_id: str | |
| element_name: str # Name of suspect/weapon/room | |
| element_type: str # "suspect", "weapon", "room" | |
| status: str # "unknown", "eliminated", "maybe" | |
| class RoomPosition(BaseModel): | |
| """Position of a room on the board.""" | |
| name: str | |
| x: int # Grid X position | |
| y: int # Grid Y position | |
| class BoardLayout(BaseModel): | |
| """Board layout configuration.""" | |
| rooms: List[RoomPosition] = Field(default_factory=list) | |
| grid_width: int = 8 | |
| grid_height: int = 8 | |
| class Game(BaseModel): | |
| """Represents a complete game instance.""" | |
| game_id: str | |
| name: str | |
| status: GameStatus = GameStatus.WAITING | |
| # Theme and narrative | |
| narrative_tone: str = NarrativeTone.SERIOUS.value | |
| custom_prompt: Optional[str] = None | |
| # Game elements (customizable) | |
| rooms: List[str] | |
| custom_weapons: List[str] = Field(default_factory=list) | |
| custom_suspects: List[str] = Field(default_factory=list) | |
| use_ai: bool = False | |
| # Players | |
| players: List[Player] = Field(default_factory=list) | |
| max_players: int = 8 | |
| current_player_index: int = 0 | |
| # Cards | |
| characters: List[Card] = Field(default_factory=list) | |
| weapons: List[Card] = Field(default_factory=list) | |
| room_cards: List[Card] = Field(default_factory=list) | |
| # Solution | |
| solution: Optional[Solution] = None | |
| # Game state | |
| turns: List[Turn] = Field(default_factory=list) | |
| winner: Optional[str] = None | |
| # Investigation notes (for UI) | |
| investigation_notes: List[InvestigationNote] = Field(default_factory=list) | |
| # Board layout | |
| board_layout: Optional[BoardLayout] = None | |
| # AI-generated content | |
| scenario: Optional[str] = None | |
| def generate_game_id() -> str: | |
| """Generate a unique 4-character game ID (like AB7F).""" | |
| chars = string.ascii_uppercase + string.digits | |
| return ''.join(random.choices(chars, k=4)) | |
| def add_player(self, player_name: str) -> Player: | |
| """Add a new player to the game.""" | |
| player_id = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8)) | |
| # All players start in the first room | |
| player = Player(id=player_id, name=player_name, current_room_index=0) | |
| self.players.append(player) | |
| return player | |
| def get_current_player(self) -> Optional[Player]: | |
| """Get the player whose turn it is.""" | |
| if not self.players: | |
| return None | |
| return self.players[self.current_player_index] | |
| def next_turn(self): | |
| """Move to the next active player's turn.""" | |
| if not self.players: | |
| return | |
| # Reset has_rolled for current player | |
| if self.current_player_index < len(self.players): | |
| self.players[self.current_player_index].has_rolled = False | |
| # Skip eliminated players | |
| attempts = 0 | |
| while attempts < len(self.players): | |
| self.current_player_index = (self.current_player_index + 1) % len(self.players) | |
| if self.players[self.current_player_index].is_active: | |
| break | |
| attempts += 1 | |
| def is_full(self) -> bool: | |
| """Check if the game has reached maximum players.""" | |
| return len(self.players) >= self.max_players | |
| class CreateGameRequest(BaseModel): | |
| """Request to create a new game.""" | |
| game_name: str | |
| narrative_tone: str = NarrativeTone.SERIOUS.value | |
| custom_prompt: Optional[str] = None | |
| rooms: List[str] | |
| custom_weapons: List[str] | |
| custom_suspects: List[str] | |
| use_ai: bool = False | |
| board_layout: Optional[BoardLayout] = None | |
| class JoinGameRequest(BaseModel): | |
| """Request to join an existing game.""" | |
| game_id: str | |
| player_name: str | |
| class GameAction(BaseModel): | |
| """Request to perform a game action.""" | |
| game_id: str | |
| player_id: str | |
| action_type: str # "move", "suggest", "accuse", "pass" | |
| # For movement | |
| dice_roll: Optional[int] = None | |
| target_room_index: Optional[int] = None | |
| # For suggestions/accusations | |
| character: Optional[str] = None | |
| weapon: Optional[str] = None | |
| room: Optional[str] = None | |