From 019c2cea32ae4fcc8a257e7ac57ec223cd6ea788 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 11:02:57 +0300 Subject: (TicTacToe): Created initial empty cog with loading. --- bot/exts/evergreen/tic_tac_toe.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 bot/exts/evergreen/tic_tac_toe.py diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py new file mode 100644 index 00000000..d4c95728 --- /dev/null +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -0,0 +1,15 @@ +from discord.ext.commands import Cog + +from bot.bot import SeasonalBot + + +class TicTacToe(Cog): + """TicTacToe cog contains tic-tac-toe game commands.""" + + def __init__(self, bot: SeasonalBot): + self.bot = bot + + +def setup(bot: SeasonalBot) -> None: + """Load TicTacToe Cog.""" + bot.add_cog(TicTacToe(bot)) -- cgit v1.2.3 From b6dc1207fd63564bee9814ca46a955c1067fe5db Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 11:06:48 +0300 Subject: (TicTacToe): Created `Player` class --- bot/exts/evergreen/tic_tac_toe.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index d4c95728..1927e021 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -1,8 +1,16 @@ +import discord from discord.ext.commands import Cog from bot.bot import SeasonalBot +class Player: + """Class that contains information about player and functions that interact with player.""" + + def __init__(self, user: discord.User): + self.user = user + + class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" -- cgit v1.2.3 From 7d5103c157fa36ba7cc75fa67a0ee062633b4148 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 11:08:57 +0300 Subject: (TicTacToe): Created `Game` class --- bot/exts/evergreen/tic_tac_toe.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 1927e021..98d793ba 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -1,5 +1,7 @@ +import typing as t + import discord -from discord.ext.commands import Cog +from discord.ext.commands import Cog, Context from bot.bot import SeasonalBot @@ -11,6 +13,15 @@ class Player: self.user = user +class Game: + """Class that contains information and functions about Tic Tac Toe game.""" + + def __init__(self, channel: discord.TextChannel, players: t.List[discord.User], ctx: Context): + self.channel = channel + self.players = players + self.ctx = ctx + + class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" -- cgit v1.2.3 From 103ddfe12af937c50ece9bd651b5d4da8cb29079 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 11:48:34 +0300 Subject: (TicTacToe): Added `ctx` variable to `Player` class. --- bot/exts/evergreen/tic_tac_toe.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 98d793ba..376cb3e9 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -9,8 +9,9 @@ from bot.bot import SeasonalBot class Player: """Class that contains information about player and functions that interact with player.""" - def __init__(self, user: discord.User): + def __init__(self, user: discord.User, ctx: Context): self.user = user + self.ctx = ctx class Game: -- cgit v1.2.3 From 3362619c919b2094994439efa0dc0564d82594f0 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 11:49:33 +0300 Subject: (TicTacToe): Replaced `discord.User` with `Player` in `Game` class signature. --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 376cb3e9..2e21b43a 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -17,7 +17,7 @@ class Player: class Game: """Class that contains information and functions about Tic Tac Toe game.""" - def __init__(self, channel: discord.TextChannel, players: t.List[discord.User], ctx: Context): + def __init__(self, channel: discord.TextChannel, players: t.List[Player], ctx: Context): self.channel = channel self.players = players self.ctx = ctx -- cgit v1.2.3 From 04fe88be9b105522aec5185ab771a73c9ea1a818 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 11:51:19 +0300 Subject: (TicTacToe): Added new player-about variables to `Game` class. --- bot/exts/evergreen/tic_tac_toe.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 2e21b43a..0fa1902b 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -22,6 +22,12 @@ class Game: self.players = players self.ctx = ctx + self.current = self.players[0] + self.next = self.players[1] + + self.winner: t.Optional[Player] = None + self.loser: t.Optional[Player] = None + class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" -- cgit v1.2.3 From 83d9173cdcd8671dedf5400dde81caac21e37634 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 11:57:13 +0300 Subject: (Constants, TicTacToe): Added number emojis that will be shown in board and in reactions. --- bot/constants.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index ca9bb94a..0135124b 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -118,6 +118,19 @@ class Emojis: pull_request_closed = "<:PRClosed:629695470519713818>" merge = "<:PRMerged:629695470570176522>" + # TicTacToe Emojis + number_emojis = { + 1: "\u0031", + 2: "\u0032", + 3: "\u0033", + 4: "\u0034", + 5: "\u0035", + 6: "\u0036", + 7: "\u0037", + 8: "\u0038", + 9: "\u0039" + } + class Hacktoberfest(NamedTuple): voice_id = 514420006474219521 -- cgit v1.2.3 From 1abec1772f140f886b84fb43c394527553cb271a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 11:59:12 +0300 Subject: (Constants, TicTacToe): Added confirmation and declining emojis. --- bot/constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index 0135124b..a23c6bcc 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -130,6 +130,8 @@ class Emojis: 8: "\u0038", 9: "\u0039" } + confirmation = "\u2705" + decline = "\u274c" class Hacktoberfest(NamedTuple): -- cgit v1.2.3 From fb9523b9c5e93ff30cdc58ac451e807da323c8b4 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 12:01:26 +0300 Subject: (Constants, TicTacToe): Added X and O emojis. --- bot/constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/constants.py b/bot/constants.py index a23c6bcc..d43cf4d3 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -132,6 +132,8 @@ class Emojis: } confirmation = "\u2705" decline = "\u274c" + x = "\U0001f1fd" + o = "\U0001f1f4" class Hacktoberfest(NamedTuple): -- cgit v1.2.3 From 3d92f7e907fcee767c1428a52bb503314b8f6b19 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 14:14:14 +0300 Subject: (TicTacToe): Added `get_confirmation` function to `Game` class to make sure that opponent want to play. --- bot/exts/evergreen/tic_tac_toe.py | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 0fa1902b..7d82e084 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -1,9 +1,16 @@ +import asyncio import typing as t import discord from discord.ext.commands import Cog, Context from bot.bot import SeasonalBot +from bot.constants import Emojis + +CONFIRMATION_MESSAGE = ( + "{opponent}, {requester} want to play Tic-Tac-Toe against you. React to this message with " + f"{Emojis.confirmation} to accept or with {Emojis.decline} to decline." +) class Player: @@ -28,6 +35,40 @@ class Game: self.winner: t.Optional[Player] = None self.loser: t.Optional[Player] = None + async def get_confirmation(self) -> t.Tuple[bool, t.Optional[str]]: + """Ask does user want to play TicTacToe against requester. First player is always requester.""" + confirm_message = await self.ctx.send( + CONFIRMATION_MESSAGE.format( + opponent=self.players[1].user.mention, + requester=self.players[0].user.mention + ) + ) + await confirm_message.add_reaction(Emojis.confirmation) + await confirm_message.add_reaction(Emojis.decline) + + def confirm_check(reaction: discord.Reaction, user: discord.User) -> bool: + return ( + reaction.emoji in (Emojis.confirmation, Emojis.decline) + and reaction.message.id == confirm_message.id + and user == self.players[1].user + ) + + try: + reaction, user = await self.ctx.bot.wait_for( + "reaction_add", + timeout=60.0, + check=confirm_check + ) + except asyncio.TimeoutError: + await confirm_message.delete() + return False, "Running out of time... Cancelled game." + + await confirm_message.delete() + if reaction.emoji == Emojis.confirmation: + return True, None + else: + return False, "User declined" + class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" -- cgit v1.2.3 From cab121e93829258251154c0f00b1b9d8f5dca2e4 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 14:17:35 +0300 Subject: (TicTacToe): Created helper function `add_reactions` to `Game` class to add all number reactions to message. --- bot/exts/evergreen/tic_tac_toe.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 7d82e084..24021842 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -69,6 +69,11 @@ class Game: else: return False, "User declined" + async def add_reactions(self, msg: discord.Message) -> None: + """Add number emojis to message.""" + for nr in Emojis.number_emojis: + await msg.add_reaction(nr) + class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" -- cgit v1.2.3 From fa5ba85d3c9da611c10412cc2ee75eeff3a434f9 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 14:18:33 +0300 Subject: (TicTacToe): Added new variable `games` to `TicTacToe` cog. --- bot/exts/evergreen/tic_tac_toe.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 24021842..28f48e11 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -80,6 +80,7 @@ class TicTacToe(Cog): def __init__(self, bot: SeasonalBot): self.bot = bot + self.games: t.List[Game] = [] def setup(bot: SeasonalBot) -> None: -- cgit v1.2.3 From 88762008d4fc36744f1dc5507a90015a37a0a9eb Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 14:39:01 +0300 Subject: (TicTacToe): Created check `is_channel_free`. --- bot/exts/evergreen/tic_tac_toe.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 28f48e11..301cb9ff 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -2,7 +2,7 @@ import asyncio import typing as t import discord -from discord.ext.commands import Cog, Context +from discord.ext.commands import Cog, Context, check from bot.bot import SeasonalBot from bot.constants import Emojis @@ -82,6 +82,13 @@ class TicTacToe(Cog): self.bot = bot self.games: t.List[Game] = [] + @staticmethod + def is_channel_free() -> t.Callable: + """Check is channel where command will be invoked free.""" + async def predicate(ctx: Context) -> bool: + return all(game.channel != ctx.channel for game in ctx.cog.games) + return check(predicate) + def setup(bot: SeasonalBot) -> None: """Load TicTacToe Cog.""" -- cgit v1.2.3 From 88a121db968de07880037ffcc05462ef65cfaa2e Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 17:02:16 +0300 Subject: (TicTacToe): Created check `is_requester_free`. --- bot/exts/evergreen/tic_tac_toe.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 301cb9ff..90f916ef 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -89,6 +89,13 @@ class TicTacToe(Cog): return all(game.channel != ctx.channel for game in ctx.cog.games) return check(predicate) + @staticmethod + def is_requester_free() -> t.Callable: + """Check is requester not already in any game.""" + async def predicate(ctx: Context) -> bool: + return all(ctx.author not in (player.user for player in game.players) for game in ctx.cog.games) + return check(predicate) + def setup(bot: SeasonalBot) -> None: """Load TicTacToe Cog.""" -- cgit v1.2.3 From 4a97fcd91d610da69d9dd4b75ca2bf80a094ae61 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 17:05:38 +0300 Subject: (TicTacToe): Created new class variable `over` to `Game`, added over check to cog checks. --- bot/exts/evergreen/tic_tac_toe.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 90f916ef..2795f94a 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -34,6 +34,7 @@ class Game: self.winner: t.Optional[Player] = None self.loser: t.Optional[Player] = None + self.over = False async def get_confirmation(self) -> t.Tuple[bool, t.Optional[str]]: """Ask does user want to play TicTacToe against requester. First player is always requester.""" @@ -60,6 +61,7 @@ class Game: check=confirm_check ) except asyncio.TimeoutError: + self.over = True await confirm_message.delete() return False, "Running out of time... Cancelled game." @@ -67,6 +69,7 @@ class Game: if reaction.emoji == Emojis.confirmation: return True, None else: + self.over = True return False, "User declined" async def add_reactions(self, msg: discord.Message) -> None: @@ -86,14 +89,16 @@ class TicTacToe(Cog): def is_channel_free() -> t.Callable: """Check is channel where command will be invoked free.""" async def predicate(ctx: Context) -> bool: - return all(game.channel != ctx.channel for game in ctx.cog.games) + return all(game.channel != ctx.channel for game in ctx.cog.games if not game.over) return check(predicate) @staticmethod def is_requester_free() -> t.Callable: """Check is requester not already in any game.""" async def predicate(ctx: Context) -> bool: - return all(ctx.author not in (player.user for player in game.players) for game in ctx.cog.games) + return all( + ctx.author not in (player.user for player in game.players) for game in ctx.cog.games if not game.over + ) return check(predicate) -- cgit v1.2.3 From 85750308db80a56b539004d935c3032e96c4947f Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 17:16:08 +0300 Subject: (TicTacToe): Moved checks to outside of class, created initial tic tac toe command. --- bot/exts/evergreen/tic_tac_toe.py | 46 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 2795f94a..1a906535 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -2,7 +2,7 @@ import asyncio import typing as t import discord -from discord.ext.commands import Cog, Context, check +from discord.ext.commands import Cog, Context, check, command, guild_only from bot.bot import SeasonalBot from bot.constants import Emojis @@ -78,6 +78,22 @@ class Game: await msg.add_reaction(nr) +def is_channel_free() -> t.Callable: + """Check is channel where command will be invoked free.""" + async def predicate(ctx: Context) -> bool: + return all(game.channel != ctx.channel for game in ctx.cog.games if not game.over) + return check(predicate) + + +def is_requester_free() -> t.Callable: + """Check is requester not already in any game.""" + async def predicate(ctx: Context) -> bool: + return all( + ctx.author not in (player.user for player in game.players) for game in ctx.cog.games if not game.over + ) + return check(predicate) + + class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" @@ -85,21 +101,19 @@ class TicTacToe(Cog): self.bot = bot self.games: t.List[Game] = [] - @staticmethod - def is_channel_free() -> t.Callable: - """Check is channel where command will be invoked free.""" - async def predicate(ctx: Context) -> bool: - return all(game.channel != ctx.channel for game in ctx.cog.games if not game.over) - return check(predicate) - - @staticmethod - def is_requester_free() -> t.Callable: - """Check is requester not already in any game.""" - async def predicate(ctx: Context) -> bool: - return all( - ctx.author not in (player.user for player in game.players) for game in ctx.cog.games if not game.over - ) - return check(predicate) + @guild_only() + @is_channel_free() + @is_requester_free() + @command(name="tictactoe", aliases=("ttt",)) + async def tic_tac_toe(self, ctx: Context, opponent: discord.User) -> None: + """Tic Tac Toe game. Play agains friends. Use reactions to add your mark to field.""" + game = Game( + ctx.channel, + [Player(ctx.author, ctx), Player(opponent, ctx)], + ctx + ) + self.games.append(game) + await game.get_confirmation() def setup(bot: SeasonalBot) -> None: -- cgit v1.2.3 From 7a7385bf222c24f78356ea46331ae5d4abddb65a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 19:33:05 +0300 Subject: (Constants, TicTacToe): Fixed number emojis contants, created helper function `send_board` to `Game` class. --- bot/constants.py | 18 +++++++++--------- bot/exts/evergreen/tic_tac_toe.py | 23 +++++++++++++++++++++-- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index d43cf4d3..a8dd03e6 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -120,15 +120,15 @@ class Emojis: # TicTacToe Emojis number_emojis = { - 1: "\u0031", - 2: "\u0032", - 3: "\u0033", - 4: "\u0034", - 5: "\u0035", - 6: "\u0036", - 7: "\u0037", - 8: "\u0038", - 9: "\u0039" + 1: "\u0031\ufe0f\u20e3", + 2: "\u0032\ufe0f\u20e3", + 3: "\u0033\ufe0f\u20e3", + 4: "\u0034\ufe0f\u20e3", + 5: "\u0035\ufe0f\u20e3", + 6: "\u0036\ufe0f\u20e3", + 7: "\u0037\ufe0f\u20e3", + 8: "\u0038\ufe0f\u20e3", + 9: "\u0039\ufe0f\u20e3" } confirmation = "\u2705" decline = "\u274c" diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 1a906535..55bbb7be 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -28,6 +28,11 @@ class Game: self.channel = channel self.players = players self.ctx = ctx + self.board = [ + [Emojis.number_emojis[1], Emojis.number_emojis[2], Emojis.number_emojis[3]], + [Emojis.number_emojis[4], Emojis.number_emojis[5], Emojis.number_emojis[6]], + [Emojis.number_emojis[7], Emojis.number_emojis[8], Emojis.number_emojis[9]] + ] self.current = self.players[0] self.next = self.players[1] @@ -74,9 +79,18 @@ class Game: async def add_reactions(self, msg: discord.Message) -> None: """Add number emojis to message.""" - for nr in Emojis.number_emojis: + for nr in Emojis.number_emojis.values(): await msg.add_reaction(nr) + async def send_board(self) -> discord.Message: + """Send board and return it's message.""" + msg = "" + for line in self.board: + msg += " ".join(line) + msg += "\n" + message = await self.ctx.send(msg) + return message + def is_channel_free() -> t.Callable: """Check is channel where command will be invoked free.""" @@ -113,7 +127,12 @@ class TicTacToe(Cog): ctx ) self.games.append(game) - await game.get_confirmation() + confirmed, msg = await game.get_confirmation() + + if not confirmed: + if msg: + await ctx.send(msg) + return def setup(bot: SeasonalBot) -> None: -- cgit v1.2.3 From 0af8726612f53ce6e52fbd636e0709c06d78415d Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 19:36:32 +0300 Subject: (TicTacToe): Added check is opponent free when request playing. --- bot/exts/evergreen/tic_tac_toe.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 55bbb7be..01417f25 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -121,6 +121,11 @@ class TicTacToe(Cog): @command(name="tictactoe", aliases=("ttt",)) async def tic_tac_toe(self, ctx: Context, opponent: discord.User) -> None: """Tic Tac Toe game. Play agains friends. Use reactions to add your mark to field.""" + if not all( + opponent not in (player.user for player in g.players) for g in ctx.cog.games if not g.over + ): + await ctx.send("Opponent is already in game.") + return game = Game( ctx.channel, [Player(ctx.author, ctx), Player(opponent, ctx)], -- cgit v1.2.3 From 89e963b08a6348213d0585f17de4e18441426eb4 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 19:37:53 +0300 Subject: (TicTacToe): Added `symbol` to `Player` class. --- bot/exts/evergreen/tic_tac_toe.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 01417f25..bb8f8dac 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -16,9 +16,10 @@ CONFIRMATION_MESSAGE = ( class Player: """Class that contains information about player and functions that interact with player.""" - def __init__(self, user: discord.User, ctx: Context): + def __init__(self, user: discord.User, ctx: Context, symbol: str): self.user = user self.ctx = ctx + self.symbol = symbol class Game: -- cgit v1.2.3 From 65c496169259c79f537d8aa38d21eb32feb817c2 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 7 Apr 2020 19:38:48 +0300 Subject: (TicTacToe): Added `symbol` to player class creation in `tictactoe` command. --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index bb8f8dac..1873216d 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -129,7 +129,7 @@ class TicTacToe(Cog): return game = Game( ctx.channel, - [Player(ctx.author, ctx), Player(opponent, ctx)], + [Player(ctx.author, ctx, Emojis.x), Player(opponent, ctx, Emojis.o)], ctx ) self.games.append(game) -- cgit v1.2.3 From 7c930b7bc003fecd42aef58384d33708aa1d4bfa Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 13:28:11 +0300 Subject: (TicTacToe): Removed unnecessary variable creation in `Game.send_board`. --- bot/exts/evergreen/tic_tac_toe.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 1873216d..ea503e83 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -89,8 +89,7 @@ class Game: for line in self.board: msg += " ".join(line) msg += "\n" - message = await self.ctx.send(msg) - return message + return await self.ctx.send(msg) def is_channel_free() -> t.Callable: -- cgit v1.2.3 From b4fa0b421b7949f0aea37e1eede68c0ba2432918 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 13:36:01 +0300 Subject: (TicTacToe): Created function `Game.play`. --- bot/exts/evergreen/tic_tac_toe.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index ea503e83..260c8c2f 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -91,6 +91,12 @@ class Game: msg += "\n" return await self.ctx.send(msg) + async def play(self) -> None: + """Start and handle game.""" + await self.ctx.send("It's time for game! Let's begin.") + board = await self.send_board() + await self.add_reactions(board) + def is_channel_free() -> t.Callable: """Check is channel where command will be invoked free.""" -- cgit v1.2.3 From ed7bbbcb635dcc6bf5e4028cc6fd1bc4abfe97a2 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 17:14:23 +0300 Subject: (TicTacToe): Redesigned board system, applied it's changes + created new function to `Player` class: `get_move`. --- bot/exts/evergreen/tic_tac_toe.py | 47 ++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 260c8c2f..1df4571b 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -21,6 +21,27 @@ class Player: self.ctx = ctx self.symbol = symbol + async def get_move(self, board: t.Dict[int, str], msg: discord.Message) -> t.Tuple[bool, t.Optional[int]]: + """ + Get move from user. + + Return is timeout reached and position of field what user will fill when timeout don't reach. + """ + def check_for_move(r: discord.Reaction, u: discord.User) -> bool: + return ( + u.id == self.user.id + and msg.id == r.message.id + and r.emoji in board.values() + and r.emoji in Emojis.number_emojis.values() + ) + + try: + react, = await self.ctx.bot.wait_for('reaction_add', timeout=120.0, check=check_for_move) + except asyncio.TimeoutError: + return True, None + else: + return False, Emojis.number_emojis.keys()[Emojis.number_emojis.values().index(react.emoji)] + class Game: """Class that contains information and functions about Tic Tac Toe game.""" @@ -29,11 +50,17 @@ class Game: self.channel = channel self.players = players self.ctx = ctx - self.board = [ - [Emojis.number_emojis[1], Emojis.number_emojis[2], Emojis.number_emojis[3]], - [Emojis.number_emojis[4], Emojis.number_emojis[5], Emojis.number_emojis[6]], - [Emojis.number_emojis[7], Emojis.number_emojis[8], Emojis.number_emojis[9]] - ] + self.board = { + 1: Emojis.number_emojis[1], + 2: Emojis.number_emojis[2], + 3: Emojis.number_emojis[3], + 4: Emojis.number_emojis[4], + 5: Emojis.number_emojis[5], + 6: Emojis.number_emojis[6], + 7: Emojis.number_emojis[7], + 8: Emojis.number_emojis[8], + 9: Emojis.number_emojis[9] + } self.current = self.players[0] self.next = self.players[1] @@ -86,9 +113,13 @@ class Game: async def send_board(self) -> discord.Message: """Send board and return it's message.""" msg = "" - for line in self.board: - msg += " ".join(line) - msg += "\n" + c = 0 + for line in self.board.values(): + msg += f"{line} " + c += 1 + if c == 3: + msg += "\n" + c = 0 return await self.ctx.send(msg) async def play(self) -> None: -- cgit v1.2.3 From c62669579d566e0c396bccf9798344d74b77aefa Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 18:55:14 +0300 Subject: (TicTacToe): Created `edit_board` function to `Game`, made fixes to `Player.get_move`, implemented `Game.play` functionality. --- bot/exts/evergreen/tic_tac_toe.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 1df4571b..b9e44220 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -36,11 +36,11 @@ class Player: ) try: - react, = await self.ctx.bot.wait_for('reaction_add', timeout=120.0, check=check_for_move) + react, _ = await self.ctx.bot.wait_for('reaction_add', timeout=120.0, check=check_for_move) except asyncio.TimeoutError: return True, None else: - return False, Emojis.number_emojis.keys()[Emojis.number_emojis.values().index(react.emoji)] + return False, list(Emojis.number_emojis.keys())[list(Emojis.number_emojis.values()).index(react.emoji)] class Game: @@ -122,12 +122,38 @@ class Game: c = 0 return await self.ctx.send(msg) + async def edit_board(self, message: discord.Message) -> None: + """Edit Tic Tac Toe game board in message.""" + msg = "" + c = 0 + for line in self.board.values(): + msg += f"{line} " + c += 1 + if c == 3: + msg += "\n" + c = 0 + await message.edit(content=msg) + async def play(self) -> None: """Start and handle game.""" await self.ctx.send("It's time for game! Let's begin.") board = await self.send_board() await self.add_reactions(board) + for _ in range(9): + announce = await self.ctx.send(f"{self.current.user.mention}, your turn! React to emoji to mark field.") + timeout, pos = await self.current.get_move(self.board, board) + await announce.delete() + if timeout: + await self.ctx.send(f"{self.current.user.mention} ran out of time. Canceling game.") + self.over = True + return + self.board[pos] = self.current.symbol + await self.edit_board(board) + await board.clear_reaction(Emojis.number_emojis[pos]) + self.current, self.next = self.next, self.current + self.over = True + def is_channel_free() -> t.Callable: """Check is channel where command will be invoked free.""" @@ -175,6 +201,7 @@ class TicTacToe(Cog): if msg: await ctx.send(msg) return + await game.play() def setup(bot: SeasonalBot) -> None: -- cgit v1.2.3 From 94a39f3f7f1b19b678572d2d494f07535e52a32f Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 19:05:33 +0300 Subject: (TicTacToe): Created helper function `Game.check_for_win`. --- bot/exts/evergreen/tic_tac_toe.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index b9e44220..970f359d 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -134,6 +134,24 @@ class Game: c = 0 await message.edit(content=msg) + async def check_for_win(self) -> bool: + """Check from board, is any player won game.""" + if ( + # Horizontal + self.board[1] == self.board[2] == self.board[3] + or self.board[4] == self.board[5] == self.board[6] + or self.board[7] == self.board[8] == self.board[9] + # Vertical + or self.board[1] == self.board[4] == self.board[7] + or self.board[2] == self.board[5] == self.board[8] + or self.board[3] == self.board[6] == self.board[9] + # Diagonal + or self.board[1] == self.board[5] == self.board[9] + or self.board[3] == self.board[5] == self.board[7] + ): + return True + return False + async def play(self) -> None: """Start and handle game.""" await self.ctx.send("It's time for game! Let's begin.") -- cgit v1.2.3 From 310ac58be883f07a9a410ce19c6b0dd8ffcf09bd Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 19:10:24 +0300 Subject: (TicTacToe): Added winner check to `Game.play`. --- bot/exts/evergreen/tic_tac_toe.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 970f359d..ed612182 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -169,7 +169,12 @@ class Game: self.board[pos] = self.current.symbol await self.edit_board(board) await board.clear_reaction(Emojis.number_emojis[pos]) + if await self.check_for_win(): + await self.ctx.send(f":tada: {self.current.user.mention} is won this game! :tada:") + await board.clear_reactions() + break self.current, self.next = self.next, self.current + self.over = True -- cgit v1.2.3 From 235e8d9b9b3360e9ebc2a49c2ba0c4006fe5ae9f Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 19:11:24 +0300 Subject: (TicTacToe): Removed unnecessary variable `channel` from `Game`. --- bot/exts/evergreen/tic_tac_toe.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index ed612182..e8aafa07 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -46,8 +46,7 @@ class Player: class Game: """Class that contains information and functions about Tic Tac Toe game.""" - def __init__(self, channel: discord.TextChannel, players: t.List[Player], ctx: Context): - self.channel = channel + def __init__(self, players: t.List[Player], ctx: Context): self.players = players self.ctx = ctx self.board = { @@ -213,7 +212,6 @@ class TicTacToe(Cog): await ctx.send("Opponent is already in game.") return game = Game( - ctx.channel, [Player(ctx.author, ctx, Emojis.x), Player(opponent, ctx, Emojis.o)], ctx ) -- cgit v1.2.3 From b15c637568f33b2600e822a5aff54a90ea02f91f Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 19:14:25 +0300 Subject: (TicTacToe): Added check that don't allow you to play against yourself to `tictactoe` command. --- bot/exts/evergreen/tic_tac_toe.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index e8aafa07..f1e0834b 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -206,6 +206,9 @@ class TicTacToe(Cog): @command(name="tictactoe", aliases=("ttt",)) async def tic_tac_toe(self, ctx: Context, opponent: discord.User) -> None: """Tic Tac Toe game. Play agains friends. Use reactions to add your mark to field.""" + if opponent == ctx.author: + await ctx.send("You can't play against yourself.") + return if not all( opponent not in (player.user for player in g.players) for g in ctx.cog.games if not g.over ): -- cgit v1.2.3 From a2963c4b2cf3f81e845b5e223a1b4de6feac0fda Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 19:15:55 +0300 Subject: (TicTacToe): Added loser and winner attaching to `Game.play` winning handling. --- bot/exts/evergreen/tic_tac_toe.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index f1e0834b..27bdbda1 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -169,6 +169,8 @@ class Game: await self.edit_board(board) await board.clear_reaction(Emojis.number_emojis[pos]) if await self.check_for_win(): + self.winner = self.current + self.loser = self.next await self.ctx.send(f":tada: {self.current.user.mention} is won this game! :tada:") await board.clear_reactions() break -- cgit v1.2.3 From b362d24b225e5d1d101e784d84227d563b226c64 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 19:27:37 +0300 Subject: (TicTacToe): Added new variable to `Game` class: `canceled`, applied it's changes and created new `tictactoe history` command. --- bot/exts/evergreen/tic_tac_toe.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 27bdbda1..c1f780cf 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -2,10 +2,11 @@ import asyncio import typing as t import discord -from discord.ext.commands import Cog, Context, check, command, guild_only +from discord.ext.commands import Cog, Context, check, group, guild_only from bot.bot import SeasonalBot from bot.constants import Emojis +from bot.utils.pagination import LinePaginator CONFIRMATION_MESSAGE = ( "{opponent}, {requester} want to play Tic-Tac-Toe against you. React to this message with " @@ -67,6 +68,7 @@ class Game: self.winner: t.Optional[Player] = None self.loser: t.Optional[Player] = None self.over = False + self.canceled = False async def get_confirmation(self) -> t.Tuple[bool, t.Optional[str]]: """Ask does user want to play TicTacToe against requester. First player is always requester.""" @@ -94,6 +96,7 @@ class Game: ) except asyncio.TimeoutError: self.over = True + self.canceled = True await confirm_message.delete() return False, "Running out of time... Cancelled game." @@ -102,6 +105,7 @@ class Game: return True, None else: self.over = True + self.canceled = True return False, "User declined" async def add_reactions(self, msg: discord.Message) -> None: @@ -164,6 +168,7 @@ class Game: if timeout: await self.ctx.send(f"{self.current.user.mention} ran out of time. Canceling game.") self.over = True + self.canceled = True return self.board[pos] = self.current.symbol await self.edit_board(board) @@ -205,7 +210,7 @@ class TicTacToe(Cog): @guild_only() @is_channel_free() @is_requester_free() - @command(name="tictactoe", aliases=("ttt",)) + @group(name="tictactoe", aliases=("ttt",), invoke_without_command=True) async def tic_tac_toe(self, ctx: Context, opponent: discord.User) -> None: """Tic Tac Toe game. Play agains friends. Use reactions to add your mark to field.""" if opponent == ctx.author: @@ -229,6 +234,23 @@ class TicTacToe(Cog): return await game.play() + @tic_tac_toe.group(name="history", aliases=("log",), invoke_without_command=True) + async def tic_tac_toe_logs(self, ctx: Context) -> None: + """Show most recent tic-tac-toe games.""" + if len(self.games) < 1: + await ctx.send("No recent games.") + return + await LinePaginator.paginate( + ( + f"**#{i+1}**: {game.winner.user.mention} :trophy: vs {game.loser.user.mention}" + for i, game in enumerate(self.games) + if game.over + and not game.canceled + ), + ctx, + discord.Embed(title="Most recent Tic Tac Toe games") + ) + def setup(bot: SeasonalBot) -> None: """Load TicTacToe Cog.""" -- cgit v1.2.3 From 2d22d7b791416adea1052175da386a196d19d1a8 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 19:28:44 +0300 Subject: (TicTacToe): Added way to send board to custom channel in `Game.send_board`. --- bot/exts/evergreen/tic_tac_toe.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index c1f780cf..87a845f1 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -113,7 +113,7 @@ class Game: for nr in Emojis.number_emojis.values(): await msg.add_reaction(nr) - async def send_board(self) -> discord.Message: + async def send_board(self, channel: t.Optional[discord.TextChannel] = None) -> discord.Message: """Send board and return it's message.""" msg = "" c = 0 @@ -123,6 +123,8 @@ class Game: if c == 3: msg += "\n" c = 0 + if channel: + return await channel.send(msg) return await self.ctx.send(msg) async def edit_board(self, message: discord.Message) -> None: -- cgit v1.2.3 From 14e5cadcbd8344cdba0b410567525f7518ac0a7a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 9 Apr 2020 19:39:07 +0300 Subject: (TicTacToe): Created command to show game information `tictactoe history show `. --- bot/exts/evergreen/tic_tac_toe.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 87a845f1..a9971ad2 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -253,6 +253,16 @@ class TicTacToe(Cog): discord.Embed(title="Most recent Tic Tac Toe games") ) + @tic_tac_toe_logs.command(name="show", aliases=("s",)) + async def show_tic_tac_toe_board(self, ctx: Context, game_id: int) -> None: + """View game board by ID (ID is possible to get by `.tictactoe history`).""" + if len(self.games) < game_id: + await ctx.send("Game don't exist.") + return + game = self.games[game_id - 1] + await ctx.send(f"{game.winner.user} :trophy: vs {game.loser.user}") + await game.send_board(ctx.channel) + def setup(bot: SeasonalBot) -> None: """Load TicTacToe Cog.""" -- cgit v1.2.3 From 5e13a3e2a7b6ed4b93e306784fae8443bf2cab33 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 12 Apr 2020 18:41:37 +0300 Subject: (TicTacToe): Created `AI` class for Tic Tac Toe against computer playing. --- bot/exts/evergreen/tic_tac_toe.py | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index a9971ad2..b306e803 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -1,4 +1,5 @@ import asyncio +import random import typing as t import discord @@ -44,6 +45,52 @@ class Player: return False, list(Emojis.number_emojis.keys())[list(Emojis.number_emojis.values()).index(react.emoji)] +class AI: + """Tic Tac Toe AI class for against computer gaming.""" + + def __init__(self, symbol: str): + self.symbol = symbol + + async def check_win(self, board: t.Dict[int, str]) -> bool: + """Check does this move will result game end.""" + if ( + # Horizontal + board[1] == board[2] == board[3] + or board[4] == board[5] == board[6] + or board[7] == board[8] == board[9] + # Vertical + or board[1] == board[4] == board[7] + or board[2] == board[5] == board[8] + or board[3] == board[6] == board[9] + # Diagonal + or board[1] == board[5] == board[9] + or board[3] == board[5] == board[7] + ): + return True + return False + + async def get_move(self, board: t.Dict[int, str], _: discord.Message) -> t.Tuple[bool, int]: + """Get move from AI. AI use Minimax strategy.""" + possible_moves = [i for i, emoji in board.items() if emoji in Emojis.number_emojis] + + for symbol in (Emojis.x, Emojis.o): + for move in possible_moves: + board_copy = board.copy() + board_copy[move] = symbol + if self.check_win(board_copy): + return False, move + + open_corners = [i for i in possible_moves if i in (1, 3, 7, 9)] + if len(open_corners) > 0: + return False, random.choice(open_corners) + + if 5 in possible_moves: + return False, 5 + + open_edges = [i for i in possible_moves if i in (2, 4, 6, 8)] + return False, random.choice(open_edges) + + class Game: """Class that contains information and functions about Tic Tac Toe game.""" -- cgit v1.2.3 From 85ed1469cefd408233fcdb84643089b4b6a36d88 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 12 Apr 2020 19:14:18 +0300 Subject: (TicTacToe): Implemented AI to game and cog. --- bot/exts/evergreen/tic_tac_toe.py | 82 ++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index b306e803..fc8edd70 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -71,13 +71,13 @@ class AI: async def get_move(self, board: t.Dict[int, str], _: discord.Message) -> t.Tuple[bool, int]: """Get move from AI. AI use Minimax strategy.""" - possible_moves = [i for i, emoji in board.items() if emoji in Emojis.number_emojis] + possible_moves = [i for i, emoji in board.items() if emoji in list(Emojis.number_emojis.values())] for symbol in (Emojis.x, Emojis.o): for move in possible_moves: board_copy = board.copy() board_copy[move] = symbol - if self.check_win(board_copy): + if await self.check_win(board_copy): return False, move open_corners = [i for i in possible_moves if i in (1, 3, 7, 9)] @@ -94,7 +94,7 @@ class AI: class Game: """Class that contains information and functions about Tic Tac Toe game.""" - def __init__(self, players: t.List[Player], ctx: Context): + def __init__(self, players: t.List[t.Union[Player, AI]], ctx: Context): self.players = players self.ctx = ctx self.board = { @@ -112,10 +112,11 @@ class Game: self.current = self.players[0] self.next = self.players[1] - self.winner: t.Optional[Player] = None - self.loser: t.Optional[Player] = None + self.winner: t.Optional[t.Union[Player, AI]] = None + self.loser: t.Optional[t.Union[Player, AI]] = None self.over = False self.canceled = False + self.draw = False async def get_confirmation(self) -> t.Tuple[bool, t.Optional[str]]: """Ask does user want to play TicTacToe against requester. First player is always requester.""" @@ -211,9 +212,11 @@ class Game: await self.add_reactions(board) for _ in range(9): - announce = await self.ctx.send(f"{self.current.user.mention}, your turn! React to emoji to mark field.") + if isinstance(self.current, Player): + announce = await self.ctx.send(f"{self.current.user.mention}, your turn! React to emoji to mark field.") timeout, pos = await self.current.get_move(self.board, board) - await announce.delete() + if isinstance(self.current, Player): + await announce.delete() if timeout: await self.ctx.send(f"{self.current.user.mention} ran out of time. Canceling game.") self.over = True @@ -225,11 +228,16 @@ class Game: if await self.check_for_win(): self.winner = self.current self.loser = self.next - await self.ctx.send(f":tada: {self.current.user.mention} is won this game! :tada:") + await self.ctx.send( + f":tada: {self.current.user.mention if isinstance(self.current, Player) else 'AI'} " + f"is won this game! :tada:" + ) await board.clear_reactions() break self.current, self.next = self.next, self.current - + if not self.winner: + self.draw = True + await self.ctx.send("It's DRAW!") self.over = True @@ -260,27 +268,34 @@ class TicTacToe(Cog): @is_channel_free() @is_requester_free() @group(name="tictactoe", aliases=("ttt",), invoke_without_command=True) - async def tic_tac_toe(self, ctx: Context, opponent: discord.User) -> None: - """Tic Tac Toe game. Play agains friends. Use reactions to add your mark to field.""" + async def tic_tac_toe(self, ctx: Context, opponent: t.Optional[discord.User]) -> None: + """Tic Tac Toe game. Play agains friends or AI. Use reactions to add your mark to field.""" if opponent == ctx.author: await ctx.send("You can't play against yourself.") return - if not all( + if opponent is not None and not all( opponent not in (player.user for player in g.players) for g in ctx.cog.games if not g.over ): await ctx.send("Opponent is already in game.") return - game = Game( - [Player(ctx.author, ctx, Emojis.x), Player(opponent, ctx, Emojis.o)], - ctx - ) + if opponent is None: + game = Game( + [Player(ctx.author, ctx, Emojis.x), AI(Emojis.o)], + ctx + ) + else: + game = Game( + [Player(ctx.author, ctx, Emojis.x), Player(opponent, ctx, Emojis.o)], + ctx + ) self.games.append(game) - confirmed, msg = await game.get_confirmation() + if opponent is not None: + confirmed, msg = await game.get_confirmation() - if not confirmed: - if msg: - await ctx.send(msg) - return + if not confirmed: + if msg: + await ctx.send(msg) + return await game.play() @tic_tac_toe.group(name="history", aliases=("log",), invoke_without_command=True) @@ -289,13 +304,21 @@ class TicTacToe(Cog): if len(self.games) < 1: await ctx.send("No recent games.") return + log_games = [] + for i, game in enumerate(self.games): + if game.over and not game.canceled: + if game.draw: + log_games.append( + f"**#{i+1}**: {game.players[0].user.mention} vs " + f"{game.players[1].user.mention if isinstance(game.players[1], Player) else 'AI'} (draw)" + ) + else: + log_games.append( + f"**#{i+1}**: {game.winner.user.mention if isinstance(game.winner, Player) else 'AI'} :trophy: " + f"vs {game.loser.user.mention if isinstance(game.loser, Player) else 'AI'}" + ) await LinePaginator.paginate( - ( - f"**#{i+1}**: {game.winner.user.mention} :trophy: vs {game.loser.user.mention}" - for i, game in enumerate(self.games) - if game.over - and not game.canceled - ), + log_games, ctx, discord.Embed(title="Most recent Tic Tac Toe games") ) @@ -307,7 +330,10 @@ class TicTacToe(Cog): await ctx.send("Game don't exist.") return game = self.games[game_id - 1] - await ctx.send(f"{game.winner.user} :trophy: vs {game.loser.user}") + await ctx.send( + f"{game.winner.user if isinstance(game.winner, Player) else 'AI'} " + f":trophy: vs {game.loser.user if isinstance(game.winner, Player) else 'AI'}" + ) await game.send_board(ctx.channel) -- cgit v1.2.3 From af6357c444cd55fb203cf9696f9b568b27ccd666 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 12 Apr 2020 19:17:15 +0300 Subject: (TicTacToe): Setting winning as priority in AI instead blocking opponent. --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index fc8edd70..72eb2090 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -73,7 +73,7 @@ class AI: """Get move from AI. AI use Minimax strategy.""" possible_moves = [i for i, emoji in board.items() if emoji in list(Emojis.number_emojis.values())] - for symbol in (Emojis.x, Emojis.o): + for symbol in (Emojis.o, Emojis.x): for move in possible_moves: board_copy = board.copy() board_copy[move] = symbol -- cgit v1.2.3 From ddec3a15ffa37b775bc483910baefb4e0aba2f88 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Mon, 13 Apr 2020 08:14:42 +0300 Subject: (TicTacToe): Created new helper functions `display` to `Player` and `AI` class to avoid `if` checks in strings. --- bot/exts/evergreen/tic_tac_toe.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 72eb2090..8d0c384b 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -44,6 +44,10 @@ class Player: else: return False, list(Emojis.number_emojis.keys())[list(Emojis.number_emojis.values()).index(react.emoji)] + async def display(self) -> str: + """Return mention of user.""" + return self.user.mention + class AI: """Tic Tac Toe AI class for against computer gaming.""" @@ -90,6 +94,10 @@ class AI: open_edges = [i for i in possible_moves if i in (2, 4, 6, 8)] return False, random.choice(open_edges) + def display(self) -> str: + """Return `AI` as user name.""" + return "AI" + class Game: """Class that contains information and functions about Tic Tac Toe game.""" -- cgit v1.2.3 From 494cb28681f2de44ff4f95b1b9fe4b1662239cb6 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Mon, 13 Apr 2020 08:19:10 +0300 Subject: (TicTacToe): Applied `Player.display()` and `AI.display()` to cog and `Game` class. --- bot/exts/evergreen/tic_tac_toe.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 8d0c384b..435c7af3 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -237,8 +237,7 @@ class Game: self.winner = self.current self.loser = self.next await self.ctx.send( - f":tada: {self.current.user.mention if isinstance(self.current, Player) else 'AI'} " - f"is won this game! :tada:" + f":tada: {self.current.display()} is won this game! :tada:" ) await board.clear_reactions() break @@ -317,13 +316,11 @@ class TicTacToe(Cog): if game.over and not game.canceled: if game.draw: log_games.append( - f"**#{i+1}**: {game.players[0].user.mention} vs " - f"{game.players[1].user.mention if isinstance(game.players[1], Player) else 'AI'} (draw)" + f"**#{i+1}**: {game.players[0].display()} vs {game.players[1].display()} (draw)" ) else: log_games.append( - f"**#{i+1}**: {game.winner.user.mention if isinstance(game.winner, Player) else 'AI'} :trophy: " - f"vs {game.loser.user.mention if isinstance(game.loser, Player) else 'AI'}" + f"**#{i+1}**: {game.winner.display()} :trophy: vs {game.loser.display()}" ) await LinePaginator.paginate( log_games, @@ -339,8 +336,7 @@ class TicTacToe(Cog): return game = self.games[game_id - 1] await ctx.send( - f"{game.winner.user if isinstance(game.winner, Player) else 'AI'} " - f":trophy: vs {game.loser.user if isinstance(game.winner, Player) else 'AI'}" + f"{game.winner.display()} :trophy: vs {game.loser.display()}" ) await game.send_board(ctx.channel) -- cgit v1.2.3 From d3690c4056826d963c4a25b8fc7f5704aa8e93e1 Mon Sep 17 00:00:00 2001 From: Hambira Date: Sat, 30 May 2020 22:36:45 +0530 Subject: "adding xkcd feature" --- bot/exts/evergreen/xkcd.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 bot/exts/evergreen/xkcd.py diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py new file mode 100644 index 00000000..11477216 --- /dev/null +++ b/bot/exts/evergreen/xkcd.py @@ -0,0 +1,72 @@ +import logging +import random + +import discord +from discord.ext import commands + +log = logging.getLogger(__name__) + + +class XKCD(commands.Cog): + """A cog for posting the XKCD .""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + @commands.command(name="xkcd") + async def fetch_xkcd_comics(self, ctx: commands.Context, comic: str = "latest") -> None: + """Read your Fav XKCD comics.""" + if comic not in ["random", "latest"]: + url = f"https://xkcd.com/{comic}/info.0.json" + else: + url = "https://xkcd.com/info.0.json" + + # ---- random choice ----- + if comic == "random": + async with self.bot.http_session.get(url) as r: + json_data = await r.json() + random_pick = random.randint(1, int(json_data["num"])) + url = f"https://xkcd.com/{random_pick}/info.0.json" + + log.trace(f"Querying xkcd API: {url}") + async with self.bot.http_session.get(url) as r: + if r.status == "200": + json_data = await r.json() + else: + # ----- Exception handling | Guides to use ------ + log.warning(f"Received response {r.status} from: {url}") + # -- get the latest comic number --- + url = f"https://xkcd.com/info.0.json" + async with self.bot.http_session.get(url) as r: + latest_data = await r.json() + + # --- beautify response --- + latest_num = latest_data["num"] + resp = discord.Embed( + title="Guides | Usage", + description=f''' + .xkcd latest (Retrieves the latest comic) + .xkcd random (Retrieves random comic) + .xkcd number (Enter a comic number between 1 & {latest_num}) + ''' + ) + return await ctx.send(embed=resp) + + # --- response variables ---- + day, month, year = json_data["day"], json_data["month"], json_data["year"] + comic_number = json_data["num"] + + # ---- beautify response ---- + embed = discord.Embed( + title=json_data['title'], + description=json_data["alt"] + ) + embed.set_image(url=json_data['img']) + embed.set_footer(text=f"Post date : {day}-{month}-{year} | xkcd comics - {comic_number}") + + await ctx.send(embed=embed) + + +def setup(bot: commands.Bot) -> None: + """XKCD Cog load.""" + bot.add_cog(XKCD(bot)) -- cgit v1.2.3 From 8df212d6ca6bb44668da57fa99bae78871f2e864 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 24 Sep 2020 17:33:06 +0300 Subject: Tictactoe: Use __str__ instead custom display method for user/AI name display --- bot/exts/evergreen/tic_tac_toe.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 435c7af3..74b04db8 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -44,7 +44,7 @@ class Player: else: return False, list(Emojis.number_emojis.keys())[list(Emojis.number_emojis.values()).index(react.emoji)] - async def display(self) -> str: + def __str__(self) -> str: """Return mention of user.""" return self.user.mention @@ -94,7 +94,7 @@ class AI: open_edges = [i for i in possible_moves if i in (2, 4, 6, 8)] return False, random.choice(open_edges) - def display(self) -> str: + def __str__(self) -> str: """Return `AI` as user name.""" return "AI" @@ -237,7 +237,7 @@ class Game: self.winner = self.current self.loser = self.next await self.ctx.send( - f":tada: {self.current.display()} is won this game! :tada:" + f":tada: {self.current} is won this game! :tada:" ) await board.clear_reactions() break @@ -316,11 +316,11 @@ class TicTacToe(Cog): if game.over and not game.canceled: if game.draw: log_games.append( - f"**#{i+1}**: {game.players[0].display()} vs {game.players[1].display()} (draw)" + f"**#{i+1}**: {game.players[0]} vs {game.players[1]} (draw)" ) else: log_games.append( - f"**#{i+1}**: {game.winner.display()} :trophy: vs {game.loser.display()}" + f"**#{i+1}**: {game.winner} :trophy: vs {game.loser}" ) await LinePaginator.paginate( log_games, @@ -336,7 +336,7 @@ class TicTacToe(Cog): return game = self.games[game_id - 1] await ctx.send( - f"{game.winner.display()} :trophy: vs {game.loser.display()}" + f"{game.winner} :trophy: vs {game.loser}" ) await game.send_board(ctx.channel) -- cgit v1.2.3 From 0a275c90bfd824b320dd36c9b8c5fd73a143ad72 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 24 Sep 2020 20:00:44 +0300 Subject: Tictactoe: Refactor board message content generation --- bot/exts/evergreen/tic_tac_toe.py | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 74b04db8..16871070 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -169,31 +169,12 @@ class Game: for nr in Emojis.number_emojis.values(): await msg.add_reaction(nr) - async def send_board(self, channel: t.Optional[discord.TextChannel] = None) -> discord.Message: - """Send board and return it's message.""" - msg = "" - c = 0 - for line in self.board.values(): - msg += f"{line} " - c += 1 - if c == 3: - msg += "\n" - c = 0 - if channel: - return await channel.send(msg) - return await self.ctx.send(msg) - - async def edit_board(self, message: discord.Message) -> None: - """Edit Tic Tac Toe game board in message.""" - msg = "" - c = 0 - for line in self.board.values(): - msg += f"{line} " - c += 1 - if c == 3: - msg += "\n" - c = 0 - await message.edit(content=msg) + def format_board(self) -> str: + """Get formatted tic-tac-toe board for message.""" + board = list(self.board.values()) + return "\n".join( + (f"{board[line]} {board[line + 1]} {board[line + 2]}" for line in range(0, len(board), 3)) + ) async def check_for_win(self) -> bool: """Check from board, is any player won game.""" @@ -216,7 +197,7 @@ class Game: async def play(self) -> None: """Start and handle game.""" await self.ctx.send("It's time for game! Let's begin.") - board = await self.send_board() + board = await self.ctx.send(self.format_board()) await self.add_reactions(board) for _ in range(9): @@ -231,7 +212,7 @@ class Game: self.canceled = True return self.board[pos] = self.current.symbol - await self.edit_board(board) + await board.edit(content=self.format_board()) await board.clear_reaction(Emojis.number_emojis[pos]) if await self.check_for_win(): self.winner = self.current @@ -338,7 +319,7 @@ class TicTacToe(Cog): await ctx.send( f"{game.winner} :trophy: vs {game.loser}" ) - await game.send_board(ctx.channel) + await ctx.send(game.format_board()) def setup(bot: SeasonalBot) -> None: -- cgit v1.2.3 From e6ca49b4745a3c568da6adf3e8c670661b15443d Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 24 Sep 2020 20:04:30 +0300 Subject: Tictactoe: Document `get_confirmation` return value --- bot/exts/evergreen/tic_tac_toe.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 16871070..0c13964a 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -127,7 +127,13 @@ class Game: self.draw = False async def get_confirmation(self) -> t.Tuple[bool, t.Optional[str]]: - """Ask does user want to play TicTacToe against requester. First player is always requester.""" + """ + Ask does user want to play TicTacToe against requester. First player is always requester. + + This return tuple that have: + - first element boolean (is game accepted?) + - (optional, only when first element is False, otherwise None) reason for declining. + """ confirm_message = await self.ctx.send( CONFIRMATION_MESSAGE.format( opponent=self.players[1].user.mention, -- cgit v1.2.3 From fdc4cbd258a66f37371e427c57271b8abf8378e5 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 24 Sep 2020 20:11:25 +0300 Subject: Tictactoe: Remove duplicate functions --- bot/exts/evergreen/tic_tac_toe.py | 62 +++++++++++++++------------------------ 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 0c13964a..26df4e10 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -15,6 +15,25 @@ CONFIRMATION_MESSAGE = ( ) +def check_win(board: t.Dict[int, str]) -> bool: + """Check from board, is any player won game.""" + if ( + # Horizontal + board[1] == board[2] == board[3] + or board[4] == board[5] == board[6] + or board[7] == board[8] == board[9] + # Vertical + or board[1] == board[4] == board[7] + or board[2] == board[5] == board[8] + or board[3] == board[6] == board[9] + # Diagonal + or board[1] == board[5] == board[9] + or board[3] == board[5] == board[7] + ): + return True + return False + + class Player: """Class that contains information about player and functions that interact with player.""" @@ -55,24 +74,6 @@ class AI: def __init__(self, symbol: str): self.symbol = symbol - async def check_win(self, board: t.Dict[int, str]) -> bool: - """Check does this move will result game end.""" - if ( - # Horizontal - board[1] == board[2] == board[3] - or board[4] == board[5] == board[6] - or board[7] == board[8] == board[9] - # Vertical - or board[1] == board[4] == board[7] - or board[2] == board[5] == board[8] - or board[3] == board[6] == board[9] - # Diagonal - or board[1] == board[5] == board[9] - or board[3] == board[5] == board[7] - ): - return True - return False - async def get_move(self, board: t.Dict[int, str], _: discord.Message) -> t.Tuple[bool, int]: """Get move from AI. AI use Minimax strategy.""" possible_moves = [i for i, emoji in board.items() if emoji in list(Emojis.number_emojis.values())] @@ -81,7 +82,7 @@ class AI: for move in possible_moves: board_copy = board.copy() board_copy[move] = symbol - if await self.check_win(board_copy): + if check_win(board_copy): return False, move open_corners = [i for i in possible_moves if i in (1, 3, 7, 9)] @@ -144,6 +145,9 @@ class Game: await confirm_message.add_reaction(Emojis.decline) def confirm_check(reaction: discord.Reaction, user: discord.User) -> bool: + """ + Check is user who reacted user from who this was requested, message is confirm message and emoji is valid. + """ return ( reaction.emoji in (Emojis.confirmation, Emojis.decline) and reaction.message.id == confirm_message.id @@ -182,24 +186,6 @@ class Game: (f"{board[line]} {board[line + 1]} {board[line + 2]}" for line in range(0, len(board), 3)) ) - async def check_for_win(self) -> bool: - """Check from board, is any player won game.""" - if ( - # Horizontal - self.board[1] == self.board[2] == self.board[3] - or self.board[4] == self.board[5] == self.board[6] - or self.board[7] == self.board[8] == self.board[9] - # Vertical - or self.board[1] == self.board[4] == self.board[7] - or self.board[2] == self.board[5] == self.board[8] - or self.board[3] == self.board[6] == self.board[9] - # Diagonal - or self.board[1] == self.board[5] == self.board[9] - or self.board[3] == self.board[5] == self.board[7] - ): - return True - return False - async def play(self) -> None: """Start and handle game.""" await self.ctx.send("It's time for game! Let's begin.") @@ -220,7 +206,7 @@ class Game: self.board[pos] = self.current.symbol await board.edit(content=self.format_board()) await board.clear_reaction(Emojis.number_emojis[pos]) - if await self.check_for_win(): + if check_win(self.board): self.winner = self.current self.loser = self.next await self.ctx.send( -- cgit v1.2.3 From 88c5270cebb56c1cf84ea0a2bfb78cca6bf2252a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 24 Sep 2020 20:14:09 +0300 Subject: Tictactoe: Document another check --- bot/exts/evergreen/tic_tac_toe.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 26df4e10..1bd20b70 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -49,6 +49,9 @@ class Player: Return is timeout reached and position of field what user will fill when timeout don't reach. """ def check_for_move(r: discord.Reaction, u: discord.User) -> bool: + """ + Check does user who reacted is user who we want, message is board message and emoji is in board values. + """ return ( u.id == self.user.id and msg.id == r.message.id -- cgit v1.2.3 From d7c215e723da334f3ef3927c173680307d7764ba Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Thu, 24 Sep 2020 20:17:47 +0300 Subject: Tictactoe: Fix docstrings formatting --- bot/exts/evergreen/tic_tac_toe.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 1bd20b70..20fbb9be 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -49,9 +49,7 @@ class Player: Return is timeout reached and position of field what user will fill when timeout don't reach. """ def check_for_move(r: discord.Reaction, u: discord.User) -> bool: - """ - Check does user who reacted is user who we want, message is board message and emoji is in board values. - """ + """Check does user who reacted is user who we want, message is board and emoji is in board values.""" return ( u.id == self.user.id and msg.id == r.message.id @@ -148,9 +146,7 @@ class Game: await confirm_message.add_reaction(Emojis.decline) def confirm_check(reaction: discord.Reaction, user: discord.User) -> bool: - """ - Check is user who reacted user from who this was requested, message is confirm message and emoji is valid. - """ + """Check is user who reacted from who this was requested, message is confirmation and emoji is valid.""" return ( reaction.emoji in (Emojis.confirmation, Emojis.decline) and reaction.message.id == confirm_message.id -- cgit v1.2.3 From fa7c5c68959b664c0b4d5d983cfaafc84d2759da Mon Sep 17 00:00:00 2001 From: xithrius Date: Fri, 8 Jan 2021 17:06:23 -0800 Subject: Corrected small spelling mistake. --- bot/exts/evergreen/tic_tac_toe.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 20fbb9be..b27f1942 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -5,7 +5,7 @@ import typing as t import discord from discord.ext.commands import Cog, Context, check, group, guild_only -from bot.bot import SeasonalBot +from bot.bot import Bot from bot.constants import Emojis from bot.utils.pagination import LinePaginator @@ -239,7 +239,7 @@ def is_requester_free() -> t.Callable: class TicTacToe(Cog): """TicTacToe cog contains tic-tac-toe game commands.""" - def __init__(self, bot: SeasonalBot): + def __init__(self, bot: Bot): self.bot = bot self.games: t.List[Game] = [] @@ -248,7 +248,7 @@ class TicTacToe(Cog): @is_requester_free() @group(name="tictactoe", aliases=("ttt",), invoke_without_command=True) async def tic_tac_toe(self, ctx: Context, opponent: t.Optional[discord.User]) -> None: - """Tic Tac Toe game. Play agains friends or AI. Use reactions to add your mark to field.""" + """Tic Tac Toe game. Play against friends or AI. Use reactions to add your mark to field.""" if opponent == ctx.author: await ctx.send("You can't play against yourself.") return @@ -313,6 +313,6 @@ class TicTacToe(Cog): await ctx.send(game.format_board()) -def setup(bot: SeasonalBot) -> None: +def setup(bot: Bot) -> None: """Load TicTacToe Cog.""" bot.add_cog(TicTacToe(bot)) -- cgit v1.2.3 From 48103a601f48ac620adf8f97de7ab9f7ab942998 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 9 Jan 2021 08:21:44 +0200 Subject: Simplify check_win function returning --- bot/exts/evergreen/tic_tac_toe.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index b27f1942..d5c2a558 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -17,21 +17,21 @@ CONFIRMATION_MESSAGE = ( def check_win(board: t.Dict[int, str]) -> bool: """Check from board, is any player won game.""" - if ( + return any( + ( # Horizontal - board[1] == board[2] == board[3] - or board[4] == board[5] == board[6] - or board[7] == board[8] == board[9] + board[1] == board[2] == board[3], + board[4] == board[5] == board[6], + board[7] == board[8] == board[9], # Vertical - or board[1] == board[4] == board[7] - or board[2] == board[5] == board[8] - or board[3] == board[6] == board[9] + board[1] == board[4] == board[7], + board[2] == board[5] == board[8], + board[3] == board[6] == board[9], # Diagonal - or board[1] == board[5] == board[9] - or board[3] == board[5] == board[7] - ): - return True - return False + board[1] == board[5] == board[9], + board[3] == board[5] == board[7], + ) + ) class Player: -- cgit v1.2.3 From 97ef76a6206a69d4ab58ca9de9c980afa4ca20c6 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 16 Jan 2021 20:12:51 +0200 Subject: Move Tic Tac Toe board to embed description --- bot/exts/evergreen/tic_tac_toe.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index d5c2a558..a206aee7 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -188,7 +188,9 @@ class Game: async def play(self) -> None: """Start and handle game.""" await self.ctx.send("It's time for game! Let's begin.") - board = await self.ctx.send(self.format_board()) + board = await self.ctx.send( + embed=discord.Embed(description=self.format_board()) + ) await self.add_reactions(board) for _ in range(9): @@ -203,7 +205,9 @@ class Game: self.canceled = True return self.board[pos] = self.current.symbol - await board.edit(content=self.format_board()) + await board.edit( + embed=discord.Embed(description=self.format_board()) + ) await board.clear_reaction(Emojis.number_emojis[pos]) if check_win(self.board): self.winner = self.current -- cgit v1.2.3 From 3fcddb2e7d4e855eef815df9e3552fce929ce00f Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 16 Jan 2021 20:27:58 +0200 Subject: Fix grammar Co-authored-by: ChrisJL --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index a206aee7..db84427c 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -10,7 +10,7 @@ from bot.constants import Emojis from bot.utils.pagination import LinePaginator CONFIRMATION_MESSAGE = ( - "{opponent}, {requester} want to play Tic-Tac-Toe against you. React to this message with " + "{opponent}, {requester} wants to play Tic-Tac-Toe against you. React to this message with " f"{Emojis.confirmation} to accept or with {Emojis.decline} to decline." ) -- cgit v1.2.3 From fb09472ddc9a52b4bcc78ce2f67f2ac768c8a8ec Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 16 Jan 2021 20:33:47 +0200 Subject: Add missing a article Co-authored-by: ChrisJL --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index db84427c..4b76f94d 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -220,7 +220,7 @@ class Game: self.current, self.next = self.next, self.current if not self.winner: self.draw = True - await self.ctx.send("It's DRAW!") + await self.ctx.send("It's a DRAW!") self.over = True -- cgit v1.2.3 From 6a3366a03de682f6d00aef9bfe33d7c8a10e6ba7 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 16 Jan 2021 20:35:28 +0200 Subject: Improve "your turn" message --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 4b76f94d..49571a34 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -195,7 +195,7 @@ class Game: for _ in range(9): if isinstance(self.current, Player): - announce = await self.ctx.send(f"{self.current.user.mention}, your turn! React to emoji to mark field.") + announce = await self.ctx.send(f"{self.current.user.mention}, it's your turn! React with an emoji to take your go.") timeout, pos = await self.current.get_move(self.board, board) if isinstance(self.current, Player): await announce.delete() -- cgit v1.2.3 From e2878ab625b7add00fe6aea3175107c9bd9e1cdb Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 16 Jan 2021 20:37:11 +0200 Subject: More grammar fixes --- bot/exts/evergreen/tic_tac_toe.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 49571a34..ed4a6d52 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -187,7 +187,7 @@ class Game: async def play(self) -> None: """Start and handle game.""" - await self.ctx.send("It's time for game! Let's begin.") + await self.ctx.send("It's time for the game! Let's begin.") board = await self.ctx.send( embed=discord.Embed(description=self.format_board()) ) @@ -213,7 +213,7 @@ class Game: self.winner = self.current self.loser = self.next await self.ctx.send( - f":tada: {self.current} is won this game! :tada:" + f":tada: {self.current} won this game! :tada:" ) await board.clear_reactions() break -- cgit v1.2.3 From b5490070a481d09a82e682be0b822baab4fc373e Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 16 Jan 2021 20:48:27 +0200 Subject: Fix too long line --- bot/exts/evergreen/tic_tac_toe.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index ed4a6d52..bcc4c97e 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -195,7 +195,10 @@ class Game: for _ in range(9): if isinstance(self.current, Player): - announce = await self.ctx.send(f"{self.current.user.mention}, it's your turn! React with an emoji to take your go.") + announce = await self.ctx.send( + f"{self.current.user.mention}, it's your turn! " + "React with an emoji to take your go." + ) timeout, pos = await self.current.get_move(self.board, board) if isinstance(self.current, Player): await announce.delete() -- cgit v1.2.3 From 674147c2858dfb6e10470d7b4c044f46615950e3 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 17 Jan 2021 08:32:00 +0200 Subject: Remove unnecessary line split Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/exts/evergreen/tic_tac_toe.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index bcc4c97e..daa646a8 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -314,9 +314,7 @@ class TicTacToe(Cog): await ctx.send("Game don't exist.") return game = self.games[game_id - 1] - await ctx.send( - f"{game.winner} :trophy: vs {game.loser}" - ) + await ctx.send(f"{game.winner} :trophy: vs {game.loser}") await ctx.send(game.format_board()) -- cgit v1.2.3 From 1d6912caf4bf3cb0604933ec66fa13294fa5d68a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 17 Jan 2021 08:38:20 +0200 Subject: Fix indention --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index daa646a8..22fff102 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -314,7 +314,7 @@ class TicTacToe(Cog): await ctx.send("Game don't exist.") return game = self.games[game_id - 1] - await ctx.send(f"{game.winner} :trophy: vs {game.loser}") + await ctx.send(f"{game.winner} :trophy: vs {game.loser}") await ctx.send(game.format_board()) -- cgit v1.2.3 From dd8664c3b4ff8037b9e28d9935a2f99fe9463a7b Mon Sep 17 00:00:00 2001 From: soul crusher 2005 Date: Wed, 20 Jan 2021 16:18:43 +0530 Subject: Update trivia_quiz.json new questions --- bot/resources/evergreen/trivia_quiz.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index 8f0a4114..a5e3a3e4 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -247,6 +247,18 @@ "question": "What is the capital of Iraq?", "answer": "Baghdad", "info": "Baghdad is the capital of Iraq. It has a population of 7 million people." + }, + { + "id": 136, + "question":"The United Nations headquarters is located at which city?", + "answer":"New York", + "info":"The United Nations is headquartered in New York City in a complex designed by a board of architects led by Wallace Harrison and built by the architectural firm Harrison & Abramovitz. The complex has served as the official headquarters of the United Nations since its completion in 1951." + }, + { + "id": 137, + "question":"At what year did Christopher Columbus discover America?", + "answer":"1492", + "info":"The explorer Christopher Columbus made four trips across the Atlantic Ocean from Spain: in 1492, 1493, 1498 and 1502. He was determined to find a direct water route west from Europe to Asia, but he never did. Instead, he stumbled upon the Americas" } ] } -- cgit v1.2.3 From ee829251a1f83b8620a76e6c81fe907ada1e1cc1 Mon Sep 17 00:00:00 2001 From: Xithrius <15021300+Xithrius@users.noreply.github.com> Date: Sun, 24 Jan 2021 01:21:24 -0800 Subject: Put a space after the colons for #562 trivia. --- bot/resources/evergreen/trivia_quiz.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/resources/evergreen/trivia_quiz.json b/bot/resources/evergreen/trivia_quiz.json index a5e3a3e4..faa3bc3b 100644 --- a/bot/resources/evergreen/trivia_quiz.json +++ b/bot/resources/evergreen/trivia_quiz.json @@ -250,15 +250,15 @@ }, { "id": 136, - "question":"The United Nations headquarters is located at which city?", - "answer":"New York", - "info":"The United Nations is headquartered in New York City in a complex designed by a board of architects led by Wallace Harrison and built by the architectural firm Harrison & Abramovitz. The complex has served as the official headquarters of the United Nations since its completion in 1951." + "question": "The United Nations headquarters is located at which city?", + "answer": "New York", + "info": "The United Nations is headquartered in New York City in a complex designed by a board of architects led by Wallace Harrison and built by the architectural firm Harrison & Abramovitz. The complex has served as the official headquarters of the United Nations since its completion in 1951." }, { "id": 137, - "question":"At what year did Christopher Columbus discover America?", - "answer":"1492", - "info":"The explorer Christopher Columbus made four trips across the Atlantic Ocean from Spain: in 1492, 1493, 1498 and 1502. He was determined to find a direct water route west from Europe to Asia, but he never did. Instead, he stumbled upon the Americas" + "question": "At what year did Christopher Columbus discover America?", + "answer": "1492", + "info": "The explorer Christopher Columbus made four trips across the Atlantic Ocean from Spain: in 1492, 1493, 1498 and 1502. He was determined to find a direct water route west from Europe to Asia, but he never did. Instead, he stumbled upon the Americas" } ] } -- cgit v1.2.3 From 948a64f3de06b946f7e4c3e1e730dd6849c58ca1 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sun, 24 Jan 2021 03:00:47 -0800 Subject: Refactored the xkcd command, added a refresher. --- bot/exts/evergreen/xkcd.py | 113 ++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index 11477216..5c100bf0 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -1,72 +1,71 @@ import logging -import random +from random import randint +from typing import Dict, Optional, Union -import discord -from discord.ext import commands +from discord import Embed +from discord.ext import tasks +from discord.ext.commands import Cog, Context, command + +from bot.bot import Bot log = logging.getLogger(__name__) +URL = "https://xkcd.com/{0}/info.0.json" +LATEST = "https://xkcd.com/info.0.json" + -class XKCD(commands.Cog): - """A cog for posting the XKCD .""" +class XKCD(Cog): + """Retrieving XKCD comics.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot) -> None: self.bot = bot + self.latest_comic_info: Dict[str, Union[str, int]] = {} + self.get_latest_comic_info.start() - @commands.command(name="xkcd") - async def fetch_xkcd_comics(self, ctx: commands.Context, comic: str = "latest") -> None: - """Read your Fav XKCD comics.""" - if comic not in ["random", "latest"]: - url = f"https://xkcd.com/{comic}/info.0.json" - else: - url = "https://xkcd.com/info.0.json" - - # ---- random choice ----- - if comic == "random": - async with self.bot.http_session.get(url) as r: - json_data = await r.json() - random_pick = random.randint(1, int(json_data["num"])) - url = f"https://xkcd.com/{random_pick}/info.0.json" - - log.trace(f"Querying xkcd API: {url}") - async with self.bot.http_session.get(url) as r: - if r.status == "200": - json_data = await r.json() + def cog_unload(self) -> None: + """Cancels refreshing of the task for refreshing the most recent comic info.""" + self.get_latest_comic_info.cancel() + + @tasks.loop(minutes=30) + async def get_latest_comic_info(self) -> None: + """Refreshes latest comic's information ever 30 minutes. Also used for finding a random comic.""" + async with self.bot.http_session.get(LATEST) as resp: + if resp.status == 200: + self.latest_comic_info = await resp.json() else: - # ----- Exception handling | Guides to use ------ - log.warning(f"Received response {r.status} from: {url}") - # -- get the latest comic number --- - url = f"https://xkcd.com/info.0.json" - async with self.bot.http_session.get(url) as r: - latest_data = await r.json() - - # --- beautify response --- - latest_num = latest_data["num"] - resp = discord.Embed( - title="Guides | Usage", - description=f''' - .xkcd latest (Retrieves the latest comic) - .xkcd random (Retrieves random comic) - .xkcd number (Enter a comic number between 1 & {latest_num}) - ''' - ) - return await ctx.send(embed=resp) - - # --- response variables ---- - day, month, year = json_data["day"], json_data["month"], json_data["year"] - comic_number = json_data["num"] - - # ---- beautify response ---- - embed = discord.Embed( - title=json_data['title'], - description=json_data["alt"] - ) - embed.set_image(url=json_data['img']) - embed.set_footer(text=f"Post date : {day}-{month}-{year} | xkcd comics - {comic_number}") + log.debug(f"Failed to get latest XKCD comic information. Status code {resp.status}") + + @command(name="xkcd") + async def fetch_xkcd_comics(self, ctx: Context, comic: Optional[str]) -> None: + """ + Getting an xkcd comic's information along with the image. + + To get a random comic, don't type any number as an argument. To get the latest, enter 0. + """ + embed = Embed() + + comic = comic or randint(1, self.latest_comic_info['num']) + + if comic == "latest": + info = self.latest_comic_info + + else: + async with self.bot.http_session.get(URL.format(comic)) as resp: + if resp.status == 200: + info = await resp.json() + else: + embed.description = f"{resp.status}: Could not retrieve xkcd comic #{comic}." + log.debug(f"Retrieving xkcd comic #{comic} failed with status code {resp.status}.") + await ctx.send(embed=embed) + return + + embed.set_image(url=info["img"]) + date = f"{info['year']}/{info['month']}/{info['day']}" + embed.set_footer(text=f"{date} - #{comic}, \'{info['safe_title']}\'") await ctx.send(embed=embed) -def setup(bot: commands.Bot) -> None: - """XKCD Cog load.""" +def setup(bot: Bot) -> None: + """Loading the XKCD cog.""" bot.add_cog(XKCD(bot)) -- cgit v1.2.3 From 075559cddd1ca3d99c5d85e4bc4f01687d845fda Mon Sep 17 00:00:00 2001 From: xithrius Date: Sun, 24 Jan 2021 03:17:40 -0800 Subject: Added footer comic number for random and latest. --- bot/exts/evergreen/xkcd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index 5c100bf0..06c3b4a2 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -61,7 +61,7 @@ class XKCD(Cog): embed.set_image(url=info["img"]) date = f"{info['year']}/{info['month']}/{info['day']}" - embed.set_footer(text=f"{date} - #{comic}, \'{info['safe_title']}\'") + embed.set_footer(text=f"{date} - #{info['num']}, \'{info['safe_title']}\'") await ctx.send(embed=embed) -- cgit v1.2.3 From 2d52b09977b73b5f7583e914ffc5465321548f6a Mon Sep 17 00:00:00 2001 From: xithrius Date: Sun, 24 Jan 2021 03:23:34 -0800 Subject: Added soft red color if the command fails. --- bot/exts/evergreen/xkcd.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index 06c3b4a2..b2f8879a 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -7,6 +7,7 @@ from discord.ext import tasks from discord.ext.commands import Cog, Context, command from bot.bot import Bot +from bot.constants import Colours log = logging.getLogger(__name__) @@ -55,6 +56,7 @@ class XKCD(Cog): info = await resp.json() else: embed.description = f"{resp.status}: Could not retrieve xkcd comic #{comic}." + embed.colour = Colours.soft_red log.debug(f"Retrieving xkcd comic #{comic} failed with status code {resp.status}.") await ctx.send(embed=embed) return -- cgit v1.2.3 From 295f0d33a4257d7d930a4da5ddf2f845f86ac730 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sun, 24 Jan 2021 03:59:09 -0800 Subject: Added handling for comic arguments and interactive comics. --- bot/exts/evergreen/xkcd.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index b2f8879a..cb61e5b8 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -1,4 +1,5 @@ import logging +import re from random import randint from typing import Dict, Optional, Union @@ -11,8 +12,8 @@ from bot.constants import Colours log = logging.getLogger(__name__) -URL = "https://xkcd.com/{0}/info.0.json" -LATEST = "https://xkcd.com/info.0.json" +COMIC_FORMAT = re.compile(r"latest|[0-9]+") +BASE_URL = "https://xkcd.com" class XKCD(Cog): @@ -30,7 +31,7 @@ class XKCD(Cog): @tasks.loop(minutes=30) async def get_latest_comic_info(self) -> None: """Refreshes latest comic's information ever 30 minutes. Also used for finding a random comic.""" - async with self.bot.http_session.get(LATEST) as resp: + async with self.bot.http_session.get(f"{BASE_URL}/info.0.json") as resp: if resp.status == 200: self.latest_comic_info = await resp.json() else: @@ -41,29 +42,42 @@ class XKCD(Cog): """ Getting an xkcd comic's information along with the image. - To get a random comic, don't type any number as an argument. To get the latest, enter 0. + To get a random comic, don't type any number as an argument. To get the latest, type 'latest'. """ - embed = Embed() + embed = Embed(title=f"XKCD comic #{self.latest_comic_info['num'] if comic == 'latest' else comic}") - comic = comic or randint(1, self.latest_comic_info['num']) + embed.colour = Colours.soft_red + + if (comic := re.match(COMIC_FORMAT, comic)) is None: + embed.description = "Inputted comic parameter should either be an integer or 'latest'." + await ctx.send(embed=embed) + return + + comic = comic.group(0) or randint(1, self.latest_comic_info['num']) if comic == "latest": info = self.latest_comic_info else: - async with self.bot.http_session.get(URL.format(comic)) as resp: + async with self.bot.http_session.get(f"{BASE_URL}/{comic}/info.0.json") as resp: if resp.status == 200: info = await resp.json() else: embed.description = f"{resp.status}: Could not retrieve xkcd comic #{comic}." - embed.colour = Colours.soft_red log.debug(f"Retrieving xkcd comic #{comic} failed with status code {resp.status}.") await ctx.send(embed=embed) return - embed.set_image(url=info["img"]) - date = f"{info['year']}/{info['month']}/{info['day']}" - embed.set_footer(text=f"{date} - #{info['num']}, \'{info['safe_title']}\'") + if info["img"][:-3] in ("jpg", "png", "gif"): + embed.set_image(url=info["img"]) + date = f"{info['year']}/{info['month']}/{info['day']}" + embed.set_footer(text=f"{date} - #{info['num']}, \'{info['safe_title']}\'") + embed.colour = Colours.soft_green + else: + embed.description = ( + "Selected comic is interactive, and cannot be displayed within an embed.\n" + f"Comic can be viewed [here](https://xkcd.com/{info['num']})" + ) await ctx.send(embed=embed) -- cgit v1.2.3 From f5ea1ff1a9013df7426abc3076f66d12e64cb6b8 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sun, 24 Jan 2021 04:00:38 -0800 Subject: Grammer formatting. --- bot/exts/evergreen/xkcd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index cb61e5b8..91006715 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -75,8 +75,8 @@ class XKCD(Cog): embed.colour = Colours.soft_green else: embed.description = ( - "Selected comic is interactive, and cannot be displayed within an embed.\n" - f"Comic can be viewed [here](https://xkcd.com/{info['num']})" + "The selected comic is interactive, and cannot be displayed within an embed.\n" + f"Comic can be viewed [here](https://xkcd.com/{info['num']})." ) await ctx.send(embed=embed) -- cgit v1.2.3 From 2c14deb0cd25ca8f1c80fc6e02d321dca1af75d1 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sun, 24 Jan 2021 04:24:09 -0800 Subject: Finished up optimization of statements. --- bot/exts/evergreen/xkcd.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index 91006715..e387d3c8 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -44,16 +44,16 @@ class XKCD(Cog): To get a random comic, don't type any number as an argument. To get the latest, type 'latest'. """ - embed = Embed(title=f"XKCD comic #{self.latest_comic_info['num'] if comic == 'latest' else comic}") + embed = Embed(title=f"XKCD comic '{comic}'") embed.colour = Colours.soft_red - if (comic := re.match(COMIC_FORMAT, comic)) is None: + if comic and (comic := re.match(COMIC_FORMAT, comic)) is None: embed.description = "Inputted comic parameter should either be an integer or 'latest'." await ctx.send(embed=embed) return - comic = comic.group(0) or randint(1, self.latest_comic_info['num']) + comic = randint(1, self.latest_comic_info['num']) if comic is None else comic.group(0) if comic == "latest": info = self.latest_comic_info @@ -63,12 +63,15 @@ class XKCD(Cog): if resp.status == 200: info = await resp.json() else: + embed.title = f"XKCD comic #{comic}" embed.description = f"{resp.status}: Could not retrieve xkcd comic #{comic}." log.debug(f"Retrieving xkcd comic #{comic} failed with status code {resp.status}.") await ctx.send(embed=embed) return - if info["img"][:-3] in ("jpg", "png", "gif"): + embed.title = f"XKCD comic #{info['num']}" + + if info["img"][-3:] in ("jpg", "png", "gif"): embed.set_image(url=info["img"]) date = f"{info['year']}/{info['month']}/{info['day']}" embed.set_footer(text=f"{date} - #{info['num']}, \'{info['safe_title']}\'") -- cgit v1.2.3 From 62b1b277146c19ba441b5dcceec073489dd7178a Mon Sep 17 00:00:00 2001 From: Xithrius <15021300+Xithrius@users.noreply.github.com> Date: Sun, 24 Jan 2021 04:38:59 -0800 Subject: Changed comic argument error for fluency of reading. Co-authored-by: ChrisJL --- bot/exts/evergreen/xkcd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index e387d3c8..674ad4b0 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -49,7 +49,7 @@ class XKCD(Cog): embed.colour = Colours.soft_red if comic and (comic := re.match(COMIC_FORMAT, comic)) is None: - embed.description = "Inputted comic parameter should either be an integer or 'latest'." + embed.description = "Comic parameter should either be an integer or 'latest'." await ctx.send(embed=embed) return -- cgit v1.2.3 From a81d30f34dc4d4fa6e8550437e6d329a4da4e746 Mon Sep 17 00:00:00 2001 From: xithrius Date: Sun, 24 Jan 2021 05:41:58 -0800 Subject: Removed newline between if/else statement. --- bot/exts/evergreen/xkcd.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/exts/evergreen/xkcd.py b/bot/exts/evergreen/xkcd.py index 674ad4b0..d3224bfe 100644 --- a/bot/exts/evergreen/xkcd.py +++ b/bot/exts/evergreen/xkcd.py @@ -57,7 +57,6 @@ class XKCD(Cog): if comic == "latest": info = self.latest_comic_info - else: async with self.bot.http_session.get(f"{BASE_URL}/{comic}/info.0.json") as resp: if resp.status == 200: -- cgit v1.2.3 From 7cdf800368e4e287a8fd2bc3b539395ff6c73e41 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 24 Jan 2021 17:10:08 +0200 Subject: Decrease timeout from 120 sec to 30 sec --- bot/exts/evergreen/tic_tac_toe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/evergreen/tic_tac_toe.py b/bot/exts/evergreen/tic_tac_toe.py index 22fff102..e1190502 100644 --- a/bot/exts/evergreen/tic_tac_toe.py +++ b/bot/exts/evergreen/tic_tac_toe.py @@ -58,7 +58,7 @@ class Player: ) try: - react, _ = await self.ctx.bot.wait_for('reaction_add', timeout=120.0, check=check_for_move) + react, _ = await self.ctx.bot.wait_for('reaction_add', timeout=30.0, check=check_for_move) except asyncio.TimeoutError: return True, None else: -- cgit v1.2.3