1
0
Fork 0
mirror of synced 2024-05-24 06:19:39 +12:00
Bonfire/cogs/hangman.py
2016-08-21 15:23:47 -05:00

178 lines
7.6 KiB
Python

from discord.ext import commands
from discord.ext.commands.cooldowns import BucketType
from .utils import checks
import re
class Game:
def __init__(self, word, creator):
self.word = word
self.creator = creator
# This converts everything but spaces to a blank
# TODO: Only convert [a-zA-Z0-9]
self.blanks = "".join(letter if not re.search("[a-zA-Z0-9]", letter) else "_" for letter in word)
self.failed_letters = []
self.guessed_letters = []
self.fails = 0
def guess_letter(self, letter):
# No matter what, add this to guessed letters so we only have to do one check if a letter was already guessed
self.guessed_letters.append(letter)
if letter.lower() in self.word.lower():
# Replace every occurence of the guessed letter, with the correct letter
# Use the one in the word instead of letter, due to capitalization
self.blanks = "".join(
word_letter if letter.lower() == word_letter.lower() else self.blanks[i] for i, word_letter in
enumerate(self.word))
return True
else:
self.fails += 1
self.failed_letters.append(letter)
return False
def guess_word(self, word):
if word.lower() == self.word.lower():
self.blanks = self.word
return True
else:
self.fails += 1
return False
def win(self):
return self.word == self.blanks
def failed(self):
return self.fails == 7
def __str__(self):
# Here's our fancy formatting for the hangman picture
# Each position in the hangman picture is either a space, or part of the man, based on how many fails there are
man = " ——\n"
man += " | |\n"
man += " {} |\n".format("o" if self.fails > 0 else " ")
man += " {}{}{} |\n".format("/" if self.fails > 1 else " ", "|" if self.fails > 2 else " ",
"\\" if self.fails > 3 else " ")
man += " {} |\n".format("|" if self.fails > 4 else " ")
man += " {} {} |\n".format("/" if self.fails > 5 else " ", "\\" if self.fails > 6 else " ")
man += " |\n"
man += " ———————\n"
fmt = "```\n{}```".format(man)
# Then just add the guesses and the blanks to the string
fmt += "```\nGuesses: {}\nWord: {}```".format(", ".join(self.failed_letters), " ".join(self.blanks))
return fmt
class Hangman:
def __init__(self, bot):
self.bot = bot
self.games = {}
def create(self, word, ctx):
# Create a new game, then save it as the server's game
game = Game(word, ctx.message.author)
self.games[ctx.message.server.id] = game
return game
@commands.group(aliases=['hm'], pass_context=True, no_pm=True, invoke_without_command=True)
@commands.cooldown(1, 30, BucketType.user)
@checks.custom_perms(send_messages=True)
async def hangman(self, ctx, *, guess):
"""Makes a guess towards the server's currently running hangman game"""
game = self.games.get(ctx.message.server.id)
if not game:
await self.bot.say("There are currently no hangman games running!")
return
if ctx.message.author == game.creator:
await self.bot.say("You can't guess at your own hangman game! :S")
return
# Check if we are guessing a letter or a phrase. Only one letter can be guessed at a time
# So if anything more than one was provided, we're guessing at the phrase
# We're creating a fmt variable, so that we can add a message for if a guess was correct or not
# And also add a message for a loss/win
if len(guess) == 1:
if guess in game.guessed_letters:
await self.bot.say("That letter has already been guessed!")
# Return here as we don't want to count this as a failure
return
if game.guess_letter(guess):
fmt = "That's correct!"
else:
fmt = "Sorry, that letter is not in the phrase..."
else:
if game.guess_word(guess):
fmt = "That's correct!"
else:
fmt = "Sorry that's not the correct phrase..."
if game.win():
fmt += " You guys got it! The word was `{}`".format(game.word)
del self.games[ctx.message.server.id]
elif game.failed():
fmt += " Sorry, you guys failed...the word was `{}`".format(game.word)
del self.games[ctx.message.server.id]
else:
fmt += str(game)
await self.bot.say(fmt)
@hangman.command(name='create', aliases=['start'], no_pm=True, pass_context=True)
@checks.custom_perms(send_messages=True)
async def create_hangman(self, ctx):
"""This is used to create a new hangman game
Due to the fact that I might not be able to delete a message, I will PM you and ask for the phrase you want.
The phrase needs to be under 30 characters"""
# Only have one hangman game per server, since anyone
# In a server (except the creator) can guess towards the current game
if self.games.get(ctx.message.server.id) is not None:
await self.bot.say("Sorry but only one Hangman game can be running per server!")
return
# Make sure the phrase is less than 30 characters
check = lambda m: len(m.content) < 30
# Doing this so that while we wait for the phrase, another one cannot be started.
self.games[ctx.message.server.id] = "placeholder"
# We want to send this message instead of just PM'ing the creator
# As some people have PM's turned off/ don't pay attention to them
await self.bot.say(
"I have just PM'd you {}, please respond there with the phrase you want to start a new"
" hangman game with".format(ctx.message.author.display_name))
# The only reason we save this variable, is so that we can retrieve
# The PrivateChannel for it, for use in our wait_for_message command
_msg = await self.bot.whisper("Please respond with the phrase you would like to use for your new hangman game\n"
"Please note that it must be under 30 characters long")
msg = await self.bot.wait_for_message(timeout=60.0, channel=_msg.channel, check=check)
if not msg:
await self.bot.whisper("Sorry, you took too long.")
del self.games[ctx.message.server.id]
return
else:
game = self.create(msg.content, ctx)
# Let them know the game has started, then print the current game so that the blanks are shown
await self.bot.say(
"Alright, a hangman game has just started, you can start guessing now!\n{}".format(str(game)))
@hangman.command(name='delete', aliases=['stop', 'remove'], pass_context=True, no_pm=True)
@checks.custom_perms(kick_members=True)
async def stop_game(self, ctx):
"""Force stops a game of hangman
This should realistically only be used in a situation like one player leaves
Hopefully a moderator will not abuse it, but there's not much we can do to avoid that"""
if self.games.get(ctx.message.server.id) is None:
await self.bot.say("There are no Hangman games running on this server!")
return
del self.games[ctx.message.server.id]
await self.bot.say("I have just stopped the game of Hangman, a new should be able to be started now!")
def setup(bot):
bot.add_cog(Hangman(bot))