Refactor: Remove legacy game commands

Removes the following legacy game commands from the Games cog:
- coinflipbet
- rps
- rpschallenge
- guess
- hangman

These commands were kept for backward compatibility but are no longer needed.
This commit is contained in:
Slipstream 2025-05-19 15:10:13 -06:00
parent fe6e4f2cc0
commit 154d7d665d
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD

View File

@ -578,433 +578,23 @@ class GamesCog(commands.Cog, name="Games"):
if hasattr(view, '_interaction'): del view._interaction
# --- Legacy Commands (kept for backward compatibility) ---
@app_commands.command(name="coinflipbet", description="Challenge another user to a coin flip game.")
@app_commands.describe(
opponent="The user you want to challenge."
)
async def coinflipbet(self, interaction: discord.Interaction, opponent: discord.User):
"""Initiates a coin flip game against another user."""
initiator = interaction.user
# --- Input Validation ---
if opponent.bot:
await interaction.response.send_message("You cannot challenge a bot!", ephemeral=True)
return
# --- Start the Game ---
view = CoinFlipView(initiator, opponent)
initial_message = f"{initiator.mention} has challenged {opponent.mention} to a coin flip game! {initiator.mention}, choose your side:"
# Send the initial message and store it in the view
await interaction.response.send_message(initial_message, view=view)
message = await interaction.original_response()
view.message = message
@app_commands.command(name="rps", description="Play Rock-Paper-Scissors against the bot.")
@app_commands.describe(choice="Your choice: Rock, Paper, or Scissors.")
@app_commands.choices(choice=[
app_commands.Choice(name="Rock 🪨", value="Rock"),
app_commands.Choice(name="Paper 📄", value="Paper"),
app_commands.Choice(name="Scissors ✂️", value="Scissors")
])
async def rps(self, interaction: discord.Interaction, choice: app_commands.Choice[str]):
"""Play Rock-Paper-Scissors against the bot."""
choices = ["Rock", "Paper", "Scissors"]
bot_choice = random.choice(choices)
user_choice = choice.value # Get value from choice
if user_choice == bot_choice:
result = "It's a tie!"
elif (user_choice == "Rock" and bot_choice == "Scissors") or \
(user_choice == "Paper" and bot_choice == "Rock") or \
(user_choice == "Scissors" and bot_choice == "Paper"):
result = "You win! 🎉"
else:
result = "You lose! 😢"
emojis = {
"Rock": "🪨",
"Paper": "📄",
"Scissors": "✂️"
}
await interaction.response.send_message(
f"You chose **{user_choice}** {emojis[user_choice]}\n"
f"I chose **{bot_choice}** {emojis[bot_choice]}\n\n"
f"{result}"
)
@app_commands.command(name="rpschallenge", description="Challenge another user to a game of Rock-Paper-Scissors.")
@app_commands.describe(opponent="The user you want to challenge.")
async def rpschallenge(self, interaction: discord.Interaction, opponent: discord.User):
"""Starts a Rock-Paper-Scissors game with another user."""
initiator = interaction.user
if opponent == initiator:
await interaction.response.send_message("You cannot challenge yourself!", ephemeral=True)
return
if opponent.bot:
await interaction.response.send_message("You cannot challenge a bot!", ephemeral=True)
return
view = RockPaperScissorsView(initiator, opponent)
initial_message = f"Rock Paper Scissors: {initiator.mention} vs {opponent.mention}\n\nChoose your move!"
await interaction.response.send_message(initial_message, view=view)
message = await interaction.original_response()
view.message = message
@app_commands.command(name="guess", description="Guess the number I'm thinking of (1-100).")
@app_commands.describe(guess="Your guess (1-100).")
async def guess(self, interaction: discord.Interaction, guess: int):
"""Guess the number the bot is thinking of."""
# Simple implementation: generate number per guess (no state needed)
number_to_guess = random.randint(1, 100)
if guess < 1 or guess > 100:
await interaction.response.send_message("Please guess a number between 1 and 100.", ephemeral=True)
return
if guess == number_to_guess:
await interaction.response.send_message(f"🎉 Correct! The number was **{number_to_guess}**.")
elif guess < number_to_guess:
await interaction.response.send_message(f"Too low! The number was {number_to_guess}.")
else:
await interaction.response.send_message(f"Too high! The number was {number_to_guess}.")
@app_commands.command(name="hangman", description="Play a game of Hangman.")
async def hangman(self, interaction: discord.Interaction):
"""Play a game of Hangman."""
await play_hangman(self.bot, interaction.channel, interaction.user)
@app_commands.command(name="tictactoe", description="Challenge another user to a game of Tic-Tac-Toe.")
@app_commands.describe(opponent="The user you want to challenge.")
async def tictactoe(self, interaction: discord.Interaction, opponent: discord.User):
"""Starts a Tic-Tac-Toe game with another user."""
initiator = interaction.user
if opponent == initiator:
await interaction.response.send_message("You cannot challenge yourself!", ephemeral=True)
return
if opponent.bot:
await interaction.response.send_message("You cannot challenge a bot! Use `/tictactoebot` instead.", ephemeral=True)
return
view = TicTacToeView(initiator, opponent)
initial_message = f"Tic Tac Toe: {initiator.mention} (X) vs {opponent.mention} (O)\n\nTurn: **{initiator.mention} (X)**"
await interaction.response.send_message(initial_message, view=view)
message = await interaction.original_response()
view.message = message # Store message for timeout handling
@app_commands.command(name="tictactoebot", description="Play a game of Tic-Tac-Toe against the bot.")
@app_commands.describe(difficulty="Bot difficulty: random, rule, or minimax (default: minimax)")
@app_commands.choices(difficulty=[
app_commands.Choice(name="Random (Easy)", value="random"),
app_commands.Choice(name="Rule-based (Medium)", value="rule"),
app_commands.Choice(name="Minimax (Hard)", value="minimax")
])
async def tictactoebot(self, interaction: discord.Interaction, difficulty: app_commands.Choice[str] = None):
"""Play a game of Tic-Tac-Toe against the bot."""
# Use default if no choice is made (discord.py handles default value assignment)
difficulty_value = difficulty.value if difficulty else "minimax"
# Ensure tictactoe module is importable
try:
import sys
import os
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if parent_dir not in sys.path:
sys.path.append(parent_dir)
from tictactoe import TicTacToe # Assuming tictactoe.py is in the parent directory
except ImportError:
await interaction.response.send_message("Error: TicTacToe game engine module not found.", ephemeral=True)
return
except Exception as e:
await interaction.response.send_message(f"Error importing TicTacToe module: {e}", ephemeral=True)
return
# Create a new game instance
try:
game = TicTacToe(ai_player='O', ai_difficulty=difficulty_value)
except Exception as e:
await interaction.response.send_message(f"Error initializing TicTacToe game: {e}", ephemeral=True)
return
# Create a view for the user interface
view = BotTicTacToeView(game, interaction.user)
await interaction.response.send_message(
f"Tic Tac Toe: {interaction.user.mention} (X) vs Bot (O) - Difficulty: {difficulty_value.capitalize()}\n\nYour turn!",
view=view
)
view.message = await interaction.original_response()
@app_commands.command(name="chess", description="Challenge another user to a game of chess.")
@app_commands.describe(opponent="The user you want to challenge.")
async def chess(self, interaction: discord.Interaction, opponent: discord.User):
"""Start a game of chess with another user."""
initiator = interaction.user
if opponent == initiator:
await interaction.response.send_message("You cannot challenge yourself!", ephemeral=True)
return
if opponent.bot:
await interaction.response.send_message("You cannot challenge a bot! Use `/chessbot` instead.", ephemeral=True)
return
# Initiator is white, opponent is black
view = ChessView(initiator, opponent)
initial_status = f"Turn: **{initiator.mention}** (White)"
initial_message = f"Chess: {initiator.mention} (White) vs {opponent.mention} (Black)\n\n{initial_status}"
board_image = generate_board_image(view.board) # Generate initial board image
await interaction.response.send_message(initial_message, file=board_image, view=view)
message = await interaction.original_response()
view.message = message
# Send initial DMs
asyncio.create_task(view._send_or_update_dm(view.white_player))
asyncio.create_task(view._send_or_update_dm(view.black_player))
@app_commands.command(name="chessbot", description="Play chess against the bot.")
@app_commands.describe(
color="Choose your color (default: White).",
variant="Choose the chess variant (default: Standard).",
skill_level="Bot skill level (0=Easy - 20=Hard, default: 10).",
think_time="Bot thinking time per move in seconds (0.1 - 5.0, default: 1.0)."
)
@app_commands.choices(
color=[
app_commands.Choice(name="White", value="white"),
app_commands.Choice(name="Black", value="black"),
],
variant=[
app_commands.Choice(name="Standard", value="standard"),
app_commands.Choice(name="Chess960 (Fischer Random)", value="chess960"),
# Add more variants here as supported
]
)
async def chessbot(self, interaction: discord.Interaction, color: app_commands.Choice[str] = None, variant: app_commands.Choice[str] = None, skill_level: int = 10, think_time: float = 1.0):
"""Starts a chess game against the Stockfish engine."""
player = interaction.user
player_color_str = color.value if color else "white"
variant_str = variant.value if variant else "standard"
player_color = chess.WHITE if player_color_str == "white" else chess.BLACK
# Validate inputs
skill_level = max(0, min(20, skill_level))
think_time = max(0.1, min(5.0, think_time))
# Check if variant is supported (currently standard and chess960)
supported_variants = ["standard", "chess960"]
if variant_str not in supported_variants:
await interaction.response.send_message(f"Sorry, the variant '{variant_str}' is not currently supported. Choose from: {', '.join(supported_variants)}", ephemeral=True)
return
# Defer response as engine start might take a moment
await interaction.response.defer()
view = ChessBotView(player, player_color, variant_str, skill_level, think_time)
# Start the engine asynchronously
# Store interaction temporarily for potential error reporting during init
view._interaction = interaction
await view.start_engine()
del view._interaction # Remove temporary attribute
if view.engine is None or view.is_finished(): # Check if engine failed or view stopped during init
# Error message should have been sent by start_engine or view stopped itself
# Ensure we don't try to send another response if already handled
# No need to send another message here, start_engine handles it.
print("ChessBotView: Engine failed to start, stopping command execution.")
return # Stop if engine failed
# Determine initial message based on who moves first
initial_status_prefix = "Your turn." if player_color == chess.WHITE else "Bot is thinking..."
initial_message_content = view.get_board_message(initial_status_prefix)
board_image = generate_board_image(view.board, perspective_white=(player_color == chess.WHITE))
# Send the initial game state using followup
message = await interaction.followup.send(initial_message_content, file=board_image, view=view, wait=True)
view.message = message
self.active_chess_bot_views[message.id] = view # Track the view
# Send initial DM to player
asyncio.create_task(view._send_or_update_dm())
# If bot moves first (player chose black), trigger its move
if player_color == chess.BLACK:
# Don't await this, let it run in the background
asyncio.create_task(view.make_bot_move())
@app_commands.command(name="loadchess", description="Load a chess game from FEN, PGN, or array representation.")
@app_commands.describe(
state="FEN string, PGN string, or board array (e.g., [['r',...],...]).",
turn="Whose turn? ('white' or 'black'). Required only for array state.",
opponent="Challenge a user (optional, defaults to playing the bot).",
color="Your color vs bot (White/Black). Required if playing vs bot.",
skill_level="Bot skill level (0-20, default: 10).",
think_time="Bot think time (0.1-5.0, default: 1.0)."
)
@app_commands.choices(
turn=[app_commands.Choice(name="White", value="white"), app_commands.Choice(name="Black", value="black")],
color=[app_commands.Choice(name="White", value="white"), app_commands.Choice(name="Black", value="black")]
)
async def loadchess(self, interaction: discord.Interaction,
state: str,
turn: Optional[app_commands.Choice[str]] = None,
opponent: Optional[discord.User] = None,
color: Optional[app_commands.Choice[str]] = None, # Now required for bot games
skill_level: int = 10,
think_time: float = 1.0):
"""Loads a chess game state (FEN, PGN, Array) and starts a view."""
await interaction.response.defer()
initiator = interaction.user
board = None
load_error = None
loaded_pgn_game = None # To store the loaded PGN game object if parsed
# --- Input Validation ---
if not opponent and not color:
await interaction.followup.send("The 'color' parameter is required when playing against the bot.", ephemeral=True)
return
# --- Parsing Logic ---
state_trimmed = state.strip()
# 1. Try parsing as PGN
if state_trimmed.startswith("[Event") or ('.' in state_trimmed and ('O-O' in state_trimmed or 'x' in state_trimmed or state_trimmed[0].isdigit())):
try:
pgn_io = io.StringIO(state_trimmed)
loaded_pgn_game = chess.pgn.read_game(pgn_io)
if loaded_pgn_game is None:
raise ValueError("Could not parse PGN data.")
# Get the board state from the end of the main line
board = loaded_pgn_game.end().board()
print("[Debug] Parsed as PGN.")
except Exception as e:
load_error = f"Could not parse as PGN: {e}. Trying other formats."
print(f"[Debug] PGN parsing failed: {e}")
loaded_pgn_game = None # Reset if PGN parsing failed
# 2. Try parsing as FEN (if not already parsed as PGN)
if board is None and '/' in state_trimmed and (' w ' in state_trimmed or ' b ' in state_trimmed):
try:
board = chess.Board(fen=state_trimmed)
print(f"[Debug] Parsed as FEN: {state_trimmed}")
except ValueError as e:
load_error = f"Invalid FEN string: {e}. Trying array format."
print(f"[Error] FEN parsing failed: {e}")
except Exception as e:
load_error = f"Unexpected FEN parsing error: {e}. Trying array format."
print(f"[Error] Unexpected FEN parsing error: {e}")
# 3. Try parsing as Array (if not parsed as PGN or FEN)
if board is None:
try:
# Check if it looks like a list before eval
if not state_trimmed.startswith('[') or not state_trimmed.endswith(']'):
raise ValueError("Input does not look like a list array.")
board_array = ast.literal_eval(state_trimmed)
print("[Debug] Attempting to parse as array...")
if not isinstance(board_array, list) or len(board_array) != 8 or \
not all(isinstance(row, list) and len(row) == 8 for row in board_array):
raise ValueError("Invalid array structure. Must be 8x8 list.")
if not turn:
load_error = "The 'turn' parameter is required when providing a board array."
else:
turn_color = chess.WHITE if turn.value == "white" else chess.BLACK
fen = self._array_to_fen(board_array, turn_color)
print(f"[Debug] Converted array to FEN: {fen}")
board = chess.Board(fen=fen)
except (ValueError, SyntaxError, TypeError) as e:
# If PGN/FEN failed, this is the final error message
load_error = f"Invalid state format. Could not parse as PGN, FEN, or Python list array. Error: {e}"
print(f"[Error] Array parsing failed: {e}")
except Exception as e:
load_error = f"Error parsing array state: {e}"
print(f"[Error] Unexpected array parsing error: {e}")
# --- Final Check and Error Handling ---
if board is None:
final_error = load_error or "Failed to load board state from the provided input."
await interaction.followup.send(final_error, ephemeral=True)
return
# --- Game Setup ---
if opponent:
# Player vs Player
if opponent == initiator:
await interaction.followup.send("You cannot challenge yourself!", ephemeral=True)
return
if opponent.bot:
await interaction.followup.send("You cannot challenge a bot! Use `/chessbot` or load without opponent.", ephemeral=True)
return
white_player = initiator if board.turn == chess.WHITE else opponent
black_player = opponent if board.turn == chess.WHITE else initiator
view = ChessView(white_player, black_player, board=board) # Pass loaded board
# If loaded from PGN, set the game object in the view
if loaded_pgn_game:
view.game_pgn = loaded_pgn_game
view.pgn_node = loaded_pgn_game.end() # Start from the end node
current_player_mention = white_player.mention if board.turn == chess.WHITE else black_player.mention
turn_color_name = "White" if board.turn == chess.WHITE else "Black"
initial_status = f"Turn: **{current_player_mention}** ({turn_color_name})"
if board.is_check(): initial_status += " **Check!**"
initial_message = f"Loaded Chess Game: {white_player.mention} (White) vs {black_player.mention} (Black)\n\n{initial_status}"
perspective_white = (board.turn == chess.WHITE)
board_image = generate_board_image(view.board, perspective_white=perspective_white)
message = await interaction.followup.send(initial_message, file=board_image, view=view, wait=True)
view.message = message
# Send initial DMs
asyncio.create_task(view._send_or_update_dm(view.white_player))
asyncio.create_task(view._send_or_update_dm(view.black_player))
else:
# Player vs Bot
player = initiator
# Color is now required, checked at the start
player_color = chess.WHITE if color.value == "white" else chess.BLACK
skill_level = max(0, min(20, skill_level))
think_time = max(0.1, min(5.0, think_time))
variant_str = "chess960" if board.chess960 else "standard"
view = ChessBotView(player, player_color, variant_str, skill_level, think_time, board=board) # Pass loaded board
# If loaded from PGN, set the game object in the view
if loaded_pgn_game:
view.game_pgn = loaded_pgn_game
view.pgn_node = loaded_pgn_game.end() # Start from the end node
view._interaction = interaction # For error reporting during start
await view.start_engine()
if hasattr(view, '_interaction'): del view._interaction
if view.engine is None or view.is_finished():
print("ChessBotView (Load): Engine failed to start, stopping command execution.")
return
status_prefix = "Your turn." if board.turn == player_color else "Bot is thinking..."
initial_message_content = view.get_board_message(status_prefix)
board_image = generate_board_image(view.board, perspective_white=(player_color == chess.WHITE))
message = await interaction.followup.send(initial_message_content, file=board_image, view=view, wait=True)
view.message = message
self.active_chess_bot_views[message.id] = view
# Send initial DM to player
asyncio.create_task(view._send_or_update_dm())
if board.turn != player_color:
asyncio.create_task(view.make_bot_move())
# --- Prefix Commands (Legacy Support) ---
@ -1038,6 +628,7 @@ class GamesCog(commands.Cog, name="Games"):
@commands.command(name="magic8ball", add_to_app_commands=False)
async def magic8ball_prefix(self, ctx: commands.Context, *, question: str):
"""(Prefix) Ask the magic 8 ball."""
# Note: question parameter is required for UX but not used in the response
response = magic8ball_response()
await ctx.send(f"🎱 {response}")