2016-08-15 11:26:10 +12:00
from discord . ext import commands
from discord . ext . commands . cooldowns import BucketType
2017-03-27 12:57:52 +13:00
import discord
2016-08-15 11:26:10 +12:00
from . utils import checks
2016-08-20 15:27:48 +12:00
import re
2016-09-01 14:01:43 +12:00
import random
2017-03-27 15:00:36 +13:00
import asyncio
2016-09-01 14:01:43 +12:00
2016-08-15 11:26:10 +12:00
class Game :
2016-09-01 14:01:43 +12:00
def __init__ ( self , word ) :
2016-08-15 11:26:10 +12:00
self . word = word
2016-08-16 15:30:52 +12:00
# This converts everything but spaces to a blank
2016-08-20 15:27:48 +12:00
self . blanks = " " . join ( letter if not re . search ( " [a-zA-Z0-9] " , letter ) else " _ " for letter in word )
2016-08-15 12:00:01 +12:00
self . failed_letters = [ ]
2016-08-15 11:26:10 +12:00
self . guessed_letters = [ ]
self . fails = 0
2016-08-17 03:30:04 +12:00
2016-08-15 11:26:10 +12:00
def guess_letter ( self , letter ) :
2016-08-16 15:30:52 +12:00
# No matter what, add this to guessed letters so we only have to do one check if a letter was already guessed
2016-08-15 12:00:01 +12:00
self . guessed_letters . append ( letter )
2016-08-17 03:30:04 +12:00
if letter . lower ( ) in self . word . lower ( ) :
2016-08-16 15:30:52 +12:00
# Replace every occurence of the guessed letter, with the correct letter
# Use the one in the word instead of letter, due to capitalization
2016-08-17 03:30:04 +12:00
self . blanks = " " . join (
word_letter if letter . lower ( ) == word_letter . lower ( ) else self . blanks [ i ] for i , word_letter in
enumerate ( self . word ) )
2016-08-15 11:26:10 +12:00
return True
else :
self . fails + = 1
2016-08-15 12:00:01 +12:00
self . failed_letters . append ( letter )
2016-08-15 11:26:10 +12:00
return False
2016-08-17 03:30:04 +12:00
2016-08-15 11:26:10 +12:00
def guess_word ( self , word ) :
2016-08-15 11:33:42 +12:00
if word . lower ( ) == self . word . lower ( ) :
2016-08-15 12:00:01 +12:00
self . blanks = self . word
2016-08-15 11:26:10 +12:00
return True
else :
self . fails + = 1
return False
2016-08-17 03:30:04 +12:00
2016-08-15 11:26:10 +12:00
def win ( self ) :
return self . word == self . blanks
2016-08-17 03:30:04 +12:00
2016-08-15 11:26:10 +12:00
def failed ( self ) :
return self . fails == 7
2016-08-17 03:30:04 +12:00
2016-08-15 11:26:10 +12:00
def __str__ ( self ) :
2016-08-16 15:30:52 +12:00
# 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
2016-08-15 11:31:23 +12:00
man = " —— \n "
man + = " | | \n "
man + = " {} | \n " . format ( " o " if self . fails > 0 else " " )
2016-08-17 03:30:04 +12:00
man + = " {} {} {} | \n " . format ( " / " if self . fails > 1 else " " , " | " if self . fails > 2 else " " ,
" \\ " if self . fails > 3 else " " )
2016-08-15 11:31:23 +12:00
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 "
2016-08-15 11:26:10 +12:00
fmt = " ``` \n {} ``` " . format ( man )
2016-08-16 15:30:52 +12:00
# Then just add the guesses and the blanks to the string
2016-08-15 12:00:01 +12:00
fmt + = " ``` \n Guesses: {} \n Word: {} ``` " . format ( " , " . join ( self . failed_letters ) , " " . join ( self . blanks ) )
2016-08-15 11:26:10 +12:00
return fmt
2016-08-17 03:30:04 +12:00
2016-08-15 11:26:10 +12:00
class Hangman :
def __init__ ( self , bot ) :
self . bot = bot
self . games = { }
2016-08-17 03:30:04 +12:00
2016-08-15 11:26:10 +12:00
def create ( self , word , ctx ) :
2016-08-16 15:30:52 +12:00
# Create a new game, then save it as the server's game
2016-09-01 14:01:43 +12:00
game = Game ( word )
2017-03-06 15:45:44 +13:00
self . games [ ctx . message . guild . id ] = game
2017-03-27 12:57:52 +13:00
game . author = ctx . message . author . id
2016-08-15 11:26:10 +12:00
return game
2016-08-17 03:30:04 +12:00
2017-04-09 15:04:46 +12:00
@commands.group ( aliases = [ ' hm ' ] , invoke_without_command = True )
@commands.guild_only ( )
2016-11-11 17:49:16 +13:00
@commands.cooldown ( 1 , 7 , BucketType . user )
2016-08-15 14:10:12 +12:00
@checks.custom_perms ( send_messages = True )
2016-08-15 11:26:10 +12:00
async def hangman ( self , ctx , * , guess ) :
2016-11-29 17:55:55 +13:00
""" Makes a guess towards the server ' s currently running hangman game
EXAMPLE : ! hangman e ( or ) ! hangman The Phrase !
RESULT : Hopefully a win ! """
2017-03-06 15:45:44 +13:00
game = self . games . get ( ctx . message . guild . id )
2016-08-15 11:26:10 +12:00
if not game :
2016-09-11 20:32:36 +12:00
ctx . command . reset_cooldown ( ctx )
2017-03-06 15:45:44 +13:00
await ctx . send ( " There are currently no hangman games running! " )
2016-08-15 11:26:10 +12:00
return
2017-03-27 12:57:52 +13:00
if game . author == ctx . message . author . id :
await ctx . send ( " You cannot guess on your own hangman game! " )
return
2016-08-17 03:30:04 +12:00
2016-08-16 15:30:52 +12:00
# 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
2016-08-15 11:26:10 +12:00
if len ( guess ) == 1 :
2016-08-15 12:00:01 +12:00
if guess in game . guessed_letters :
2016-09-11 20:32:36 +12:00
ctx . command . reset_cooldown ( ctx )
2017-03-06 15:45:44 +13:00
await ctx . send ( " That letter has already been guessed! " )
2016-08-16 15:30:52 +12:00
# Return here as we don't want to count this as a failure
2016-08-15 12:00:01 +12:00
return
2016-08-15 11:26:10 +12:00
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... "
2016-08-17 03:30:04 +12:00
2016-08-15 11:48:17 +12:00
if game . win ( ) :
2017-03-27 12:57:52 +13:00
fmt + = " You guys got it! The phrase was ` {} ` " . format ( game . word )
2017-03-06 15:45:44 +13:00
del self . games [ ctx . message . guild . id ]
2016-08-15 11:49:15 +12:00
elif game . failed ( ) :
2017-03-27 12:57:52 +13:00
fmt + = " Sorry, you guys failed...the phrase was ` {} ` " . format ( game . word )
2017-03-06 15:45:44 +13:00
del self . games [ ctx . message . guild . id ]
2016-08-15 11:26:10 +12:00
else :
fmt + = str ( game )
2016-08-17 03:30:04 +12:00
2017-03-06 15:45:44 +13:00
await ctx . send ( fmt )
2016-08-17 03:30:04 +12:00
2017-04-09 15:04:46 +12:00
@hangman.command ( name = ' create ' , aliases = [ ' start ' ] )
@commands.guild_only ( )
2016-08-15 14:10:12 +12:00
@checks.custom_perms ( send_messages = True )
2016-08-15 11:26:10 +12:00
async def create_hangman ( self , ctx ) :
""" This is used to create a new hangman game
2017-01-06 07:13:22 +13:00
A predefined phrase will be randomly chosen as the phrase to use
2016-11-29 17:55:55 +13:00
EXAMPLE : ! hangman start
RESULT : This is pretty obvious . - . """
2016-08-17 03:30:04 +12:00
2016-08-18 08:46:20 +12:00
# Only have one hangman game per server, since anyone
# In a server (except the creator) can guess towards the current game
2017-03-06 15:45:44 +13:00
if self . games . get ( ctx . message . guild . id ) is not None :
await ctx . send ( " Sorry but only one Hangman game can be running per server! " )
2016-08-15 11:26:10 +12:00
return
2016-08-17 03:30:04 +12:00
2017-03-27 12:57:52 +13:00
try :
msg = await ctx . message . author . send ( " Please respond with a phrase you would like to use for your hangman game in ** {} ** \n \n Please keep phrases less than 20 characters " . format ( ctx . message . guild . name ) )
except discord . Forbidden :
await ctx . send ( " I can ' t message you {} ! Please allow DM ' s so I can message you and ask for the hangman phrase you want to use! " . format ( ctx . message . author . display_name ) )
return
await ctx . send ( " I have DM ' d you {} , please respond there with the phrase you would like to setup " . format ( ctx . message . author . display_name ) )
def check ( m ) :
2017-04-10 10:32:50 +12:00
return m . channel == msg . channel and len ( m . content ) < 20
2017-03-27 12:57:52 +13:00
try :
msg = await self . bot . wait_for ( ' message ' , check = check , timeout = 60 )
except asyncio . TimeoutError :
await ctx . send ( " You took too long! Please look at your DM ' s next to as that ' s where I ' m asking for the phrase you want to use " )
return
2017-04-07 09:53:37 +12:00
forbidden_phrases = [ ' stop ' , ' delete ' , ' remove ' , ' end ' , ' create ' , ' start ' ]
if msg . content in forbidden_phrases :
await ctx . send ( " Detected forbidden hangman phrase; current forbidden phrases are: \n {} " . format ( " \n " . join ( forbidden_phrases ) ) )
return
2017-03-27 12:57:52 +13:00
game = self . create ( msg . content , ctx )
2016-08-16 15:30:52 +12:00
# Let them know the game has started, then print the current game so that the blanks are shown
2017-03-06 15:45:44 +13:00
await ctx . send (
2016-08-17 03:30:04 +12:00
" Alright, a hangman game has just started, you can start guessing now! \n {} " . format ( str ( game ) ) )
2016-08-31 10:33:46 +12:00
2017-04-09 15:04:46 +12:00
@hangman.command ( name = ' delete ' , aliases = [ ' stop ' , ' remove ' , ' end ' ] )
@commands.guild_only ( )
2016-08-22 08:23:47 +12:00
@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
2016-11-29 17:55:55 +13:00
Hopefully a moderator will not abuse it , but there ' s not much we can do to avoid that
EXAMPLE : ! hangman stop
RESULT : No more men being hung """
2017-03-06 15:45:44 +13:00
if self . games . get ( ctx . message . guild . id ) is None :
await ctx . send ( " There are no Hangman games running on this server! " )
2016-08-22 08:23:47 +12:00
return
2016-08-31 10:33:46 +12:00
2017-03-06 15:45:44 +13:00
del self . games [ ctx . message . guild . id ]
await ctx . send ( " I have just stopped the game of Hangman, a new should be able to be started now! " )
2016-08-17 03:30:04 +12:00
2016-08-15 11:27:03 +12:00
def setup ( bot ) :
bot . add_cog ( Hangman ( bot ) )