Skip to content

Conversation

@TheGoatInTheBoat
Copy link

@TheGoatInTheBoat TheGoatInTheBoat commented Sep 30, 2025

Summary by Sourcery

Add two new interactive game commands to the Discord bot

New Features:

  • Implement /tictactoe command for a turn-based Tic Tac Toe game between two users with board rendering and win/draw detection
  • Implement /higherlower command for a number guessing game within user-specified bounds with real-time feedback and timeout handling

YaoxuanZhang and others added 30 commits July 22, 2025 14:38
…many parameters. Also changed initialization of ticketBase in FeedbackCog, BugReportCog, and FeatureRequestCog (files that use TicketBase)
… old add_buttons. Future calls should use buttons: (True, WhatTheOldAddButtonsValueWasSetTo). Additionally refactored error handler cog
@sourcery-ai
Copy link

sourcery-ai bot commented Sep 30, 2025

Reviewer's Guide

Implements two new interactive game cogs—Tic Tac Toe and Higher/Lower—using Discord slash commands and message-based loops with timeouts to handle user input, game state, and result announcements.

Sequence diagram for Tic Tac Toe game interaction

sequenceDiagram
    actor User1
    participant Bot
    actor User2
    User1->>Bot: /tictactoe @User2
    Bot->>User1: Validate opponent
    Bot->>User1: Send initial board
    loop Turns (max 9)
        User1->>Bot: Send move (if turn)
        User2->>Bot: Send move (if turn)
        Bot->>User1: Update board, announce turn/result
        Bot->>User2: Update board, announce turn/result
    end
    Bot->>User1: Announce win/draw/timeout
    Bot->>User2: Announce win/draw/timeout
Loading

File-Level Changes

Change Details Files
Add interactive Tic Tac Toe slash command cog
  • Register /tictactoe command scoped to a debug guild
  • Initialize board state, player symbols, and turn tracking
  • Render the board as an emoji grid and detect wins or draws
  • Use bot.wait_for to capture moves with validation and timeout
  • Send follow-up messages for turns, wins, draws, invalid self-play, and timeouts
src/capy_app/frontend/cogs/games/tictactoe_cog.py
Add interactive Higher/Lower guessing slash command cog
  • Register /higherlower command with lower/upper bound parameters and validation
  • Pick a random target number and log it for debugging
  • Loop on bot.wait_for to collect numeric guesses with timeout
  • Provide high/low feedback and announce correct guesses
  • Handle invalid bounds and timeout game-over scenarios
src/capy_app/frontend/cogs/games/higherlower_cog.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `src/capy_app/frontend/cogs/games/tictactoe_cog.py:63` </location>
<code_context>
+            ephemeral=True,
+        )
+
+        def check(msg: discord.Message):
+            return (
+                msg.author == interaction.user
</code_context>

<issue_to_address>
**suggestion:** Message check function does not handle non-integer input gracefully.

Consider providing feedback to users when they send invalid input to enhance user experience.
</issue_to_address>

### Comment 2
<location> `src/capy_app/frontend/cogs/games/higherlower_cog.py:25-29` </location>
<code_context>
+        self, interaction: discord.Interaction, lower_bound: int, upper_bound: int
+    ):
+        """Higher/Lower guessing game."""
+        if lower_bound > upper_bound:
+            await interaction.response.send_message(
+                "❌ Lower bound must be <= upper bound", ephemeral=True
</code_context>

<issue_to_address>
**suggestion:** No check for negative or zero bounds.

Validate that both bounds are positive integers to prevent unexpected game behavior.

```suggestion
        if lower_bound <= 0 or upper_bound <= 0:
            await interaction.response.send_message(
                "❌ Both bounds must be positive integers greater than zero.", ephemeral=True
            )
            return

        if lower_bound > upper_bound:
            await interaction.response.send_message(
                "❌ Lower bound must be <= upper bound", ephemeral=True
            )
            return
```
</issue_to_address>

### Comment 3
<location> `src/capy_app/frontend/cogs/games/tictactoe_cog.py:20` </location>
<code_context>
+        name="tictactoe",
+        description="Play Tic Tac Toe against another user",
+    )
+    async def tictactoe(self, interaction: discord.Interaction, opponent: discord.User):
+        """Tic Tac Toe game between two players."""
+        if opponent == interaction.user:
</code_context>

