You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Fox-V3/hangman/hangman.py

357 lines
12 KiB

import discord
import os
from discord.ext import commands
from random import randint
from .utils.dataIO import dataIO
from .utils import checks
class Hangman:
"""Lets anyone play a game of hangman with custom phrases"""
def __init__(self, bot):
self.bot = bot
self.path = "data/Fox-Cogs/hangman"
self.file_path = "data/Fox-Cogs/hangman/hangman.json"
self.answer_path = "data/hangman/hanganswers.txt"
self.the_data = dataIO.load_json(self.file_path)
self.winbool = False
self.letters = "🇦🇧🇨🇩🇪🇫🇬🇭🇮🇯🇰🇱🇲🇳🇴🇵🇶🇷🇸🇹🇺🇻🇼🇽🇾🇿"
self.navigate = "🔼🔽"
self._updateHanglist()
def _updateHanglist(self):
self.hanglist = (
""">
\_________
|/
|
|
|
|
|
|\___
""",
""">
\_________
|/ |
|
|
|
|
|
|\___
H""",
""">
\_________
|/ |
| """+self.the_data["theface"]+"""
|
|
|
|
|\___
HA""",
""">
\________
|/ |
| """+self.the_data["theface"]+"""
| |
| |
|
|
|\___
HAN""",
""">
\_________
|/ |
| """+self.the_data["theface"]+"""
| /|
| |
|
|
|\___
HANG""",
""">
\_________
|/ |
| """+self.the_data["theface"]+"""
| /|\
| |
|
|
|\___
HANGM""",
""">
\________
|/ |
| """+self.the_data["theface"]+"""
| /|\
| |
| /
|
|\___
HANGMA""",
""">
\________
|/ |
| """+self.the_data["theface"]+"""
| /|\
| |
| / \
|
|\___
HANGMAN""")
def save_data(self):
"""Saves the json"""
dataIO.save_json(self.file_path, self.the_data)
@commands.group(aliases=['sethang'], pass_context=True)
@checks.mod_or_permissions(administrator=True)
async def hangset(self, ctx):
"""Adjust hangman settings"""
if ctx.invoked_subcommand is None:
await self.bot.send_cmd_help(ctx)
@hangset.command(pass_context=True)
async def face(self, ctx, theface):
message = ctx.message
#Borrowing FlapJack's emoji validation (https://github.com/flapjax/FlapJack-Cogs/blob/master/smartreact/smartreact.py)
if theface[:2] == "<:":
theface = [r for server in self.bot.servers for r in server.emojis if r.id == theface.split(':')[2][:-1]][0]
try:
# Use the face as reaction to see if it's valid (THANKS FLAPJACK <3)
await self.bot.add_reaction(message, theface)
self.the_data["theface"] = str(theface)
self.save_data()
self._updateHanglist()
await self.bot.say("Face has been updated!")
except discord.errors.HTTPException:
await self.bot.say("That's not an emoji I recognize.")
@commands.command(aliases=['hang'], pass_context=True)
async def hangman(self, ctx, guess: str=None):
"""Play a game of hangman against the bot!"""
if guess is None:
if self.the_data["running"]:
await self.bot.say("Game of hangman is already running!\nEnter your guess!")
self._printgame()
"""await self.bot.send_cmd_help(ctx)"""
else:
await self.bot.say("Starting a game of hangman!")
self._startgame()
await self._printgame()
elif not self.the_data["running"]:
await self.bot.say("Game of hangman is not yet running!\nStarting a game of hangman!")
self._startgame()
await self._printgame()
else:
await self._guessletter(guess)
def _startgame(self):
"""Starts a new game of hangman"""
self.the_data["answer"] = self._getphrase().upper()
self.the_data["hangman"] = 0
self.the_data["guesses"] = []
self.winbool = False
self.the_data["running"] = True
self.the_data["trackmessage"] = False
self.save_data()
def _stopgame(self):
"""Stops the game in current state"""
self.the_data["running"] = False
self.save_data()
async def _checkdone(self, channel=None):
if self.winbool:
if channel:
await self.bot.send_message(channel, "You Win!")
else:
await self.bot.say("You Win!")
self._stopgame()
if self.the_data["hangman"] >= 7:
if channel:
await self.bot.send_message(channel, "You Lose!\nThe Answer was: **"+self.the_data["answer"]+"**")
else:
await self.bot.say("You Lose!\nThe Answer was: **"+self.the_data["answer"]+"**")
self._stopgame()
def _getphrase(self):
"""Get a new phrase for the game and returns it"""
phrasefile = open(self.answer_path, 'r')
phrases = phrasefile.readlines()
outphrase = ""
while outphrase == "":
outphrase = phrases[randint(0, len(phrases)-1)].partition(" (")[0]
# outphrase = phrases[randint(0,10)].partition(" (")[0]
return outphrase
def _hideanswer(self):
"""Returns the obscured answer"""
out_str = ""
self.winbool = True
for i in self.the_data["answer"]:
if i == " " or i == "-":
out_str += i*2
elif i in self.the_data["guesses"] or i not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
out_str += "__"+i+"__ "
else:
out_str += "**\_** "
self.winbool = False
return out_str
def _guesslist(self):
"""Returns the current letter list"""
out_str = ""
for i in self.the_data["guesses"]:
out_str += str(i) + ","
out_str = out_str[:-1]
return out_str
async def _guessletter(self, guess, channel=None):
"""Checks the guess on a letter and prints game if acceptable guess"""
if not guess.upper() in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or not len(guess) == 1:
if channel:
await self.bot.send_message(channel, "Invalid guess. Only A-Z is accepted")
else:
await self.bot.say("Invalid guess. Only A-Z is accepted")
return
if guess.upper() in self.the_data["guesses"]:
if channel:
await self.bot.send_message(channel, "Already guessed that! Try again")
else:
await self.bot.say("Already guessed that! Try again")
return
if not guess.upper() in self.the_data["answer"]:
self.the_data["hangman"] += 1
self.the_data["guesses"].append(guess.upper())
self.save_data()
await self._printgame(channel)
async def _on_react(self, reaction, user):
""" Thanks to flapjack reactpoll for guidelines
https://github.com/flapjax/FlapJack-Cogs/blob/master/reactpoll/reactpoll.py"""
if not self.the_data["trackmessage"]:
return
if user == self.bot.user:
return # Don't remove bot's own reactions
message = reaction.message
emoji = reaction.emoji
if not message.id == self.the_data["trackmessage"]:
return
if str(emoji) in self.letters:
letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[self.letters.index(str(emoji))]
await self._guessletter(letter, message.channel)
if str(emoji) in self.navigate:
if str(emoji) == self.navigate[0]:
await self._reactmessage_am(message)
if str(emoji) == self.navigate[-1]:
await self._reactmessage_nz(message)
async def _reactmessage_menu(self, message):
"""React with menu options"""
await self.bot.clear_reactions(message)
await self.bot.add_reaction(message, self.navigate[0])
await self.bot.add_reaction(message, self.navigate[-1])
async def _reactmessage_am(self, message):
await self.bot.clear_reactions(message)
for x in range(len(self.letters)):
if x in [i for i,b in enumerate("ABCDEFGHIJKLM") if b not in self._guesslist()]:
await self.bot.add_reaction(message, self.letters[x])
await self.bot.add_reaction(message, self.navigate[-1])
async def _reactmessage_nz(self, message):
await self.bot.clear_reactions(message)
for x in range(len(self.letters)):
if x in [i for i,b in enumerate("NOPQRSTUVWXYZ") if b not in self._guesslist()]:
await self.bot.add_reaction(message, self.letters[x+13])
await self.bot.add_reaction(message, self.navigate[0])
async def _printgame(self, channel=None):
"""Print the current state of game"""
cSay = ("Guess this: " + str(self._hideanswer()) + "\n"
+ "Used Letters: " + str(self._guesslist()) + "\n"
+ self.hanglist[self.the_data["hangman"]] + "\n"
+ self.navigate[0]+" for A-M, "+self.navigate[-1]+" for N-Z")
if channel:
message = await self.bot.send_message(channel, cSay)
else:
message = await self.bot.say(cSay)
self.the_data["trackmessage"] = message.id
self.save_data()
await self._reactmessage_menu(message)
await self._checkdone(channel)
def check_folders():
if not os.path.exists("data/Fox-Cogs"):
print("Creating data/Fox-Cogs folder...")
os.makedirs("data/Fox-Cogs")
if not os.path.exists("data/Fox-Cogs/hangman"):
print("Creating data/Fox-Cogs/hangman folder...")
os.makedirs("data/Fox-Cogs/hangman")
def check_files():
if not dataIO.is_valid_json("data/Fox-Cogs/hangman/hangman.json"):
dataIO.save_json("data/Fox-Cogs/hangman/hangman.json", {"running": False, "hangman": 0, "guesses": [], "theface": "<:never:336861463446814720>", "trackmessage": False})
def setup(bot):
check_folders()
check_files()
n = Hangman(bot)
bot.add_cog(n)
bot.add_listener(n._on_react, "on_reaction_add")