diff options
-rw-r--r-- | bot/exts/fun/hangman.py | 182 | ||||
-rw-r--r-- | bot/resources/fun/hangman_words.txt | 877 |
2 files changed, 1059 insertions, 0 deletions
diff --git a/bot/exts/fun/hangman.py b/bot/exts/fun/hangman.py new file mode 100644 index 00000000..a2c8c735 --- /dev/null +++ b/bot/exts/fun/hangman.py @@ -0,0 +1,182 @@ +from asyncio import TimeoutError +from pathlib import Path +from random import choice + +from discord import Embed, Message +from discord.ext import commands + +from bot.bot import Bot +from bot.constants import Colours, NEGATIVE_REPLIES + +# Defining all words in the list of words as a global variable +ALL_WORDS = Path("bot/resources/fun/hangman_words.txt").read_text().splitlines() + +# Defining a dictionary of images that will be used for the game to represent the hangman person +IMAGES = { + 6: "https://cdn.discordapp.com/attachments/859123972884922418/888133201497837598/hangman0.png", + 5: "https://cdn.discordapp.com/attachments/859123972884922418/888133595259084800/hangman1.png", + 4: "https://cdn.discordapp.com/attachments/859123972884922418/888134194474139688/hangman2.png", + 3: "https://cdn.discordapp.com/attachments/859123972884922418/888133758069395466/hangman3.png", + 2: "https://cdn.discordapp.com/attachments/859123972884922418/888133786724859924/hangman4.png", + 1: "https://cdn.discordapp.com/attachments/859123972884922418/888133828831477791/hangman5.png", + 0: "https://cdn.discordapp.com/attachments/859123972884922418/888133845449338910/hangman6.png", +} + + +class Hangman(commands.Cog): + """ + Cog for the Hangman game. + + Hangman is a classic game where the user tries to guess a word, with a limited amount of tries. + """ + + def __init__(self, bot: Bot): + self.bot = bot + + @staticmethod + def create_embed(tries: int, user_guess: str) -> Embed: + """ + Helper method that creates the embed where the game information is shown. + + This includes how many letters the user has guessed so far, and the hangman photo itself. + """ + hangman_embed = Embed( + title="Hangman", + color=Colours.python_blue, + ) + hangman_embed.set_image(url=IMAGES[tries]) + hangman_embed.add_field( + name=f"You've guessed `{user_guess}` so far.", + value="Guess the word by sending a message with a letter!" + ) + hangman_embed.set_footer(text=f"Tries remaining: {tries}") + return hangman_embed + + @commands.command() + async def hangman( + self, + ctx: commands.Context, + min_length: int = 0, + max_length: int = 25, + min_unique_letters: int = 0, + max_unique_letters: int = 25, + ) -> None: + """ + Play hangman against the bot, where you have to guess the word it has provided! + + The arguments for this command mean: + - min_length: the minimum length you want the word to be (i.e. 2) + - max_length: the maximum length you want the word to be (i.e. 5) + - min_unique_letters: the minimum unique letters you want the word to have (i.e. 4) + - max_unique_letters: the maximum unique letters you want the word to have (i.e. 7) + """ + # Filtering the list of all words depending on the configuration + filtered_words = [ + word for word in ALL_WORDS + if min_length < len(word) < max_length + and min_unique_letters < len(set(word)) < max_unique_letters + ] + + if not filtered_words: + filter_not_found_embed = Embed( + title=choice(NEGATIVE_REPLIES), + description="No words could be found that fit all filters specified.", + color=Colours.soft_red, + ) + await ctx.send(embed=filter_not_found_embed) + return + + word = choice(filtered_words) + # `pretty_word` is used for comparing the indices where the guess of the user is similar to the word + # The `user_guess` variable is prettified by adding spaces between every dash, and so is the `pretty_word` + pretty_word = ''.join([f"{letter} " for letter in word])[:-1] + user_guess = ("_ " * len(word))[:-1] + tries = 6 + guessed_letters = set() + + def check(msg: Message) -> bool: + return msg.author == ctx.author and msg.channel == ctx.channel + + original_message = await ctx.send(embed=Embed( + title="Hangman", + description="Loading game...", + color=Colours.soft_green + )) + + # Game loop + while user_guess.replace(' ', '') != word: + # Edit the message to the current state of the game + await original_message.edit(embed=self.create_embed(tries, user_guess)) + + try: + message = await self.bot.wait_for( + event="message", + timeout=60.0, + check=check + ) + except TimeoutError: + timeout_embed = Embed( + title=choice(NEGATIVE_REPLIES), + description="Looks like the bot timed out! You must send a letter within 60 seconds.", + color=Colours.soft_red, + ) + await original_message.edit(embed=timeout_embed) + return + + # If the user enters a capital letter as their guess, it is automatically converted to a lowercase letter + normalized_content = message.content.lower() + # The user should only guess one letter per message + if len(normalized_content) > 1: + letter_embed = Embed( + title=choice(NEGATIVE_REPLIES), + description="You can only send one letter at a time, try again!", + color=Colours.dark_green, + ) + await ctx.send(embed=letter_embed, delete_after=4) + continue + + # Checks for repeated guesses + elif normalized_content in guessed_letters: + already_guessed_embed = Embed( + title=choice(NEGATIVE_REPLIES), + description=f"You have already guessed `{normalized_content}`, try again!", + color=Colours.dark_green, + ) + await ctx.send(embed=already_guessed_embed, delete_after=4) + continue + + # Checks for correct guesses from the user + elif normalized_content in word: + positions = {idx for idx, letter in enumerate(pretty_word) if letter == normalized_content} + user_guess = "".join( + [normalized_content if index in positions else dash for index, dash in enumerate(user_guess)] + ) + + else: + tries -= 1 + + if tries <= 0: + losing_embed = Embed( + title="You lost.", + description=f"The word was `{word}`.", + color=Colours.soft_red, + ) + await original_message.edit(embed=self.create_embed(tries, user_guess)) + await ctx.send(embed=losing_embed) + return + + guessed_letters.add(normalized_content) + + # The loop exited meaning that the user has guessed the word + await original_message.edit(embed=self.create_embed(tries, user_guess)) + win_embed = Embed( + title="You won!", + description=f"The word was `{word}`.", + color=Colours.grass_green + ) + await ctx.send(embed=win_embed) + + +def setup(bot: Bot) -> None: + """Load the Hangman cog.""" + bot.add_cog(Hangman(bot)) diff --git a/bot/resources/fun/hangman_words.txt b/bot/resources/fun/hangman_words.txt new file mode 100644 index 00000000..5e20bfde --- /dev/null +++ b/bot/resources/fun/hangman_words.txt @@ -0,0 +1,877 @@ +abandon +ability +able +about +above +accept +according +account +across +action +activity +actually +address +administration +admit +adult +affect +after +again +against +agency +agent +agree +agreement +ahead +allow +almost +alone +along +already +also +although +always +among +amount +analysis +animal +another +answer +anyone +anything +appear +apply +approach +area +argue +around +arrive +article +artist +assume +attack +attention +attorney +audience +author +authority +available +avoid +away +baby +back +ball +bank +base +beat +beautiful +because +become +before +begin +behavior +behind +believe +benefit +best +better +between +beyond +bill +billion +black +blood +blue +board +body +book +born +both +break +bring +brother +budget +build +building +business +bytecode +call +camera +campaign +cancer +candidate +capital +card +care +career +carry +case +catch +cause +cell +center +central +century +certain +certainly +chair +challenge +chance +change +character +charge +check +child +choice +choose +church +citizen +city +civil +claim +class +clear +clearly +close +coach +cold +collection +college +color +come +commercial +common +community +company +compare +computer +concern +condition +conference +consider +consumer +contain +continue +control +cost +could +country +couple +course +court +cover +create +crime +cultural +culture +current +customer +dark +data +daughter +dead +deal +death +debate +decade +decide +decision +deep +defense +degree +describe +design +despite +detail +determine +develop +development +dictionary +difference +different +difficult +dinner +direction +director +discover +discuss +discussion +disease +doctor +door +down +draw +dream +drive +drop +during +each +early +east +easy +economic +economy +edge +education +effect +effort +eight +either +election +else +employee +energy +enjoy +enough +enter +entire +environment +environmental +especially +establish +even +evening +event +ever +every +everybody +everyone +everything +evidence +exactly +example +executive +exist +expect +experience +expert +explain +face +fact +factor +fall +false +family +fast +father +fear +federal +feel +feeling +field +fight +figure +fill +film +final +finally +financial +find +fine +finger +finish +fire +firm +first +fish +five +floor +focus +follow +food +foot +force +foreign +forget +form +former +forward +four +free +friend +from +front +full +function +fund +future +game +garden +general +generation +girl +give +glass +goal +good +government +great +green +ground +group +grow +growth +guess +guido +hair +half +hand +hang +happen +happy +hard +have +head +health +hear +heart +heat +heavy +help +here +herself +high +himself +history +hold +home +hope +hospital +hotel +hour +house +however +huge +human +hundred +husband +idea +identify +image +imagine +impact +import +important +improve +include +including +increase +indeed +indicate +individual +industry +information +inside +instead +institution +interest +interesting +international +interpreter +interview +into +investment +involve +issue +item +itself +join +just +keep +kill +kind +kitchen +know +knowledge +land +lambda +language +large +last +late +later +laugh +lawyer +lead +leader +learn +least +leave +left +legal +less +letter +level +life +light +like +likely +line +list +listen +little +live +local +long +look +loop +lose +loss +love +machine +magazine +main +maintain +major +majority +make +manage +management +manager +many +market +marriage +material +matter +maybe +mean +measure +media +medical +meet +meeting +member +memory +mention +message +method +middle +might +military +million +mind +minute +miss +mission +model +modern +moment +money +month +more +morning +most +mother +mouth +move +movement +movie +much +music +must +myself +name +nation +national +natural +nature +near +nearly +necessary +need +network +never +news +newspaper +next +nice +nightnone +north +note +nothing +notice +number +object +occur +offer +office +officer +official +often +once +only +onto +open +operation +opportunity +option +order +organization +other +others +outside +over +owner +page +pain +painting +paper +parameters +parent +part +participant +particular +particularly +partner +party +pass +past +patient +pattern +peace +people +perform +performance +perhaps +period +person +personal +phone +physical +pick +picture +piece +place +plan +plant +play +player +point +police +policy +political +politics +poor +popular +population +position +positive +possible +power +practice +prepare +present +president +pressure +pretty +prevent +price +print +private +probably +problem +process +produce +product +production +professional +professor +program +project +property +protect +prove +provide +public +pull +purpose +push +pydis +pygame +python +quality +question +quickly +quite +race +radio +raise +range +rate +rather +reach +read +ready +real +reality +realize +really +reason +receive +recent +recently +recognize +record +reduce +reflect +region +relate +relationship +remain +remember +remove +report +represent +require +research +resource +respond +response +responsibility +rest +result +return +reveal +rich +right +rise +risk +road +rock +role +room +rule +safe +same +save +scene +school +science +scientist +score +season +seat +second +section +security +seek +seem +sell +send +senior +sense +series +serious +serve +service +seven +several +shake +share +shoot +short +shot +should +shoulder +show +side +sign +significant +similar +simple +simply +since +sing +single +sister +site +situation +size +skill +skin +small +smile +social +society +soldier +some +somebody +someone +something +sometimes +song +soon +sort +sound +source +south +southern +space +speak +special +specific +speech +spend +sport +spring +staff +stage +stand +standard +star +start +state +statement +station +stay +step +still +stock +stop +store +story +strategy +street +strong +structure +student +study +stuff +style +subject +success +successful +such +suddenly +suffer +suggest +summer +support +sure +surface +system +table +take +talk +task +teach +teacher +team +technology +television +tell +tend +term +test +than +thank +that +their +them +themselves +then +theory +there +these +they +thing +think +third +this +those +though +thought +thousand +threat +three +through +throughout +throw +thus +time +today +together +tonight +total +tough +toward +town +trade +traditional +training +travel +treat +treatment +tree +trial +trip +trouble +true +truth +turn +type +under +understand +unit +until +upon +usually +value +variable +various +very +victim +view +visit +voice +vote +wait +walk +wall +want +watch +water +wear +week +weight +well +west +western +what +whatever +when +where +whether +which +while +white +whole +whom +whose +wide +wife +will +wind +window +wish +with +within +without +woman +wonder +word +work +worker +world +worry +would +write +writer +wrong +yard +yield +yeah +year +young +your +yourself |