mirror of https://github.com/stijndcl/didier
Various small fixes all around
parent
c4ef5cd619
commit
000fa93180
|
@ -9,6 +9,7 @@ from database.schemas import WordleGuess, WordleWord
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"get_active_wordle_game",
|
"get_active_wordle_game",
|
||||||
|
"get_wordle_guesses",
|
||||||
"make_wordle_guess",
|
"make_wordle_guess",
|
||||||
"set_daily_word",
|
"set_daily_word",
|
||||||
"reset_wordle_games",
|
"reset_wordle_games",
|
||||||
|
@ -23,6 +24,12 @@ async def get_active_wordle_game(session: AsyncSession, user_id: int) -> list[Wo
|
||||||
return guesses
|
return guesses
|
||||||
|
|
||||||
|
|
||||||
|
async def get_wordle_guesses(session: AsyncSession, user_id: int) -> list[str]:
|
||||||
|
"""Get the strings of a player's guesses"""
|
||||||
|
active_game = await get_active_wordle_game(session, user_id)
|
||||||
|
return list(map(lambda g: g.guess.lower(), active_game))
|
||||||
|
|
||||||
|
|
||||||
async def make_wordle_guess(session: AsyncSession, user_id: int, guess: str):
|
async def make_wordle_guess(session: AsyncSession, user_id: int, guess: str):
|
||||||
"""Make a guess in your current game"""
|
"""Make a guess in your current game"""
|
||||||
guess_instance = WordleGuess(user_id=user_id, guess=guess)
|
guess_instance = WordleGuess(user_id=user_id, guess=guess)
|
||||||
|
@ -62,7 +69,6 @@ async def set_daily_word(session: AsyncSession, word: str, *, forced: bool = Fal
|
||||||
await reset_wordle_games(session)
|
await reset_wordle_games(session)
|
||||||
elif forced:
|
elif forced:
|
||||||
current_word.word = word
|
current_word.word = word
|
||||||
current_word.day = datetime.date.today()
|
|
||||||
session.add(current_word)
|
session.add(current_word)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
|
@ -76,3 +82,4 @@ async def reset_wordle_games(session: AsyncSession):
|
||||||
"""Reset all active games"""
|
"""Reset all active games"""
|
||||||
statement = delete(WordleGuess)
|
statement = delete(WordleGuess)
|
||||||
await session.execute(statement)
|
await session.execute(statement)
|
||||||
|
await session.commit()
|
||||||
|
|
|
@ -3,6 +3,7 @@ from datetime import date
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from database.crud.users import get_or_add_user
|
||||||
from database.schemas import WordleStats
|
from database.schemas import WordleStats
|
||||||
|
|
||||||
__all__ = ["get_wordle_stats", "complete_wordle_game"]
|
__all__ = ["get_wordle_stats", "complete_wordle_game"]
|
||||||
|
@ -13,6 +14,8 @@ async def get_wordle_stats(session: AsyncSession, user_id: int) -> WordleStats:
|
||||||
|
|
||||||
If no entry is found, it is first created
|
If no entry is found, it is first created
|
||||||
"""
|
"""
|
||||||
|
await get_or_add_user(session, user_id)
|
||||||
|
|
||||||
statement = select(WordleStats).where(WordleStats.user_id == user_id)
|
statement = select(WordleStats).where(WordleStats.user_id == user_id)
|
||||||
stats = (await session.execute(statement)).scalar_one_or_none()
|
stats = (await session.execute(statement)).scalar_one_or_none()
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
|
|
|
@ -8,6 +8,8 @@ from database.crud import links, memes, ufora_courses, wordle
|
||||||
|
|
||||||
__all__ = ["CacheManager", "LinkCache", "UforaCourseCache"]
|
__all__ = ["CacheManager", "LinkCache", "UforaCourseCache"]
|
||||||
|
|
||||||
|
from database.schemas import WordleWord
|
||||||
|
|
||||||
|
|
||||||
class DatabaseCache(ABC):
|
class DatabaseCache(ABC):
|
||||||
"""Base class for a simple cache-like structure
|
"""Base class for a simple cache-like structure
|
||||||
|
@ -118,10 +120,12 @@ class UforaCourseCache(DatabaseCache):
|
||||||
class WordleCache(DatabaseCache):
|
class WordleCache(DatabaseCache):
|
||||||
"""Cache to store the current daily Wordle word"""
|
"""Cache to store the current daily Wordle word"""
|
||||||
|
|
||||||
|
word: WordleWord
|
||||||
|
|
||||||
async def invalidate(self, database_session: AsyncSession):
|
async def invalidate(self, database_session: AsyncSession):
|
||||||
word = await wordle.get_daily_word(database_session)
|
word = await wordle.get_daily_word(database_session)
|
||||||
if word is not None:
|
if word is not None:
|
||||||
self.data = [word.word]
|
self.word = word
|
||||||
|
|
||||||
|
|
||||||
class CacheManager:
|
class CacheManager:
|
||||||
|
|
|
@ -4,10 +4,11 @@ import discord
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from database.constants import WORDLE_GUESS_COUNT, WORDLE_WORD_LENGTH
|
from database.constants import WORDLE_WORD_LENGTH
|
||||||
from database.crud.wordle import get_active_wordle_game, make_wordle_guess
|
from database.crud.wordle import get_wordle_guesses, make_wordle_guess
|
||||||
|
from database.crud.wordle_stats import complete_wordle_game
|
||||||
from didier import Didier
|
from didier import Didier
|
||||||
from didier.data.embeds.wordle import WordleEmbed, WordleErrorEmbed
|
from didier.data.embeds.wordle import WordleEmbed, WordleErrorEmbed, is_wordle_game_over
|
||||||
|
|
||||||
|
|
||||||
class Games(commands.Cog):
|
class Games(commands.Cog):
|
||||||
|
@ -31,14 +32,13 @@ class Games(commands.Cog):
|
||||||
embed = WordleErrorEmbed(message=f"Guess must be 5 characters, but `{guess}` is {len(guess)}.").to_embed()
|
embed = WordleErrorEmbed(message=f"Guess must be 5 characters, but `{guess}` is {len(guess)}.").to_embed()
|
||||||
return await interaction.followup.send(embed=embed)
|
return await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
word = self.client.database_caches.wordle_word.data[0].lower()
|
word_instance = self.client.database_caches.wordle_word.word
|
||||||
|
|
||||||
async with self.client.postgres_session as session:
|
async with self.client.postgres_session as session:
|
||||||
guesses_instances = await get_active_wordle_game(session, interaction.user.id)
|
guesses = await get_wordle_guesses(session, interaction.user.id)
|
||||||
guesses = list(map(lambda g: g.guess, guesses_instances))
|
|
||||||
|
|
||||||
# Trying to guess with a complete game
|
# Trying to guess with a complete game
|
||||||
if (len(guesses) == WORDLE_GUESS_COUNT and guess) or word in guesses:
|
if is_wordle_game_over(guesses, word_instance.word):
|
||||||
embed = WordleErrorEmbed(
|
embed = WordleErrorEmbed(
|
||||||
message="You've already completed today's Wordle.\nTry again tomorrow!"
|
message="You've already completed today's Wordle.\nTry again tomorrow!"
|
||||||
).to_embed()
|
).to_embed()
|
||||||
|
@ -58,9 +58,14 @@ class Games(commands.Cog):
|
||||||
# just append locally
|
# just append locally
|
||||||
guesses.append(guess)
|
guesses.append(guess)
|
||||||
|
|
||||||
embed = WordleEmbed(guesses=guesses, word=word).to_embed()
|
embed = WordleEmbed(guesses=guesses, word=word_instance).to_embed()
|
||||||
await interaction.followup.send(embed=embed)
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
# After responding to the interaction: update stats in the background
|
||||||
|
game_over = is_wordle_game_over(guesses, word_instance.word)
|
||||||
|
if game_over:
|
||||||
|
await complete_wordle_game(session, interaction.user.id, word_instance.word in guesses)
|
||||||
|
|
||||||
|
|
||||||
async def setup(client: Didier):
|
async def setup(client: Didier):
|
||||||
"""Load the cog"""
|
"""Load the cog"""
|
||||||
|
|
|
@ -141,8 +141,8 @@ class Tasks(commands.Cog):
|
||||||
async def reset_wordle_word(self, forced: bool = False):
|
async def reset_wordle_word(self, forced: bool = False):
|
||||||
"""Reset the daily Wordle word"""
|
"""Reset the daily Wordle word"""
|
||||||
async with self.client.postgres_session as session:
|
async with self.client.postgres_session as session:
|
||||||
word = await set_daily_word(session, random.choice(tuple(self.client.wordle_words)), forced=forced)
|
await set_daily_word(session, random.choice(tuple(self.client.wordle_words)), forced=forced)
|
||||||
self.client.database_caches.wordle_word.data = [word]
|
await self.client.database_caches.wordle_word.invalidate(session)
|
||||||
|
|
||||||
@reset_wordle_word.before_loop
|
@reset_wordle_word.before_loop
|
||||||
async def _before_reset_wordle_word(self):
|
async def _before_reset_wordle_word(self):
|
||||||
|
|
|
@ -5,10 +5,22 @@ import discord
|
||||||
from overrides import overrides
|
from overrides import overrides
|
||||||
|
|
||||||
from database.constants import WORDLE_GUESS_COUNT, WORDLE_WORD_LENGTH
|
from database.constants import WORDLE_GUESS_COUNT, WORDLE_WORD_LENGTH
|
||||||
|
from database.schemas import WordleWord
|
||||||
from didier.data.embeds.base import EmbedBaseModel
|
from didier.data.embeds.base import EmbedBaseModel
|
||||||
from didier.utils.types.datetime import int_to_weekday, tz_aware_now
|
from didier.utils.types.datetime import int_to_weekday, tz_aware_now
|
||||||
|
|
||||||
__all__ = ["WordleEmbed", "WordleErrorEmbed"]
|
__all__ = ["is_wordle_game_over", "WordleEmbed", "WordleErrorEmbed"]
|
||||||
|
|
||||||
|
|
||||||
|
def is_wordle_game_over(guesses: list[str], word: str) -> bool:
|
||||||
|
"""Check if the current game is over or not"""
|
||||||
|
if not guesses:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if len(guesses) == WORDLE_GUESS_COUNT:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return word.lower() in guesses
|
||||||
|
|
||||||
|
|
||||||
def footer() -> str:
|
def footer() -> str:
|
||||||
|
@ -31,17 +43,17 @@ class WordleEmbed(EmbedBaseModel):
|
||||||
"""Embed for a Wordle game"""
|
"""Embed for a Wordle game"""
|
||||||
|
|
||||||
guesses: list[str]
|
guesses: list[str]
|
||||||
word: str
|
word: WordleWord
|
||||||
|
|
||||||
def _letter_colour(self, guess: str, index: int) -> WordleColour:
|
def _letter_colour(self, guess: str, index: int) -> WordleColour:
|
||||||
"""Get the colour for a guess at a given position"""
|
"""Get the colour for a guess at a given position"""
|
||||||
if guess[index] == self.word[index]:
|
if guess[index] == self.word.word[index]:
|
||||||
return WordleColour.CORRECT
|
return WordleColour.CORRECT
|
||||||
|
|
||||||
wrong_letter = 0
|
wrong_letter = 0
|
||||||
wrong_position = 0
|
wrong_position = 0
|
||||||
|
|
||||||
for i, letter in enumerate(self.word):
|
for i, letter in enumerate(self.word.word):
|
||||||
if letter == guess[index] and guess[i] != guess[index]:
|
if letter == guess[index] and guess[i] != guess[index]:
|
||||||
wrong_letter += 1
|
wrong_letter += 1
|
||||||
|
|
||||||
|
@ -90,23 +102,13 @@ class WordleEmbed(EmbedBaseModel):
|
||||||
|
|
||||||
return emojis
|
return emojis
|
||||||
|
|
||||||
def _is_game_over(self) -> bool:
|
|
||||||
"""Check if the current game is over or not"""
|
|
||||||
if not self.guesses:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if len(self.guesses) == WORDLE_GUESS_COUNT:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return self.word.lower() in self.guesses
|
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def to_embed(self, **kwargs) -> discord.Embed:
|
def to_embed(self, **kwargs) -> discord.Embed:
|
||||||
only_colours = kwargs.get("only_colours", False)
|
only_colours = kwargs.get("only_colours", False)
|
||||||
|
|
||||||
colours = self.colour_code_game()
|
colours = self.colour_code_game()
|
||||||
|
|
||||||
embed = discord.Embed(colour=discord.Colour.blue(), title="Wordle")
|
embed = discord.Embed(colour=discord.Colour.blue(), title=f"Wordle #{self.word.word_id + 1}")
|
||||||
emojis = self._colours_to_emojis(colours)
|
emojis = self._colours_to_emojis(colours)
|
||||||
|
|
||||||
rows = [" ".join(row) for row in emojis]
|
rows = [" ".join(row) for row in emojis]
|
||||||
|
@ -117,8 +119,8 @@ class WordleEmbed(EmbedBaseModel):
|
||||||
rows[i] += f" ||{guess.upper()}||"
|
rows[i] += f" ||{guess.upper()}||"
|
||||||
|
|
||||||
# If the game is over, reveal the word
|
# If the game is over, reveal the word
|
||||||
if self._is_game_over():
|
if is_wordle_game_over(self.guesses, self.word.word):
|
||||||
rows.append(f"\n\nThe word was **{self.word.upper()}**!")
|
rows.append(f"\n\nThe word was **{self.word.word.upper()}**!")
|
||||||
|
|
||||||
embed.description = "\n\n".join(rows)
|
embed.description = "\n\n".join(rows)
|
||||||
embed.set_footer(text=footer())
|
embed.set_footer(text=footer())
|
||||||
|
|
|
@ -121,13 +121,12 @@ async def test_make_wordle_guess(postgres: AsyncSession, user: User):
|
||||||
|
|
||||||
guess = "guess"
|
guess = "guess"
|
||||||
await crud.make_wordle_guess(postgres, test_user_id, guess)
|
await crud.make_wordle_guess(postgres, test_user_id, guess)
|
||||||
wordle_guesses = await crud.get_active_wordle_game(postgres, test_user_id)
|
assert crud.get_wordle_guesses(postgres, test_user_id) == [guess]
|
||||||
assert list(map(lambda x: x.guess, wordle_guesses)) == [guess]
|
|
||||||
|
|
||||||
other_guess = "otherguess"
|
other_guess = "otherguess"
|
||||||
await crud.make_wordle_guess(postgres, test_user_id, other_guess)
|
await crud.make_wordle_guess(postgres, test_user_id, other_guess)
|
||||||
wordle_guesses = await crud.get_active_wordle_game(postgres, test_user_id)
|
await crud.get_active_wordle_game(postgres, test_user_id)
|
||||||
assert list(map(lambda x: x.guess, wordle_guesses)) == [guess, other_guess]
|
assert crud.get_wordle_guesses(postgres, test_user_id) == [guess, other_guess]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.postgres
|
@pytest.mark.postgres
|
||||||
|
|
Loading…
Reference in New Issue