Skip to content

Instantly share code, notes, and snippets.

@crazygmr101
Created August 2, 2020 06:11
Show Gist options
  • Select an option

  • Save crazygmr101/be73a7af0e8db6c416f49d1a79af0fad to your computer and use it in GitHub Desktop.

Select an option

Save crazygmr101/be73a7af0e8db6c416f49d1a79af0fad to your computer and use it in GitHub Desktop.
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