diff options
author | 2021-07-08 13:51:14 -0400 | |
---|---|---|
committer | 2021-07-09 14:01:21 -0400 | |
commit | 4afbaf1106ecdb7fe9f6ff7c883b2fee180e9ff6 (patch) | |
tree | 04ea91a8617db4f39df559e3d0a5004f2dd59e4d /bot/exts | |
parent | Add initial embed and image manipulation functions (diff) |
Add listener to process answers
Diffstat (limited to 'bot/exts')
-rw-r--r-- | bot/exts/evergreen/duck_game.py | 66 |
1 files changed, 65 insertions, 1 deletions
diff --git a/bot/exts/evergreen/duck_game.py b/bot/exts/evergreen/duck_game.py index 96241ea1..a37b7277 100644 --- a/bot/exts/evergreen/duck_game.py +++ b/bot/exts/evergreen/duck_game.py @@ -1,5 +1,6 @@ import asyncio import random +import re from collections import defaultdict from io import BytesIO from itertools import product @@ -10,17 +11,25 @@ from PIL import Image, ImageDraw from discord.ext import commands from bot.bot import Bot -from bot.constants import Colours +from bot.constants import Colours, Emojis DECK = list(product(*[(0, 1, 2)]*4)) GAME_DURATION = 180 +CORRECT_SOLN = 1 +INCORRECT_SOLN = -1 +CORRECT_GOOSE = 2 +INCORRECT_GOOSE = -1 p = Path("bot", "resources", "evergreen", "all_cards.png") ALL_CARDS = Image.open(p) CARD_WIDTH = 155 CARD_HEIGHT = 97 +EMOJI_WRONG = Emojis.x + +ANSWER_REGEX = re.compile(r'^\D*(\d+)\D+(\d+)\D+(\d+)\D*$') + def assemble_board_image(board: list[tuple[int]], rows: int, columns: int) -> Image: """Cut and paste images representing the given cards into an image representing the board.""" @@ -71,6 +80,7 @@ class DuckGame: size = rows * columns self._solutions = None + self.claimed_answers = {} self.scores = defaultdict(int) self.board = random.sample(DECK, size) @@ -152,6 +162,52 @@ class DuckGamesDirector(commands.Cog): except KeyError: pass + @commands.Cog.listener() + async def on_message(self, msg: discord.Message) -> None: + """Listen for messages and process them as answers if appropriate.""" + if msg.author.bot: + return + + channel = msg.channel + if channel.id not in self.current_games: + return + + game = self.current_games[channel.id] + if msg.content.strip().lower() == 'goose': + # If all of the solutions have been claimed, i.e. the "goose" call is correct. + if len(game.solutions) == len(game.claimed_answers): + try: + del self.current_games[channel.id] + game.scores[msg.author] += CORRECT_GOOSE + await self.end_game(game, end_message=f"{msg.author.display_name} GOOSED!") + except KeyError: + pass + else: + await msg.add_reaction(EMOJI_WRONG) + game.scores[msg.author] += INCORRECT_GOOSE + return + + # Valid answers contain 3 numbers. + if not (match := re.match(ANSWER_REGEX, msg.content)): + return + answer = tuple(sorted(int(m) for m in match.groups())) + + # Be forgiving for answers that use indices not on the board. + if not all(0 <= n < len(game.board) for n in answer): + return + + # Also be forgiving for answers that have already been claimed (and avoid penalizing for racing conditions). + if answer in game.claimed_answers: + return + + if answer in game.solutions: + game.claimed_answers[answer] = msg.author + game.scores[msg.author] += CORRECT_SOLN + await self.display_claimed_answer(game, msg.author, answer) + else: + await msg.add_reaction(EMOJI_WRONG) + game.scores[msg.author] += INCORRECT_SOLN + async def send_board_embed(self, ctx: commands.Context, game: DuckGame) -> discord.Message: """Create and send the initial game embed. This will be edited as the game goes on.""" image = assemble_board_image(game.board, game.rows, game.columns) @@ -167,6 +223,14 @@ class DuckGamesDirector(commands.Cog): embed.set_image(url="attachment://board.png") return await ctx.send(embed=embed, file=file) + async def display_claimed_answer(self, game: DuckGame, author: discord.Member, answer: tuple[int]) -> None: + """Add a claimed answer to the game embed.""" + pass + + async def end_game(self, game: DuckGame, end_message: str) -> None: + """Edit the game embed to reflect the end of the game and mark the game as not running.""" + pass + def setup(bot: Bot) -> None: """Load the DuckGamesDirector cog.""" |