<issue_to_address>
**issue (complexity):** Consider refactoring the game logic into a separate TicTacToeGame class to isolate board management and win/draw checks from Discord I/O.

```markdown
Extract the board‐management and win/draw logic into a separate `TicTacToeGame` class.  This collapses your slash command to just “wiring” Discord I/O, and makes the logic fully testable.

– Create a new module `tic_tac_toe.py`:

```python
# tic_tac_toe.py
class TicTacToeGame:
    WIN_COMBOS = [
        (0,1,2),(3,4,5),(6,7,8),  # rows
        (0,3,6),(1,4,7),(2,5,8),  # cols
        (0,4,8),(2,4,6),          # diags
    ]

    def __init__(self, players):
        self.players = players
        self.symbols = ["❌","⭕"]
        self.board = [" "] * 9
        self.turn = 0

    def render(self) -> str:
        def cell(i):
            return self.board[i] if self.board[i] != " " else f"{i+1}\N{COMBINING ENCLOSING KEYCAP}"
        rows = [
            f"{cell(0)} | {cell(1)} | {cell(2)}",
            f"{cell(3)} | {cell(4)} | {cell(5)}",
            f"{cell(6)} | {cell(7)} | {cell(8)}",
        ]
        return "\n----+---+----\n".join(rows)

    def make_move(self, pos: int) -> bool:
        if 0 <= pos < 9 and self.board[pos] == " ":
            self.board[pos] = self.symbols[self.turn]
            return True
        return False

    def is_win(self) -> bool:
        sym = self.symbols[self.turn]
        return any(all(self.board[i] == sym for i in combo) for combo in self.WIN_COMBOS)

    def is_draw(self) -> bool:
        return all(cell != " " for cell in self.board)

    def switch_turn(self):
        self.turn ^= 1

    @property
    def current_player(self):
        return self.players[self.turn]

    @property
    def current_symbol(self):
        return self.symbols[self.turn]
```

– Simplify your slash command:

```python
# in your Cog
from .tic_tac_toe import TicTacToeGame

@app_commands.command(...)
async def tictactoe(self, interaction, opponent: discord.User):
    if opponent == interaction.user:
        return await interaction.response.send_message("❌ You cannot play yourself", ephemeral=True)

    game = TicTacToeGame([interaction.user, opponent])
    await interaction.response.send_message(
        f"🎮 TicTacToe: {game.players[0].mention} vs {game.players[1].mention}\n"
        f"{game.current_player.mention}, it's your turn!\n{game.render()}"
    )

    def check(msg):
        return (
            msg.author == game.current_player
            and msg.channel == interaction.channel
            and msg.content.isdigit()
        )

    for _ in range(9):
        try:
            msg = await self.bot.wait_for("message", check=check, timeout=60)
        except asyncio.TimeoutError:
            return await interaction.followup.send("⌛ Game timed out!")

        move = int(msg.content) - 1
        if not game.make_move(move):
            await msg.reply("Invalid move, try again.")
            continue

        if game.is_win():
            return await interaction.followup.send(f"{game.render()}\n{game.current_player.mention} wins!")

        if game.is_draw():
            break

        game.switch_turn()
        await interaction.followup.send(
            f"{game.render()}\n{game.current_player.mention}, it's your turn!"
        )

    await interaction.followup.send(f"{game.render()}\n🤝 It's a draw!")
```

Benefits:
- Slash command is now <50 lines, no inner functions.
- All game logic lives in one class for easy unit‐testing.
- Turn management, rendering, win/draw checks are clear and decoupled from Discord I/O.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

f"{players[turn].mention}, it's your turn!\n{render_board()}"
)

def check(msg: discord.Message):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Message check function does not handle non-integer input gracefully.

Consider providing feedback to users when they send invalid input to enhance user experience.

@TheGoatInTheBoat TheGoatInTheBoat changed the base branch from develop to refactor/dev-mvp October 24, 2025 21:25
Base automatically changed from refactor/dev-mvp to develop November 7, 2025 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants