245 lines
6.7 KiB
Python
245 lines
6.7 KiB
Python
import discord
|
|
from redbot.core.data_manager import bundled_data_path
|
|
from redbot.core.data_manager import cog_data_path
|
|
from redbot.core import commands
|
|
from redbot.core import Config
|
|
from redbot.core import checks
|
|
from random import randint
|
|
import asyncio, os
|
|
|
|
|
|
class Hangman(commands.Cog):
|
|
"""Play hangman with the bot."""
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
self.config = Config.get_conf(self, identifier=7345167902)
|
|
self.config.register_guild(
|
|
fp = str(bundled_data_path(self) / 'words.txt'),
|
|
doEdit = True
|
|
)
|
|
self.man = [
|
|
(
|
|
' ___ \n'
|
|
' | | \n'
|
|
' | O \n'
|
|
' | \n'
|
|
' | \n'
|
|
' | \n'
|
|
' | \n'
|
|
), (
|
|
' ___ \n'
|
|
' | | \n'
|
|
' | O \n'
|
|
' | | \n'
|
|
' | | \n'
|
|
' | \n'
|
|
' | \n'
|
|
), (
|
|
' ___ \n'
|
|
' | | \n'
|
|
' | O \n'
|
|
' | \\| \n'
|
|
' | | \n'
|
|
' | \n'
|
|
' | \n'
|
|
), (
|
|
' ___ \n'
|
|
' | | \n'
|
|
' | O \n'
|
|
' | \\|/ \n'
|
|
' | | \n'
|
|
' | \n'
|
|
' | \n'
|
|
), (
|
|
' ___ \n'
|
|
' | | \n'
|
|
' | O \n'
|
|
' | \\|/ \n'
|
|
' | | \n'
|
|
' | / \n'
|
|
' | \n'
|
|
), (
|
|
' ___ \n'
|
|
' | | \n'
|
|
' | O \n'
|
|
' | \\|/ \n'
|
|
' | | \n'
|
|
' | / \\ \n'
|
|
' | \n'
|
|
), (
|
|
' ___ \n'
|
|
' | | \n'
|
|
' | X \n'
|
|
' | \\|/ \n'
|
|
' | | \n'
|
|
' | / \\ \n'
|
|
' | \n'
|
|
)
|
|
]
|
|
|
|
@staticmethod
|
|
def _get_message(word, guessed):
|
|
"""Returns a string of the guessing text."""
|
|
p = ''
|
|
for l in word:
|
|
if l not in 'abcdefghijklmnopqrstuvwxyz': #auto print non letter characters
|
|
p += l + ' '
|
|
elif l in guessed: #print already guessed characters
|
|
p += l + ' '
|
|
else:
|
|
p += '_ '
|
|
p += ' ('
|
|
for l in guessed:
|
|
if l not in word:
|
|
p += l #add the incorrect guessed letters
|
|
p += ')'
|
|
return p
|
|
|
|
@commands.command()
|
|
async def hangman(self, ctx):
|
|
"""Play hangman with the bot."""
|
|
if ctx.guild is None: #default vars in pms
|
|
fp = str(bundled_data_path(self) / 'words.txt')
|
|
doEdit = False #cant delete messages in pms
|
|
else: #server specific vars
|
|
fp = await self.config.guild(ctx.guild).fp()
|
|
doEdit = await self.config.guild(ctx.guild).doEdit()
|
|
try:
|
|
f = open(fp)
|
|
except FileNotFoundError:
|
|
await ctx.send('Your wordlist was not found, using the default wordlist.')
|
|
f = open(str(bundled_data_path(self) / 'words.txt'))
|
|
wordlist = [line.strip().lower() for line in f]
|
|
word = wordlist[randint(0,len(wordlist)-1)] #pick and format random word
|
|
guessed = ''
|
|
fails = 0
|
|
game = True
|
|
err = 0
|
|
boardmsg = None
|
|
check = lambda m: (
|
|
m.channel == ctx.message.channel
|
|
and m.author == ctx.message.author
|
|
and len(m.content) == 1
|
|
and m.content.lower() in 'abcdefghijklmnopqrstuvwxyz'
|
|
)
|
|
while game:
|
|
p = self._get_message(word, guessed)
|
|
p = f'```{self.man[fails]}\n{p}```'
|
|
if err == 1:
|
|
p += 'You already guessed that letter.\n'
|
|
if boardmsg is None or not doEdit:
|
|
boardmsg = await ctx.send(p+'Guess:')
|
|
else:
|
|
await boardmsg.edit(content=str(p+'Guess:'))
|
|
try:
|
|
umsg = await self.bot.wait_for('message', check=check, timeout=60)
|
|
except asyncio.TimeoutError:
|
|
return await ctx.send(
|
|
f'Canceling selection. You took too long.\nThe word was {word}.'
|
|
)
|
|
t = umsg.content.lower()
|
|
if doEdit:
|
|
await asyncio.sleep(.2)
|
|
try:
|
|
await umsg.delete()
|
|
except (discord.errors.Forbidden, discord.errors.NotFound):
|
|
pass
|
|
if t in guessed:
|
|
err = 1
|
|
continue
|
|
err = 0
|
|
guessed += t
|
|
if t not in word:
|
|
fails += 1
|
|
if fails == 6: #too many fails
|
|
p = self._get_message(word, guessed)
|
|
p = f'```{self.man[fails]}\n{p}```Game Over\nThe word was {word}.'
|
|
if doEdit:
|
|
await boardmsg.edit(content=p)
|
|
else:
|
|
await ctx.send(p)
|
|
game = False
|
|
continue
|
|
#guessed entire word
|
|
if not (set('abcdefghijklmnopqrstuvwxyz') & set(word)) - set(guessed):
|
|
p = self._get_message(word, guessed)
|
|
p = f'```{self.man[fails]}\n{p}```You win!\nThe word was {word}.'
|
|
if doEdit:
|
|
await boardmsg.edit(content=p)
|
|
else:
|
|
await ctx.send(p)
|
|
game = False
|
|
|
|
@commands.guild_only()
|
|
@checks.guildowner()
|
|
@commands.group()
|
|
async def hangmanset(self, ctx):
|
|
"""Config options for hangman."""
|
|
pass
|
|
|
|
@hangmanset.group(invoke_without_command=True)
|
|
async def wordlist(self, ctx, value: str):
|
|
"""
|
|
Change the wordlist used.
|
|
|
|
Extra wordlists can be put in the data folder.
|
|
Wordlists are a .txt file with every new line being a new word.
|
|
This value is server specific.
|
|
"""
|
|
wordlists = [p.resolve() for p in cog_data_path(self).glob("*.txt")]
|
|
try:
|
|
fp = next(p for p in wordlists if p.stem == value)
|
|
await self.config.guild(ctx.guild).fp.set(str(fp))
|
|
await ctx.send(f'The wordlist is now set to `{value}`.')
|
|
except StopIteration:
|
|
await ctx.send(f'Wordlist `{value}` not found.')
|
|
|
|
@wordlist.command()
|
|
async def default(self, ctx):
|
|
"""Set the wordlist to the default list."""
|
|
fp = str(bundled_data_path(self) / 'words.txt')
|
|
await self.config.guild(ctx.guild).fp.set(fp)
|
|
await ctx.send('The wordlist is now set to the default list.')
|
|
|
|
@wordlist.command()
|
|
async def list(self, ctx):
|
|
"""List available wordlists."""
|
|
wordlists = [p.resolve().stem for p in cog_data_path(self).glob("*.txt")]
|
|
if wordlists == []:
|
|
return await ctx.send('You do not have any wordlists.')
|
|
msg = '\n'.join(wordlists).strip()
|
|
await ctx.send(f'Available wordlists:```\n{msg}```')
|
|
|
|
@wordlist.command()
|
|
async def current(self, ctx):
|
|
"""Show the current wordlist."""
|
|
v = await self.config.guild(ctx.guild).fp()
|
|
if str(v) == str(bundled_data_path(self) / 'words.txt'):
|
|
await ctx.send('The wordlist is set to the default list.')
|
|
else:
|
|
await ctx.send(f'The wordlist is set to `{str(v)[str(v).find("Hangman")+8:-4]}`.')
|
|
|
|
@hangmanset.command()
|
|
async def edit(self, ctx, value: bool=None):
|
|
"""
|
|
Set if hangman messages should be one edited message or many individual messages.
|
|
|
|
Defaults to True.
|
|
This value is server specific.
|
|
"""
|
|
if value is None:
|
|
v = await self.config.guild(ctx.guild).doEdit()
|
|
if v:
|
|
await ctx.send('Games are currently being played on a single, edited message.')
|
|
else:
|
|
await ctx.send('Games are currently being played on multiple messages.')
|
|
else:
|
|
await self.config.guild(ctx.guild).doEdit.set(value)
|
|
if value:
|
|
await ctx.send('Games will now be played on a single, edited message.')
|
|
else:
|
|
await ctx.send('Games will now be played on multiple messages.')
|
|
|
|
async def red_delete_data_for_user(self, **kwargs):
|
|
"""Nothing to delete."""
|
|
return
|