From 02dcc5345fa6bb9c05f164aed83fa91490a7d7d6 Mon Sep 17 00:00:00 2001 From: Roman Gräf Date: Sat, 5 Dec 2020 12:40:03 +0100 Subject: winstreaks, minimum players --- README.md | 2 ++ minecrafttrivia/game.py | 40 +++++++++++++++++++++++------- minecrafttrivia/trivia_interface_cog.py | 44 ++++++++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a916cdc..5e3e735 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ Guess minecraft crafting recipes the fastest. [p]minecrafttrivia high - Show highscores [p]minecrafttrivia config - Set timeouts / round counts. (ADMIN) [p]minecrafttrivia lead - Show total score leaderboard +[p]minecrafttrivia winstreak - Show current winstreaks +[p]minecrafttrivia startnow - Skip the join timer and start a game now ``` ## Legal Notice diff --git a/minecrafttrivia/game.py b/minecrafttrivia/game.py index e2373e6..e43b5fa 100644 --- a/minecrafttrivia/game.py +++ b/minecrafttrivia/game.py @@ -26,6 +26,7 @@ class OngoingGame(ABC): channel: discord.TextChannel phase: GamePhase signup_message: discord.Message + signup_embed: discord.Embed def __init__(self, bot: Red, config: Config, channel: discord.TextChannel): self.participants = [] @@ -37,23 +38,33 @@ class OngoingGame(ABC): async def start_signup(self): self.phase = GamePhase.SIGNUP join_timeout = await self.config.join_timeout() - embed = discord.Embed( + self.signup_embed = discord.Embed( title="Signups opened for new game of Minecraft trivia", description=f"React to this message in order to join. You have {join_timeout} seconds to signup.", ) - embed.timestamp = datetime.now() - self.signup_message = await self.channel.send(embed=embed) + self.signup_embed.timestamp = datetime.now() + self.signup_message = await self.channel.send(embed=self.signup_embed) await self.signup_message.add_reaction(constants.POSITIVE_REACTION) await asyncio.sleep(join_timeout) - embed.description = "Signups are now closed. Wait for the game to finish to start a new one." - await self.signup_message.edit(embed=embed) - self.participants = await utils.get_participants((await self.channel.fetch_message(self.signup_message.id)).reactions) - await self.start_game() + if self.phase == GamePhase.SIGNUP: + await self.start_game() async def start_game(self): self.phase = GamePhase.RUNNING + self.signup_embed.description = "Signups are now closed. Wait for the game to finish to start a new one." + self.participants = await utils.get_participants((await self.channel.fetch_message(self.signup_message.id)).reactions) + if len(self.participants) < await self.config.min_players(): + self.signup_embed.description = "Too few players to start game." + await self.signup_message.edit(embed=self.signup_embed) + self.phase = GamePhase.FINISHED + return + await self.signup_message.edit(embed=self.signup_embed) + await self.starting_game() await self.gameloop() + async def starting_game(self): + pass + async def conclude_game(self): self.phase = GamePhase.FINISHED embed = discord.Embed( @@ -128,16 +139,27 @@ class PointBasedGame(OngoingGame, ABC): high_scores[uid] = points else: high_scores[uid] = max(high_scores[uid], points) + async with self.config.current_winstreak() as current_winstreak: + sup = list(reversed(sorted(self.points.items(), key=lambda x: x[1]))) + winner_id = str(sup[0][0].id) + if winner_id in current_winstreak: + current_winstreak[winner_id] += 1 + else: + current_winstreak[winner_id] = 1 + for user, points in sup[1:]: + uid = str(user.id) + current_winstreak[uid] = 0 return await super().conclude_game() @property def ranks(self) -> typing.List[typing.Tuple[int, typing.Tuple[discord.User, int]]]: return utils.create_leaderboard(self.points) - async def start_game(self): + async def starting_game(self): for u in self.participants: self.points[u] = 0 - return await super().start_game() + + await super().starting_game() def leaderboard(self) -> str: return utils.format_leaderboard(self.ranks) diff --git a/minecrafttrivia/trivia_interface_cog.py b/minecrafttrivia/trivia_interface_cog.py index 405a64a..e65869a 100644 --- a/minecrafttrivia/trivia_interface_cog.py +++ b/minecrafttrivia/trivia_interface_cog.py @@ -9,7 +9,8 @@ from . import utils from .game import OngoingGame, CraftingGame, GamePhase -class TriviaInterfaceCog(commands.Cog): +class TriviaInterfaceCog(commands.Cog, name="MinecraftTrivia"): + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.config = Config.get_conf(self, identifier=262200644) @@ -18,8 +19,10 @@ class TriviaInterfaceCog(commands.Cog): join_timeout=30, guess_timeout=60, round_count=10, + min_players=2, total_scores={}, high_scores={}, + current_winstreak={}, ) self.active_games_per_channel: typing.Dict[int, OngoingGame] = {} @@ -35,19 +38,31 @@ class TriviaInterfaceCog(commands.Cog): @commands.group(aliases=["mctrivia", "mct"], invoke_without_command=True) @guild_only() - async def minecrafttrivia(self, ctx: commands.GuildContext): + async def minecrafttrivia(self, ctx: commands.GuildContext, *, extra=""): """Starts a game of minecraft trivia""" + if extra: + await ctx.send("Invalid subcommand.") game = self.get_game(ctx.channel) if game: return await ctx.send("Game already started.") game = self.create_game(ctx.bot, ctx.channel) await game.start_signup() + @minecrafttrivia.command(aliases=["now"]) + @guild_only() + @checks.mod() + async def startnow(self, ctx: commands.GuildContext): + game = self.get_game(ctx.channel) + if not game: + return await ctx.send("No game running") + await game.start_game() + @minecrafttrivia.group(aliases=["config"], invoke_without_command=True) @guild_only() @checks.admin() async def set(self, ctx: commands.GuildContext): - await ctx.send("Available config options: join_timeout guess_timeout round_count") + """Sets config options. execute without arguments to see all options""" + await ctx.send("Available config options: join_timeout guess_timeout round_count min_players") @set.command(aliases=["join"]) @guild_only() @@ -60,6 +75,17 @@ class TriviaInterfaceCog(commands.Cog): else: await ctx.send(f"`join_timeout` is `{await c.join_timeout()}`") + @set.command(aliases=["players"]) + @guild_only() + @checks.admin() + async def min_players(self, ctx: commands.GuildContext, to: int = None): + c = self.config.guild(ctx.guild) + if to: + await c.min_players.set(to) + await ctx.send(f"Set `min_players` to `{to}`") + else: + await ctx.send(f"`min_players` is `{await c.min_players()}`") + @set.command(aliases=["guess"]) @guild_only() @checks.admin() @@ -85,6 +111,7 @@ class TriviaInterfaceCog(commands.Cog): @minecrafttrivia.command(aliases=["high"]) @guild_only() async def highscore(self, ctx: commands.GuildContext): + """Show single-round highscore leaderboard""" high_scores = await self.config.guild(ctx.guild).high_scores() await ctx.send(embed=discord.Embed( title=f"MC Trivia Highscores for {ctx.guild.name}", @@ -93,14 +120,25 @@ class TriviaInterfaceCog(commands.Cog): @minecrafttrivia.command(aliases=["lead", "total"]) @guild_only() async def leaderboard(self, ctx: commands.GuildContext): + """Show total summed up highscore leaderboard""" total_scores = await self.config.guild(ctx.guild).total_scores() await ctx.send(embed=discord.Embed( title=f"MC Trivia Leaderboard for {ctx.guild.name}", description=utils.format_leaderboard(utils.create_leaderboard(total_scores)) )) + @minecrafttrivia.command(aliases=["win"]) + @guild_only() + async def winstreak(self, ctx: commands.GuildContext): + winstreaks = await self.config.guild(ctx.guild).current_winstreak() + await ctx.send(embed=discord.Embed( + title=f"MC Trivia Current Winstreaks in {ctx.guild.name}", + description=utils.format_leaderboard(utils.create_leaderboard(winstreaks)) + )) + @minecrafttrivia.command() async def info(self, ctx: commands.Context): + """Show info about this cog""" embed = discord.Embed( title="MC Trivia Cog by romangraef89", description="This cog allows you to compete against your friends in a race to guess minecraft crafting recipes the fastest" -- cgit