attempting to add chess
This commit is contained in:
parent
e00dacdbd3
commit
f680dcae17
93
reginaldCog/chess_addon.py
Normal file
93
reginaldCog/chess_addon.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import chess
|
||||||
|
import chess.svg
|
||||||
|
import io
|
||||||
|
from cairosvg import svg2png
|
||||||
|
import discord
|
||||||
|
|
||||||
|
class ChessHandler:
|
||||||
|
def __init__(self):
|
||||||
|
self.active_games = {} # {user_id: FEN string}
|
||||||
|
|
||||||
|
def set_board(self, user_id: str, fen: str):
|
||||||
|
"""Sets a board to a given FEN string for a user."""
|
||||||
|
try:
|
||||||
|
board = chess.Board(fen) # Validate FEN
|
||||||
|
self.active_games[user_id] = fen
|
||||||
|
return f"Board state updated successfully:\n```{fen}```"
|
||||||
|
except ValueError:
|
||||||
|
return "⚠️ Invalid FEN format. Please provide a valid board state."
|
||||||
|
|
||||||
|
def reset_board(self, user_id: str):
|
||||||
|
"""Resets a user's board to the standard starting position."""
|
||||||
|
self.active_games[user_id] = chess.STARTING_FEN
|
||||||
|
return "The board has been reset to the standard starting position."
|
||||||
|
|
||||||
|
def get_board(self, user_id: str):
|
||||||
|
"""Returns a chess.Board() instance based on stored FEN."""
|
||||||
|
fen = self.active_games.get(user_id, chess.STARTING_FEN)
|
||||||
|
return chess.Board(fen)
|
||||||
|
|
||||||
|
def get_fen(self, user_id: str):
|
||||||
|
"""Returns the current FEN of the user's board."""
|
||||||
|
return self.active_games.get(user_id, chess.STARTING_FEN)
|
||||||
|
|
||||||
|
def make_move(self, user_id: str, move: str):
|
||||||
|
"""Attempts to execute a move and checks if the game is over."""
|
||||||
|
board = self.get_board(user_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
board.push_san(move) # Execute move in standard algebraic notation
|
||||||
|
self.active_games[user_id] = board.fen() # Store FEN string instead of raw board object
|
||||||
|
|
||||||
|
if board.is_checkmate():
|
||||||
|
self.active_games.pop(user_id)
|
||||||
|
return f"Move executed: `{move}`. **Checkmate!** 🎉"
|
||||||
|
elif board.is_stalemate():
|
||||||
|
self.active_games.pop(user_id)
|
||||||
|
return f"Move executed: `{move}`. **Stalemate!** 🤝"
|
||||||
|
elif board.is_insufficient_material():
|
||||||
|
self.active_games.pop(user_id)
|
||||||
|
return f"Move executed: `{move}`. **Draw due to insufficient material.**"
|
||||||
|
elif board.can_claim_threefold_repetition():
|
||||||
|
self.active_games.pop(user_id)
|
||||||
|
return f"Move executed: `{move}`. **Draw by threefold repetition.**"
|
||||||
|
elif board.can_claim_fifty_moves():
|
||||||
|
self.active_games.pop(user_id)
|
||||||
|
return f"Move executed: `{move}`. **Draw by 50-move rule.**"
|
||||||
|
|
||||||
|
return f"Move executed: `{move}`.\nCurrent FEN:\n```{board.fen()}```"
|
||||||
|
except ValueError:
|
||||||
|
return "⚠️ Invalid move. Please enter a legal chess move."
|
||||||
|
|
||||||
|
def resign(self, user_id: str):
|
||||||
|
"""Handles player resignation."""
|
||||||
|
if user_id in self.active_games:
|
||||||
|
del self.active_games[user_id]
|
||||||
|
return "**You have resigned. Well played!** 🏳️"
|
||||||
|
return "No active game to resign from."
|
||||||
|
|
||||||
|
def get_board_state_text(self, user_id: str):
|
||||||
|
"""Returns the current FEN as a message."""
|
||||||
|
fen = self.active_games.get(user_id, chess.STARTING_FEN)
|
||||||
|
return f"Current board state (FEN):\n```{fen}```"
|
||||||
|
|
||||||
|
def get_board_state_image(self, user_id: str):
|
||||||
|
"""Generates a chessboard image from the current FEN and returns it as a file."""
|
||||||
|
fen = self.active_games.get(user_id, chess.STARTING_FEN)
|
||||||
|
board = chess.Board(fen)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Generate SVG representation of the board
|
||||||
|
svg_data = chess.svg.board(board)
|
||||||
|
|
||||||
|
# Convert SVG to PNG using cairosvg
|
||||||
|
png_data = svg2png(bytestring=svg_data)
|
||||||
|
|
||||||
|
# Store PNG in memory
|
||||||
|
image_file = io.BytesIO(png_data)
|
||||||
|
image_file.seek(0) # Ensure file pointer is reset before returning
|
||||||
|
|
||||||
|
return discord.File(image_file, filename="chessboard.png")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"⚠️ Error generating board image: {str(e)}"
|
||||||
@ -8,6 +8,9 @@ import traceback
|
|||||||
from collections import Counter
|
from collections import Counter
|
||||||
from redbot.core import Config, commands
|
from redbot.core import Config, commands
|
||||||
from openai import OpenAIError
|
from openai import OpenAIError
|
||||||
|
from .chess_addon import ChessHandler
|
||||||
|
|
||||||
|
chess_handler = ChessHandler()
|
||||||
|
|
||||||
class ReginaldCog(commands.Cog):
|
class ReginaldCog(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
@ -271,24 +274,91 @@ class ReginaldCog(commands.Cog):
|
|||||||
|
|
||||||
return None # No strong fact found
|
return None # No strong fact found
|
||||||
|
|
||||||
async def generate_response(self, api_key, messages):
|
async def generate_response(self, api_key, messages, ctx):
|
||||||
|
"""Handles AI responses and function calling for chess interactions."""
|
||||||
|
|
||||||
model = await self.config.openai_model()
|
model = await self.config.openai_model()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client = openai.AsyncClient(api_key=api_key)
|
client = openai.AsyncClient(api_key=api_key)
|
||||||
response = await client.chat.completions.create(
|
response = await client.chat.completions.create(
|
||||||
model=model,
|
model=model,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
max_tokens=4112,
|
max_tokens=1500, # Balanced token limit to allow function execution & flavor text
|
||||||
temperature=0.7,
|
temperature=0.7,
|
||||||
presence_penalty=0.5,
|
presence_penalty=0.5,
|
||||||
frequency_penalty=0.5
|
frequency_penalty=0.5,
|
||||||
|
functions=[
|
||||||
|
{
|
||||||
|
"name": "set_board",
|
||||||
|
"description": "Sets up the chessboard to a given FEN string.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fen": {"type": "string", "description": "The FEN string representing the board state."}
|
||||||
|
},
|
||||||
|
"required": ["fen"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "make_move",
|
||||||
|
"description": "Executes a chess move for the current game.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"move": {"type": "string", "description": "The move in SAN format (e.g., 'e2e4')."}
|
||||||
|
},
|
||||||
|
"required": ["move"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "reset_board",
|
||||||
|
"description": "Resets the chessboard to the default starting position.",
|
||||||
|
"parameters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "resign",
|
||||||
|
"description": "Resigns from the current chess game.",
|
||||||
|
"parameters": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "get_board_state_text",
|
||||||
|
"description": "Retrieves the current board state as a FEN string.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"user_id": {"type": "string", "description": "The user's unique ID."}
|
||||||
|
},
|
||||||
|
"required": ["user_id"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
)
|
)
|
||||||
response_text = response.choices[0].message.content.strip()
|
|
||||||
if response_text.startswith("Reginald:"):
|
|
||||||
response_text = response_text[len("Reginald:"):].strip()
|
|
||||||
return response_text
|
|
||||||
|
|
||||||
except OpenAIError as e:
|
response_data = response.choices[0].message
|
||||||
|
|
||||||
|
# 🟢 Check if OpenAI returned a function call
|
||||||
|
if response_data.get("function_call"):
|
||||||
|
function_call = response_data["function_call"]
|
||||||
|
function_name = function_call["name"]
|
||||||
|
function_args = json.loads(function_call["arguments"]) # Convert JSON string to dict
|
||||||
|
|
||||||
|
# 🟢 Call the appropriate function
|
||||||
|
if function_name == "set_board":
|
||||||
|
return chess_handler.set_board(ctx.author.id, function_args["fen"])
|
||||||
|
elif function_name == "make_move":
|
||||||
|
return chess_handler.make_move(ctx.author.id, function_args["move"])
|
||||||
|
elif function_name == "reset_board":
|
||||||
|
return chess_handler.reset_board(ctx.author.id)
|
||||||
|
elif function_name == "resign":
|
||||||
|
return chess_handler.resign(ctx.author.id)
|
||||||
|
elif function_name == "get_board_state_text":
|
||||||
|
return chess_handler.get_fen(ctx.author.id) # Returns FEN string of the board
|
||||||
|
|
||||||
|
# 🟢 If no function was called, return AI-generated response with flavor text
|
||||||
|
return response_data.get("content", "I'm afraid I have nothing to say.")
|
||||||
|
|
||||||
|
except openai.OpenAIError as e:
|
||||||
error_message = f"OpenAI Error: {e}"
|
error_message = f"OpenAI Error: {e}"
|
||||||
reginald_responses = [
|
reginald_responses = [
|
||||||
f"Regrettably, I must inform you that I have encountered a bureaucratic obstruction:\n\n```{error_message}```",
|
f"Regrettably, I must inform you that I have encountered a bureaucratic obstruction:\n\n```{error_message}```",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user