selena/modules/games/wordle.py

167 lines
6.7 KiB
Python
Raw Normal View History

import discord
import random
import logging
import sqlite3
from datetime import datetime
from discord.ext import tasks
from discord import app_commands
from cryptography.fernet import Fernet
class WordleGame:
def __init__(self, user_id, guild_id, target_word):
self.user_id = user_id
self.guild_id = guild_id
self.target_word = target_word
self.guesses = []
self.max_attempts = 6
def make_guess(self, guess):
if len(guess) != 5 or not guess.isalpha():
raise ValueError("Invalid guess. Must be a 5-letter word.")
feedback = [""] * 5
for i, char in enumerate(guess):
if char == self.target_word[i]:
feedback[i] = "🟩"
elif char in self.target_word:
feedback[i] = "🟨"
self.guesses.append((guess, "".join(feedback)))
return feedback
def is_game_over(self):
return len(self.guesses) >= self.max_attempts or any(guess == self.target_word for guess, _ in self.guesses)
def is_winner(self):
return any(guess == self.target_word for guess, _ in self.guesses)
def render_game(self):
game_board = "\n".join([f"{guess}: {feedback}" for guess, feedback in self.guesses])
return f"```\n{game_board}\n```"
class Wordle:
def __init__(self, bot):
self.bot = bot
self.games = {}
self.logger = logging.getLogger('Wordle')
self.logger.setLevel(logging.DEBUG)
handler = logging.FileHandler(filename='log/selena.log', encoding='utf-8', mode='w')
handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s:%(message)s'))
self.logger.addHandler(handler)
self.db_path = 'data/selena.db'
# Load the word list
self.load_word_list()
self.ensure_table_exists()
def load_word_list(self):
with open('data/wordlist.key', 'rb') as key_file:
key = key_file.read()
cipher_suite = Fernet(key)
with open('data/encrypted_word_list.bin', 'rb') as f:
encrypted_words = f.read().splitlines()
self.word_list = [cipher_suite.decrypt(word).decode() for word in encrypted_words]
def ensure_table_exists(self):
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS wordle_games (
user_id TEXT NOT NULL,
guild_id TEXT NOT NULL,
date TEXT NOT NULL,
target_word TEXT NOT NULL,
guesses TEXT,
PRIMARY KEY (user_id, guild_id, date)
);
""")
conn.commit()
conn.close()
def setup(self, tree: app_commands.CommandTree):
@tree.command(name="start_wordle", description="Start a new game of Wordle")
async def start_wordle_command(interaction: discord.Interaction):
user_id = str(interaction.user.id)
guild_id = str(interaction.guild.id)
date_str = datetime.utcnow().strftime('%Y-%m-%d')
# Check if the user has already played today's game
if self.has_played_today(user_id, guild_id, date_str):
await interaction.response.send_message("You have already played today's Wordle. Try again tomorrow!", ephemeral=True)
return
target_word = random.choice(self.word_list)
game = WordleGame(user_id, guild_id, target_word)
self.games[user_id] = game
# Save the game to the database
self.save_game(user_id, guild_id, date_str, target_word)
await interaction.response.send_message(f"New game of Wordle started!\n{game.render_game()}\nMake your guess using /guess_wordle [word]")
@tree.command(name="guess_wordle", description="Make a guess in your Wordle game")
async def guess_wordle_command(interaction: discord.Interaction, guess: str):
user_id = str(interaction.user.id)
game = self.games.get(user_id)
if not game:
await interaction.response.send_message("You don't have an active game. Start one using /start_wordle.", ephemeral=True)
return
try:
feedback = game.make_guess(guess.lower())
if game.is_game_over():
if game.is_winner():
await self.bot.profiles.record_win(user_id, str(interaction.guild.id), "wordle")
await interaction.response.send_message(f"Congratulations! You guessed the word {game.target_word}!\n{game.render_game()}")
else:
await self.bot.profiles.record_loss(user_id, str(interaction.guild.id), "wordle")
await interaction.response.send_message(f"Game over! The word was {game.target_word}.\n{game.render_game()}")
del self.games[user_id]
else:
await interaction.response.send_message(f"{game.render_game()}\nYour guess: {guess}\nFeedback: {''.join(feedback)}")
except ValueError as e:
await interaction.response.send_message(str(e), ephemeral=True)
@tree.command(name="end_wordle", description="End your current game of Wordle")
async def end_wordle_command(interaction: discord.Interaction):
user_id = str(interaction.user.id)
if user_id in self.games:
del self.games[user_id]
await interaction.response.send_message("Your game has been ended.")
else:
await interaction.response.send_message("You don't have an active game to end.", ephemeral=True)
if not tree.get_command("start_wordle"):
tree.add_command(start_wordle_command)
if not tree.get_command("guess_wordle"):
tree.add_command(guess_wordle_command)
if not tree.get_command("end_wordle"):
tree.add_command(end_wordle_command)
def has_played_today(self, user_id, guild_id, date_str):
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("SELECT 1 FROM wordle_games WHERE user_id = ? AND guild_id = ? AND date = ?", (user_id, guild_id, date_str))
result = cursor.fetchone()
conn.close()
return result is not None
def save_game(self, user_id, guild_id, date_str, target_word):
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute("INSERT INTO wordle_games (user_id, guild_id, date, target_word) VALUES (?, ?, ?, ?)", (user_id, guild_id, date_str, target_word))
conn.commit()
conn.close()
def setup(bot):
wordle = Wordle(bot)
wordle.setup(bot.tree)
bot.wordle_module = wordle