Created
August 2, 2020 06:11
-
-
Save crazygmr101/be73a7af0e8db6c416f49d1a79af0fad to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import asyncio | |
| import json | |
| import random | |
| from typing import Callable, Tuple | |
| import discord | |
| from discord.ext import commands | |
| import lib.checks | |
| async def _check_channel(ctx): | |
| if not lib.checks.istype(ctx.channel, lib.checks.BOT): | |
| await ctx.send("In a bot channel, please :3", delete_after=30) | |
| await ctx.message.delete() | |
| return False | |
| return True | |
| def _num_emoji(num: int): return f"{num}\ufe0f\u20e3" | |
| class Games(commands.Cog): | |
| def __init__(self, bot: commands.Bot): | |
| self.bot = bot | |
| with open("config/hangman.json") as file: | |
| self.hm_words = json.load(file) | |
| self.locked = [] | |
| async def _input(self, ctx, typ: type, cancel_str: str = "cancel", ch: Callable = None, err=None, | |
| check_author=True, | |
| return_author=False, del_error=60, del_response=False): | |
| def check(m): | |
| return ((m.author == ctx.author and m.channel == ctx.channel) or not check_author) and not m.author.bot | |
| while True: | |
| try: | |
| inp: discord.Message = await self.bot.wait_for('message', check=check, timeout=60.0) | |
| if del_response: | |
| await inp.delete() | |
| if inp.content.lower() == cancel_str.lower(): | |
| return (None, None) if return_author else None | |
| res = typ(inp.content.lower()) | |
| if ch: | |
| if not ch(res): | |
| raise ValueError | |
| return (res, inp.author) if return_author else res | |
| except ValueError: | |
| await ctx.send(err or "That's not a valid response, try again" + | |
| ("" if not cancel_str else f" or type `{cancel_str}` to quit"), delete_after=del_error) | |
| continue | |
| except asyncio.TimeoutError: | |
| await ctx.send("You took too long to respond ): Try to start over", delete_after=del_error) | |
| return (None, None) if return_author else None | |
| @commands.command() | |
| async def rps(self, ctx: commands.Context): | |
| """ | |
| Play rock-paper-scissors with the bot | |
| """ | |
| def point(a: str, b: str): | |
| a, b = a[0], b[0] | |
| return { | |
| "rr": 0, | |
| "pp": 0, | |
| "ss": 0, | |
| "rp": 1, | |
| "pr": -1, | |
| "sp": -1, | |
| "ps": 1, | |
| "sr": 1, | |
| "rs": -1 | |
| }[a + b] | |
| conv = { | |
| "r": "Rock", | |
| "p": "Paper", | |
| "s": "Scissors" | |
| } | |
| win = ["You got a point!", "It's a tie!", "I got a point!"] | |
| final = ["You win!", "It's a tie!", "I win."] | |
| if not await _check_channel(ctx): | |
| return | |
| await ctx.send("How many rounds? 1-10") | |
| rounds = await self._input(ctx, int, "cancel", lambda x: 0 < x <= 10) | |
| user = 0 | |
| comp = 0 | |
| for i in range(0, rounds): | |
| await ctx.send(f"Round {i + 1} - You: {user} | Me: {comp}\n" | |
| f"Input R P or S") | |
| choice = (await self._input(ctx, str, "cancel", lambda x: x[0].lower() in ["r", "p", "s"]))[0] | |
| if not choice: | |
| break | |
| compch = random.choice(["r", "p", "s"]) | |
| dev = point(choice, compch) | |
| if dev == 1: | |
| comp += 1 | |
| if dev == -1: | |
| user += 1 | |
| await ctx.send(f"{win[dev + 1]} I got {conv[compch]}, and you got {conv[choice]}") | |
| await ctx.send( | |
| f"Game over\n{final[1 if comp == user else 2 if comp > user else 0]} - You: {user} | Me: {comp}") | |
| @commands.max_concurrency(1, commands.BucketType.channel) | |
| @commands.command() | |
| async def hangman(self, ctx: commands.Context): | |
| if ctx.channel.id in self.locked: | |
| raise commands.MaxConcurrencyReached(1, commands.BucketType.channel) | |
| self.locked.append(ctx.channel.id) | |
| if not await _check_channel(ctx): | |
| return | |
| msg = await ctx.send("Starting...") | |
| while True: | |
| m = await ctx.send("Type a category, `list` to see categories or " | |
| "`cancel` to quit.") | |
| category = await self._input(ctx, str, del_response=True) | |
| await m.delete() | |
| if category == "cancel" or not category: | |
| await ctx.send("Cancelling") | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| return | |
| if category == "list": | |
| await ctx.send("Possible categories: " + ",".join(self.hm_words.keys())) | |
| await msg.delete() | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| return | |
| if category in self.hm_words.keys(): | |
| break | |
| for i in range(1, len(category)): | |
| categories = list(filter(lambda x: x.startswith(category[:i]), self.hm_words.keys())) | |
| if len(categories) == 0: | |
| await ctx.send("Invalid category") | |
| await msg.delete() | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| return | |
| elif len(categories) == 1: | |
| category = categories[0] | |
| break | |
| else: | |
| await ctx.send("You could mean more than one category - be more specific or do `list`" | |
| "to list categories. Cancelling") | |
| await msg.delete() | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| return | |
| if category in self.hm_words.keys(): | |
| break | |
| word2 = random.choice(self.hm_words[category]) | |
| word = word2.lower() | |
| turns = 10 | |
| correct = False | |
| guesses = [] | |
| wrong = [] | |
| guess = None | |
| author = None | |
| while turns > 0: | |
| failed = 0 | |
| s = [] | |
| for char in word: | |
| if char in guesses or char == " ": | |
| s.append(char) | |
| else: | |
| s.append("_") | |
| failed += 1 | |
| await msg.edit(content="", embed=discord.Embed( | |
| title=( | |
| "Right guess :)" if correct else "Wrong guess ):") + f" - {guess}" if guess else "Let's " | |
| "guess :D", | |
| description=f"Word `{' '.join(s)}`\n" | |
| f"Wrong Guesses: `{' '.join(wrong) if len(wrong) > 0 else ' '}`\n" | |
| f"{turns} guesses left\n" + | |
| (f"Last Guesser: {author.display_name}" if author else ""), | |
| colour=(discord.Colour.dark_green() | |
| if correct else discord.Colour.red()) | |
| if guess else discord.Colour.blue() | |
| ).set_footer(text=category)) | |
| if failed == 0: | |
| await msg.edit(content="", embed=discord.Embed( | |
| title="You win! :)", | |
| description=f"Word: {word2}\n" | |
| f"Wrong Guesses: `{' '.join(wrong) if len(wrong) > 0 else ' '}`\n" + | |
| (f"Last Guesser: {author.display_name}" if author else ""), | |
| colour=discord.Colour.green() | |
| ).set_footer(text=category)) | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| return | |
| correct = False | |
| guess, author = await self._input(ctx, str, ch=lambda x: (len(x) == 1 and x.isalpha()) or x == word, | |
| err="Not a letter (or the word).", check_author=False, | |
| return_author=True, | |
| del_response=True, del_error=10) | |
| if not guess: | |
| await ctx.send("Cancelling") | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| return | |
| guess = guess.lower() | |
| if guess == word: | |
| await msg.edit(content="", embed=discord.Embed( | |
| title="You win! :)", | |
| description=f"Word: {word2}\n" | |
| f"Wrong Guesses: `{' '.join(wrong) if len(wrong) > 0 else ' '}`\n" + | |
| (f"Last Guesser: {author.display_name}" if author else ""), | |
| colour=discord.Colour.green() | |
| ).set_footer(text=category)) | |
| return | |
| guesses.append(guess) | |
| if guess not in word: | |
| turns -= 1 | |
| wrong.append(guess) | |
| else: | |
| correct = True | |
| await msg.edit(content="", embed=discord.Embed( | |
| title="Game over :(", | |
| description=f"Word: {word2}\n" | |
| f"Guesses: {' '.join(guesses)}", | |
| colour=discord.Colour.red() | |
| ).set_footer(text=category)) | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| @hangman.error | |
| async def hm_error(self, ctx, error): | |
| if isinstance(error, commands.MaxConcurrencyReached): | |
| await ctx.send("Only one game can run in a channel at a time!", delete_after=10) | |
| else: | |
| raise | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| @commands.command(aliases=["ttt"]) | |
| @commands.max_concurrency(1, commands.BucketType.channel) | |
| async def tictactoe(self, ctx: commands.Context): | |
| if ctx.channel.id in self.locked: | |
| raise commands.MaxConcurrencyReached(1, commands.BucketType.channel) | |
| self.locked.append(ctx.channel.id) | |
| board = [[0] * 3 for _ in range(3)] | |
| _ne = _num_emoji | |
| def _c(x: int) -> Tuple[int, int]: | |
| return (x - 1) // 3, (x - 1) % 3 | |
| def _xo(num, neg=False): | |
| return [":x:", None, ":o:"][num + 1] if not neg else \ | |
| [":regional_indicator_x:", None, ":regional_indicator_o:"][num + 1] | |
| def _get_board(): | |
| s = "_ _\n" | |
| for i in range(1, 10): | |
| row, col = _c(i) | |
| cur = board[row][col] | |
| s += (_xo(cur) if cur else _ne(i)) | |
| if col == 2: | |
| s += "\n" | |
| return s | |
| def _status(): | |
| wins = [ | |
| [4, 5, 6], | |
| [1, 2, 3], | |
| [7, 8, 9], | |
| [8, 5, 2], | |
| [9, 6, 3], | |
| [7, 5, 3], | |
| [9, 5, 1], | |
| [7, 4, 1] | |
| ] | |
| for i in [-1, 1]: | |
| for row in wins: | |
| if all([board[_c(j)[0]][_c(j)[1]] == i for j in row]): | |
| return i, row | |
| for row in board: | |
| for col in row: | |
| if col == 0: | |
| return 0, [] | |
| return 2, [] | |
| def _make_next(): | |
| # make winning move | |
| for i in range(1, 10): | |
| orig = board[_c(i)[0]][_c(i)[1]] | |
| if orig != 0: | |
| continue | |
| board[_c(i)[0]][_c(i)[1]] = -1 | |
| if _status()[0] == -1: | |
| board[_c(i)[0]][_c(i)[1]] = -1 | |
| return | |
| board[_c(i)[0]][_c(i)[1]] = orig | |
| # block player's winning move | |
| for i in range(1, 10): | |
| orig = board[_c(i)[0]][_c(i)[1]] | |
| if orig != 0: | |
| continue | |
| board[_c(i)[0]][_c(i)[1]] = 1 | |
| if _status()[0] == 1: | |
| board[_c(i)[0]][_c(i)[1]] = -1 | |
| return | |
| board[_c(i)[0]][_c(i)[1]] = orig | |
| # pick a random square | |
| sq = random.choice(list(filter(lambda i: board[_c(i)[0]][_c(i)[1]] == 0, list(range(0, 9))))) | |
| board[_c(sq)[0]][_c(sq)[1]] = -1 | |
| comp = (random.random() > 0.5) | |
| msg = await ctx.send(embed=discord.Embed(title="Type 1-9", | |
| description=_get_board())) | |
| while True: | |
| if not comp: | |
| await msg.edit(embed=discord.Embed(title="Your turn!", | |
| description=_get_board(), colour=discord.Colour.blue())) | |
| sq = await self._input(ctx, int, ch=lambda x: (0 < x < 10) and board[_c(x)[0]][_c(x)[1]] == 0, | |
| del_response=True) | |
| board[_c(sq)[0]][_c(sq)[1]] = 1 | |
| if _status()[0] != 0: | |
| break | |
| else: | |
| await msg.edit(embed=discord.Embed(title="My turn!", | |
| description=_get_board(), colour=discord.Colour.gold())) | |
| async with ctx.typing(): | |
| await asyncio.sleep(1) | |
| _make_next() | |
| if _status()[0] != 0: | |
| break | |
| comp = not comp | |
| winner, win = _status() | |
| s = "_ _\n" | |
| for i in range(1, 10): | |
| row, col = _c(i) | |
| cur = board[row][col] | |
| s += (_xo(cur, neg=(i in win)) if cur else ":black_large_square:") | |
| if col == 2: | |
| s += "\n" | |
| if winner == 1: | |
| title = "You win!" | |
| color = discord.Colour.green() | |
| elif winner == -1: | |
| title = "You Lose ):" | |
| color = discord.Colour.red() | |
| else: | |
| title = "It's a tie" | |
| color = discord.Colour.purple() | |
| await msg.edit(embed=discord.Embed(title=title, | |
| description=s, colour=color)) | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| @tictactoe.error | |
| async def ttt_error(self, ctx, error): | |
| if isinstance(error, commands.MaxConcurrencyReached): | |
| await ctx.send("Only one game can run in a channel at a time!", delete_after=10) | |
| else: | |
| del self.locked[self.locked.index(ctx.channel.id)] | |
| raise | |
| def setup(bot): | |
| bot.add_cog(Games(bot)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment