diff --git a/.gitignore b/.gitignore index a97294d..2612161 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# ========================= +# Sphinx documentation +# ========================= +docs/_build/ + # ========================= # Python / general # ========================= diff --git a/README.md b/README.md index f7c8cab..9326382 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,50 @@ docker compose down ``` --- +## Documentation + +Developer documentation is built with [Sphinx](https://www.sphinx-doc.org/) from the [`docs/`](docs/) directory (autodoc, Google-style docstrings via Napoleon, viewcode). + +**Install dependencies** (includes Sphinx): + +```bash +pip install -r requirements.txt +``` + +**Generate HTML** (either command from the repository root): + +```bash +cd docs && make html +``` + +```bash +sphinx-build -b html docs docs/_build +``` + +Open `docs/_build/html/index.html` in a browser. + +**Rebuild after code or docstring changes** (clean output, then build again): + +```bash +cd docs && make clean html +``` + +Or remove the build directory and run `sphinx-build` again: + +```bash +rm -rf docs/_build && sphinx-build -b html docs docs/_build +``` + +**Using Docker** (from the project root, with the stack running and the app image built): + +```bash +docker compose exec web sphinx-build -b html docs docs/_build +``` + +The `docs/_build/` directory is gitignored. + +--- + ## 🚀 Stack - Django, REST, Channels - Redis diff --git a/accounts/models.py b/accounts/models.py index e3dfa12..2d3c425 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -13,38 +13,11 @@ class User(AbstractUser): """Extended user model for the Durak card game application. - This model extends Django's ``AbstractUser`` to support additional - game-specific fields and convenience methods. A UUID is used as a - primary key to improve security, avoid predictable identifiers, and - support distributed systems. - - Attributes: - id (UUIDField): Primary key using UUID4. - avatar_url (URLField): Optional URL to the user's avatar image. - created_at (DateTimeField): Timestamp of when the user account was created. - - Inherited Attributes from ``AbstractUser``: - username, email, password, first_name, last_name, - is_active, is_staff, is_superuser, - date_joined, last_login - - Reverse Relations: - sent_messages (QuerySet[Message]): Messages sent by the user. - received_messages (QuerySet[Message]): Private messages received by the user. - lobby_set (QuerySet[Lobby]): Lobbies created by the user. - lobbyplayer_set (QuerySet[LobbyPlayer]): Lobby participation records. - gameplayer_set (QuerySet[GamePlayer]): Game participation records. - playerhand_set (QuerySet[PlayerHand]): Cards owned by the user in a match. - turn_set (QuerySet[Turn]): Turns made by the user. - - Example: - user = User.objects.create_user( - username="player1", - email="player1@example.com", - password="secure_password" - ) - user.avatar_url = "https://example.com/avatar.jpg" - user.save() + Extends Django ``AbstractUser`` with game-specific fields and helpers. + Uses a UUID primary key. Inherits standard auth fields from ``AbstractUser`` + (username, email, password, ``is_active``, ``is_staff``, etc.). + Reverse relations include lobby membership, game participation, messages, + hands, and turns. """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -204,18 +177,7 @@ class Block(models.Model): A block prevents the ``blocked`` user from interacting with the ``blocker`` (e.g., sending messages, joining their lobby, sending invites). - - Attributes: - blocker (ForeignKey[User]): The user who initiated the block. - blocked (ForeignKey[User]): The user who is being blocked. - created_at (DateTimeField): Timestamp of when the block was created. - - Constraints: - - A user cannot block the same user more than once (unique_together). - - Indexed lookups for efficient permission checks. - - Example: - Block.objects.create(blocker=user1, blocked=user2) + The pair ``(blocker, blocked)`` is unique and indexed for lookups. """ blocker = models.ForeignKey( diff --git a/chat/models.py b/chat/models.py index 9dd1116..93ecccb 100644 --- a/chat/models.py +++ b/chat/models.py @@ -87,17 +87,6 @@ class Chat(models.Model): - automatically created lobby chats (is_lobby=True) Messages are always attached to a Chat, not directly to a Lobby or users. - - Attributes: - id (UUID): Unique identifier for the chat. - name (str): Optional name (e.g. "Lobby #1"). - description (str): Optional description. - is_group (bool): Whether the chat supports multiple participants. - is_lobby (bool): Whether the chat belongs to a game lobby. - is_global (bool): Whether this is a global/world channel (not lobby-bound). - lobby (ForeignKey): Optional reference to a Lobby object. - dm_pair_key (str): For 1-on-1 chats only; stable key for the user pair. - created_at (datetime): Timestamp of creation. """ objects = ChatManager() @@ -229,12 +218,6 @@ class ChatParticipant(models.Model): """Represents a user's membership in a chat with assigned permissions. Each user can belong to multiple chats and have different roles in each. - - Attributes: - chat (Chat): The chat the user participates in. - user (User): The participating user. - role (str): Permission level ("owner", "admin", "member"). - joined_at (datetime): When the user joined the chat. """ ROLE_CHOICES = [ @@ -282,20 +265,12 @@ def demote(self): self.role = "member" self.save(update_fields=["role"]) - class Message(models.Model): """Represents a text message inside a chat. - Messages belong strictly to a Chat instance. Lobby messages and private - messages are simply different chat types — there are no separate fields - for lobby/receiver. - - Attributes: - id (UUID): Unique message identifier. - sender (User): The user who sent the message. - chat (Chat): Chat to which the message belongs. - content (str): Text content. - sent_at (datetime): Timestamp of message creation. + Messages belong strictly to a ``Chat`` instance. Lobby and direct messages + differ by chat type only; there are no separate lobby or receiver fields + on this model. """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..f423a41 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,15 @@ +# Minimal makefile for Sphinx documentation +# + +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +.PHONY: help Makefile + +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..22a8dbc --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,38 @@ +# pylint: skip-file +"""Sphinx configuration for Fools_Arena.""" + +import os +import sys + +sys.path.insert(0, os.path.abspath("..")) +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Fools_Arena.settings") + +import django + +django.setup() + +project = "Fools_Arena" +copyright = "2026, Maksim Bayarchuk, Kiryl Alishkevich, Aliaksandr Saroka" +author = "Maksim Bayarchuk, Kiryl Alishkevich, Aliaksandr Saroka" +release = "0.1" + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", +] + +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +napoleon_google_docstring = True +napoleon_numpy_docstring = False + +html_theme = "alabaster" +html_static_path = ["_static"] + +autodoc_default_options = { + "members": True, + "undoc-members": True, + "show-inheritance": True, +} diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..22f1bc6 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,18 @@ +Fools_Arena developer documentation +=================================== + +Online multiplayer **Durak** (Fool) card game — Django and Django Channels. + +.. toctree:: + :maxdepth: 2 + :caption: Reference + + models + views + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/models.rst b/docs/models.rst new file mode 100644 index 0000000..cca3705 --- /dev/null +++ b/docs/models.rst @@ -0,0 +1,8 @@ +Models +====== + +.. automodule:: accounts.models + +.. automodule:: chat.models + +.. automodule:: game.models diff --git a/docs/views.rst b/docs/views.rst new file mode 100644 index 0000000..918bb1a --- /dev/null +++ b/docs/views.rst @@ -0,0 +1,12 @@ +Views +===== + +.. automodule:: accounts.views + +.. automodule:: accounts.api_views + +.. automodule:: chat.views + +.. automodule:: chat.api_views + +.. automodule:: game.views diff --git a/game/models.py b/game/models.py index e4345de..2133fdd 100644 --- a/game/models.py +++ b/game/models.py @@ -8,28 +8,12 @@ import uuid from django.db import models - class CardSuit(models.Model): """Card suit model representing playing card suits (Hearts, Diamonds, etc.). Defines the four traditional card suits with their display names and colors. Used as a lookup table for card generation and game logic. - Attributes: - id (SmallAutoField): Primary key with small integer for efficiency. - name (CharField): Display name of the suit (e.g., "Hearts", "Spades"). - color (CharField): Color of the suit, either "red" or "black". - - Color Choices: - - 'red': For Hearts and Diamonds - - 'black': For Clubs and Spades - - Example: - # Create a heart suit - hearts = CardSuit.objects.create( - name="Hearts", - color="red" - ) """ id = models.SmallAutoField(primary_key=True) @@ -57,24 +41,12 @@ class Meta: verbose_name_plural = 'Card Suits' ordering = ['name'] - class CardRank(models.Model): """Card rank model representing playing card values (Ace, King, etc.). Defines the ranks/values of playing cards with their display names and numeric values for comparison during gameplay. - Attributes: - id (SmallAutoField): Primary key with small integer for efficiency. - name (CharField): Display name of the rank (e.g., "Ace", "King"). - value (IntegerField): Numeric value used for card comparison and game logic. - - Example: - # Create an Ace card rank - ace = CardRank.objects.create( - name="Ace", - value=14 # Highest value in most variations - ) """ id = models.SmallAutoField(primary_key=True) @@ -102,7 +74,6 @@ class Meta: verbose_name_plural = 'Card Ranks' ordering = ['value'] - class Lobby(models.Model): """Game lobby model for organizing players before starting games. @@ -110,34 +81,6 @@ class Lobby(models.Model): to start a Durak game session. Handles lobby ownership, privacy settings, and player management. - Attributes: - id (UUIDField): Unique identifier for the lobby. - owner (ForeignKey): Reference to the User who created the lobby. - name (CharField): Display name of the lobby. - is_private (BooleanField): Whether the lobby requires a password to join. - password_hash (CharField, optional): Hashed password for private lobbies. - status (CharField): Current lobby state. - created_at (DateTimeField): When the lobby was created. - - Related Objects: - settings: LobbySettings object with game configuration (OneToOne). - players: LobbyPlayer objects representing users in this lobby. - games: Game objects that have been played in this lobby. - messages: Chat messages sent in this lobby. - - Status Choices: - - 'waiting': Lobby is open and waiting for players - - 'playing': Game is currently in progress - - 'closed': Lobby has been closed and is no longer accessible - - Example: - # Create a public lobby - lobby = Lobby.objects.create( - owner=user, - name="Beginner's Game", - is_private=False, - status='waiting' - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -190,7 +133,6 @@ class Meta: verbose_name_plural = 'Lobbies' ordering = ['-created_at'] - class LobbySettings(models.Model): """Configuration settings for a game lobby. @@ -198,32 +140,6 @@ class LobbySettings(models.Model): started within the associated lobby. Each lobby has exactly one settings configuration. - Attributes: - id (UUIDField): Unique identifier for the settings. - lobby (OneToOneField): Reference to the associated Lobby. - max_players (PositiveIntegerField): Maximum number of players allowed. - card_count (IntegerField): Number of cards to use in the deck. - is_transferable (BooleanField): Whether cards can be transferred between players. - neighbor_throw_only (BooleanField): Whether only neighbors can throw in additional cards. - allow_jokers (BooleanField): Whether joker cards are included in the deck. - turn_time_limit (IntegerField, optional): Time limit per turn in seconds. - special_rule_set (ForeignKey, optional): Reference to a special rule configuration. - - Card Count Choices: - - 24: Short deck (9, 10, J, Q, K, A of each suit) - - 36: Standard deck (6, 7, 8, 9, 10, J, Q, K, A of each suit) - - 52: Full deck (all cards including 2-5) - - Example: - # Create standard game settings - settings = LobbySettings.objects.create( - lobby=lobby, - max_players=4, - card_count=36, - is_transferable=True, - neighbor_throw_only=False, - allow_jokers=False - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -269,7 +185,6 @@ class Meta: verbose_name = 'Lobby Settings' verbose_name_plural = 'Lobby Settings' - class LobbyPlayer(models.Model): """Relationship model connecting users to lobbies with their status. @@ -277,29 +192,6 @@ class LobbyPlayer(models.Model): current status and readiness to play. Handles the player lifecycle from joining to leaving the lobby. - Attributes: - id (UUIDField): Unique identifier for the lobby membership. - lobby (ForeignKey): Reference to the Lobby the player has joined. - user (ForeignKey): Reference to the User who joined the lobby. - status (CharField): Current status of the player in the lobby. - - Status Choices: - - 'waiting': Player has joined but is not ready to start - - 'ready': Player is ready to start a game - - 'playing': Player is currently in an active game - - 'left': Player has left the lobby - - Example: - # Add a player to a lobby - player = LobbyPlayer.objects.create( - lobby=lobby, - user=user, - status='waiting' - ) - - # Mark player as ready - player.status = 'ready' - player.save() """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -347,7 +239,6 @@ class Meta: unique_together = ['lobby', 'user'] ordering = ['lobby', 'user__username'] - class Game(models.Model): """Core game session model for Durak card game. @@ -355,40 +246,6 @@ class Game(models.Model): Handles game state, trump card selection, and player management. Each game is linked to a specific lobby and tracks all game-related data. - Attributes: - id (UUIDField): Unique identifier for the game session. - lobby (ForeignKey): Reference to the Lobby where this game is played. - trump_card (ForeignKey): The card that determines the trump suit for this game. - started_at (DateTimeField): When the game session began. - finished_at (DateTimeField, optional): When the game ended (null for active games). - status (CharField): Current game state ('in_progress' or 'finished'). - loser (ForeignKey, optional): Reference to the User who lost the game. - - Related Objects: - players: GamePlayer objects representing players in this game session. - deck_cards: GameDeck objects representing cards remaining in the deck. - hands: PlayerHand objects showing which cards each player holds. - table_cards: TableCard objects representing cards currently on the table. - discarded_cards: DiscardPile objects for cards that have been discarded. - turns: Turn objects tracking the sequence of player turns. - - Status Choices: - - 'in_progress': Game is currently being played - - 'finished': Game has ended with a winner/loser determined - - Example: - # Start a new game - game = Game.objects.create( - lobby=lobby, - trump_card=selected_trump_card, - status='in_progress' - ) - - # End the game with a loser - game.status = 'finished' - game.loser = losing_player - game.finished_at = timezone.now() - game.save() """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -446,28 +303,12 @@ class Meta: verbose_name_plural = 'Games' ordering = ['-started_at'] - class GamePlayer(models.Model): """Relationship model connecting users to game sessions with game-specific data. Represents a player's participation in a specific game, tracking their position, remaining cards, and other game-state information. - Attributes: - id (UUIDField): Unique identifier for the game participation. - game (ForeignKey): Reference to the Game session. - user (ForeignKey): Reference to the participating User. - seat_position (IntegerField): Player's position around the table (turn order). - cards_remaining (IntegerField): Number of cards currently in player's hand. - - Example: - # Add a player to a game - game_player = GamePlayer.objects.create( - game=game, - user=user, - seat_position=1, - cards_remaining=6 - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -514,7 +355,6 @@ class Meta: unique_together = ['game', 'user'] ordering = ['seat_position'] - class Card(models.Model): """Playing card model combining suit, rank, and optional special properties. @@ -522,30 +362,6 @@ class Card(models.Model): special abilities. Cards can be standard playing cards or special cards with unique effects. - Attributes: - id (UUIDField): Unique identifier for the card. - suit (ForeignKey): Reference to the CardSuit (Hearts, Diamonds, etc.). - rank (ForeignKey): Reference to the CardRank (Ace, King, etc.). - special_card (ForeignKey, optional): Reference to special card effects if applicable. - - Related Objects: - attack_card: TableCard objects where this card is the attacking card. - defense_card: TableCard objects where this card is the defending card. - as_trump: Game objects where this card serves as the trump card. - - Example: - # Create a standard playing card - card = Card.objects.create( - suit=hearts_suit, - rank=ace_rank - ) - - # Create a special card - special_card = Card.objects.create( - suit=spades_suit, - rank=joker_rank, - special_card=skip_effect - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -613,43 +429,12 @@ class Meta: verbose_name_plural = 'Cards' unique_together = ['suit', 'rank', 'special_card'] - class SpecialCard(models.Model): """Special card effects model for custom game mechanics. Defines special abilities that can be applied to cards to create unique gameplay mechanics beyond standard Durak rules. Each special card type has a specific effect and description. - - Attributes: - id (UUIDField): Unique identifier for the special card type. - name (CharField): Display name of the special effect. - effect_type (CharField): Category of the special effect. - effect_value (JSONField): JSON data containing effect parameters. - description (TextField): Human-readable description of the effect. - - Effect Types: - - 'skip': Skip the next player's turn - - 'reverse': Reverse turn order - - 'draw': Force target player to draw additional cards - - 'custom': Custom effect with parameters in effect_value - - Example: - # Create a skip effect card - skip_effect = SpecialCard.objects.create( - name="Skip Turn", - effect_type="skip", - effect_value={}, - description="Next player loses their turn" - ) - - # Create a draw effect card - draw_effect = SpecialCard.objects.create( - name="Draw Two", - effect_type="draw", - effect_value={"card_count": 2}, - description="Target player draws 2 additional cards" - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -705,38 +490,12 @@ class Meta: verbose_name_plural = 'Special Cards' ordering = ['name'] - class SpecialRuleSet(models.Model): """Special rule configuration for advanced game variants. Defines collections of special rules and cards that can be applied to lobbies to create custom game experiences. Each rule set can specify minimum player requirements and special card inclusions. - - Attributes: - id (UUIDField): Unique identifier for the rule set. - name (CharField): Display name of the rule set. - description (TextField): Detailed description of the rules. - min_players (IntegerField): Minimum players required for this rule set. - - Related Objects: - special_cards: SpecialCard objects included in this rule set (M2M through SpecialRuleSetCard). - lobby_settings: LobbySettings objects using this rule set. - - Example: - # Create a beginner-friendly rule set - beginner_rules = SpecialRuleSet.objects.create( - name="Beginner Special", - description="Simple special cards for new players", - min_players=2 - ) - - # Create an advanced rule set - advanced_rules = SpecialRuleSet.objects.create( - name="Master's Challenge", - description="Complex special effects for experienced players", - min_players=4 - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -799,34 +558,12 @@ class Meta: verbose_name_plural = 'Special Rule Sets' ordering = ['name'] - class SpecialRuleSetCard(models.Model): """Association model linking special cards to rule sets with configuration. Defines which special cards are included in specific rule sets and how they should be configured within that context. Allows for fine-tuned control over special card availability and behavior. - - Attributes: - id (UUIDField): Unique identifier for the association. - rule_set (ForeignKey): Reference to the SpecialRuleSet. - card (ForeignKey): Reference to the SpecialCard. - is_enabled (BooleanField): Whether this card is active in the rule set. - - Example: - # Add a special card to a rule set - SpecialRuleSetCard.objects.create( - rule_set=beginner_rules, - card=skip_effect, - is_enabled=True - ) - - # Add but disable a complex card for beginners - SpecialRuleSetCard.objects.create( - rule_set=beginner_rules, - card=complex_effect, - is_enabled=False - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -875,26 +612,12 @@ class Meta: unique_together = ['rule_set', 'card'] ordering = ['rule_set__name', 'card__name'] - class GameDeck(models.Model): """Model representing cards remaining in the deck during a game. Tracks the position and order of cards in the game deck. Cards are drawn from this deck when players need to replenish their hands. - Attributes: - id (UUIDField): Unique identifier for the deck entry. - game (ForeignKey): Reference to the Game session. - card (ForeignKey): Reference to the Card in the deck. - position (IntegerField): Position of the card in the deck (for draw order). - - Example: - # Add a card to the game deck - GameDeck.objects.create( - game=game, - card=card, - position=1 - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -956,28 +679,12 @@ class Meta: unique_together = ['game', 'position'] ordering = ['position'] - class PlayerHand(models.Model): """Model representing cards in a player's hand during a game. Tracks which cards each player holds, with optional ordering information for UI display purposes. - Attributes: - id (UUIDField): Unique identifier for the hand entry. - game (ForeignKey): Reference to the Game session. - player (ForeignKey): Reference to the User who holds the card. - card (ForeignKey): Reference to the Card in the player's hand. - order_in_hand (IntegerField, optional): Display order of card in hand. - - Example: - # Add a card to a player's hand - PlayerHand.objects.create( - game=game, - player=user, - card=card, - order_in_hand=1 - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -1040,7 +747,6 @@ class Meta: unique_together = ['game', 'player', 'card'] ordering = ['order_in_hand'] - class TableCard(models.Model): """Model representing attack and defense card pairs on the table. @@ -1048,25 +754,6 @@ class TableCard(models.Model): and can be defended by appropriate defending cards. This model tracks these attack-defense pairs. - Attributes: - id (UUIDField): Unique identifier for the table card pair. - game (ForeignKey): Reference to the Game session. - attack_card (ForeignKey): The card used for attack. - defense_card (ForeignKey, optional): The card used for defense (null if undefended). - - Related Objects: - moves: Move objects referencing this table card pair. - - Example: - # Place an attack card on the table - table_card = TableCard.objects.create( - game=game, - attack_card=seven_of_hearts - ) - - # Defend the attack - table_card.defense_card = ten_of_hearts - table_card.save() """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -1129,26 +816,12 @@ class Meta: verbose_name_plural = 'Table Cards' ordering = ['id'] - class DiscardPile(models.Model): """Model representing cards that have been discarded from the game. After successful defense rounds or when cards are played out, they are moved to the discard pile and removed from active play. - Attributes: - id (UUIDField): Unique identifier for the discard entry. - game (ForeignKey): Reference to the Game session. - card (ForeignKey): Reference to the discarded Card. - position (IntegerField, optional): Order in which cards were discarded. - - Example: - # Discard cards after a successful defense - DiscardPile.objects.create( - game=game, - card=attack_card, - position=1 - ) """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -1179,7 +852,6 @@ def discard_cards(cls, game, cards): last_position = cls.objects.filter(game=game).count() discard_entries = [] - class Turn(models.Model): """Turn tracking model for managing player turn sequence in games. @@ -1187,25 +859,6 @@ class Turn(models.Model): turn it is and maintaining the sequential order of gameplay. Each turn can contain multiple moves (attack, defend, pickup). - Attributes: - id (UUIDField): Unique identifier for the turn. - game (ForeignKey): Reference to the Game session this turn belongs to. - player (ForeignKey): Reference to the User whose turn it is. - turn_number (IntegerField): Sequential number of this turn in the game. - - Related Objects: - moves: Move objects that occurred during this turn. - - Example: - # Create a new turn - turn = Turn.objects.create( - game=game, - player=current_player, - turn_number=1 - ) - - # Get the next turn number - next_turn = Turn.objects.filter(game=game).count() + 1 """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -1273,7 +926,6 @@ class Meta: unique_together = ['game', 'turn_number'] ordering = ['turn_number'] - class Move(models.Model): """Game move model representing individual player actions during gameplay. @@ -1281,32 +933,6 @@ class Move(models.Model): attacking with cards, defending attacks, or picking up cards. Each move is associated with a turn and references the relevant table cards. - Attributes: - id (UUIDField): Unique identifier for the move. - turn (ForeignKey): Reference to the Turn this move belongs to. - table_card (ForeignKey): Reference to the TableCard affected by this move. - action_type (CharField): Type of action performed (attack, defend, pickup). - created_at (DateTimeField): Timestamp when the move was made. - - Action Types: - - 'attack': Player places an attacking card on the table - - 'defend': Player defends an attack with an appropriate card - - 'pickup': Player picks up undefended cards from the table - - Example: - # Record an attack move - attack_move = Move.objects.create( - turn=current_turn, - table_card=table_card, - action_type='attack' - ) - - # Record a defense move - defense_move = Move.objects.create( - turn=current_turn, - table_card=table_card, - action_type='defend' - ) """ ACTION_CHOICES = [ diff --git a/requirements.txt b/requirements.txt index 9325eed..76a5d9a 100644 Binary files a/requirements.txt and b/requirements.txt differ