658 lines
23 KiB
Python
658 lines
23 KiB
Python
"""
|
|
Deals with commands and logic for hangman games.
|
|
|
|
*Classes*
|
|
---------
|
|
Hangman()
|
|
Deals with the game logic of hangman.
|
|
DrawHangman()
|
|
Draws the image shown to the player.
|
|
"""
|
|
import os
|
|
import datetime # Used for generating the game id
|
|
import string # string.ascii_uppercase used
|
|
import math # Used by DrawHangman(), mainly for drawing circles
|
|
import random # Used to draw poorly
|
|
import requests # Used for getting the word in Hangman.start()
|
|
import discord # Used for discord.file and type hints
|
|
|
|
from interactions.utils.manage_components import (create_button,
|
|
create_actionrow)
|
|
from interactions.model import ButtonStyle
|
|
from interactions import SlashContext, ComponentContext
|
|
# Used for typehints
|
|
from PIL import ImageDraw, Image, ImageFont # Used to draw the image
|
|
|
|
from gwendolyn_old.utils import encode_id
|
|
|
|
class Hangman():
|
|
"""
|
|
Controls hangman commands and game logic.
|
|
|
|
*Methods*
|
|
---------
|
|
start(ctx: SlashContext)
|
|
stop(ctx: SlashContext)
|
|
guess(message: discord.message, user: str, guess: str)
|
|
"""
|
|
|
|
def __init__(self, bot):
|
|
"""
|
|
Initialize the class.
|
|
|
|
*Attributes*
|
|
------------
|
|
draw: DrawHangman
|
|
The DrawHangman used to draw the hangman image.
|
|
API_url: str
|
|
The url to get the words from.
|
|
APIPARAMS: dict
|
|
The parameters to pass to every api call.
|
|
"""
|
|
self.bot = bot
|
|
self._draw = DrawHangman(bot)
|
|
self._API_url = "https://api.wordnik.com/v4/words.json/randomWords?" # pylint: disable=invalid-name
|
|
api_key = self.bot.credentials["wordnik_key"]
|
|
self._APIPARAMS = { # pylint: disable=invalid-name
|
|
"hasDictionaryDef": True,
|
|
"minCorpusCount": 5000,
|
|
"maxCorpusCount": -1,
|
|
"minDictionaryCount": 1,
|
|
"maxDictionaryCount": -1,
|
|
"minLength": 3,
|
|
"maxLength": 11,
|
|
"limit": 1,
|
|
"api_key": api_key
|
|
}
|
|
|
|
async def start(self, ctx: SlashContext):
|
|
"""
|
|
Start a game of hangman.
|
|
|
|
*Parameters*
|
|
------------
|
|
ctx: SlashContext
|
|
The context of the command.
|
|
"""
|
|
await self.bot.defer(ctx)
|
|
|
|
word = "-"
|
|
while "-" in word or "." in word:
|
|
response = requests.get(self._API_url, params=self._APIPARAMS)
|
|
word = list(response.json()[0]["word"].upper())
|
|
|
|
self.bot.log("Found the word \""+"".join(word)+"\"")
|
|
guessed = [False] * len(word)
|
|
game_id = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
|
|
remaining_letters = list(string.ascii_uppercase)
|
|
|
|
self._draw.draw_image(game_id, 0, word, guessed, [])
|
|
|
|
send_message = f"{ctx.author.display_name} started a game of hangman."
|
|
|
|
self.bot.log("Game started")
|
|
await ctx.send(send_message)
|
|
|
|
boards_path = "gwendolyn/resources/games/hangman_boards/"
|
|
file_path = f"{boards_path}hangman_board{game_id}.png"
|
|
image_message = await ctx.channel.send(
|
|
file=discord.File(file_path)
|
|
)
|
|
|
|
blank_message_one = await ctx.channel.send("_ _")
|
|
blank_message_two = await ctx.channel.send("_ _")
|
|
|
|
buttons = []
|
|
for letter in remaining_letters:
|
|
custom_id = encode_id([
|
|
"hangman",
|
|
"guess",
|
|
str(ctx.author.id),
|
|
letter,
|
|
"".join(word),
|
|
"",
|
|
game_id,
|
|
str(image_message.id),
|
|
str(blank_message_one.id),
|
|
str(blank_message_two.id)
|
|
])
|
|
buttons.append(create_button(
|
|
style=ButtonStyle.blue,
|
|
label=letter,
|
|
custom_id=custom_id
|
|
))
|
|
|
|
custom_id = encode_id([
|
|
"hangman",
|
|
"end",
|
|
str(ctx.author.id),
|
|
game_id,
|
|
str(image_message.id),
|
|
str(blank_message_one.id),
|
|
str(blank_message_two.id)
|
|
])
|
|
buttons.append(create_button(
|
|
style=ButtonStyle.red,
|
|
label="End game",
|
|
custom_id=custom_id
|
|
))
|
|
|
|
action_row_lists = []
|
|
for i in range(((len(buttons)-1)//20)+1):
|
|
action_rows = []
|
|
available_buttons = buttons[(i*20):(min(len(buttons),i*20+20))]
|
|
for x in range(((len(available_buttons)-1)//4)+1):
|
|
row_buttons = available_buttons[
|
|
(x*4):(min(len(available_buttons),x*4+4))
|
|
]
|
|
action_rows.append(create_actionrow(*row_buttons))
|
|
action_row_lists.append(action_rows)
|
|
|
|
|
|
await blank_message_one.edit(components=action_row_lists[0])
|
|
await blank_message_two.edit(components=action_row_lists[1])
|
|
|
|
|
|
async def stop(self, ctx: ComponentContext, game_id: str,
|
|
*messages: list[str]):
|
|
"""
|
|
Stop the game of hangman.
|
|
|
|
*Parameters*
|
|
------------
|
|
ctx: SlashContext
|
|
The context of the command.
|
|
"""
|
|
|
|
for msg_id in messages:
|
|
msg = await ctx.channel.fetch_message(msg_id)
|
|
await msg.delete()
|
|
|
|
self.bot.log("Deleting old messages")
|
|
|
|
await ctx.channel.send("Hangman game stopped")
|
|
boards_path = "gwendolyn/resources/games/hangman_boards/"
|
|
file_path = f"{boards_path}hangman_board{game_id}.png"
|
|
os.remove(file_path)
|
|
|
|
async def guess(self, ctx: ComponentContext, guess: str, word: str,
|
|
guessed_letters: str, game_id: str, *messages: list[str]):
|
|
"""
|
|
Guess a letter.
|
|
|
|
*Parameters*
|
|
------------
|
|
message: discord.Message
|
|
The message that the reaction was placed on.
|
|
user: str
|
|
The id of the user.
|
|
guess: str
|
|
The guess.
|
|
"""
|
|
for msg_id in messages:
|
|
msg = await ctx.channel.fetch_message(msg_id)
|
|
await msg.delete()
|
|
|
|
misses = 0
|
|
guessed_letters += guess
|
|
guessed = [False for i in range(len(word))]
|
|
for guessed_letter in guessed_letters:
|
|
correct_guess = 0
|
|
for i, letter in enumerate(word):
|
|
if guessed_letter == letter:
|
|
correct_guess += 1
|
|
guessed[i] = True
|
|
|
|
if correct_guess == 0:
|
|
misses += 1
|
|
|
|
|
|
remaining_letters = list(string.ascii_uppercase)
|
|
for letter in guessed_letters:
|
|
remaining_letters.remove(letter)
|
|
|
|
if correct_guess == 1:
|
|
send_message = "Guessed {}. There was 1 {} in the word."
|
|
send_message = send_message.format(guess, guess)
|
|
else:
|
|
send_message = "Guessed {}. There were {} {}s in the word."
|
|
send_message = send_message.format(guess, correct_guess, guess)
|
|
|
|
self._draw.draw_image(game_id, misses, word, guessed, guessed_letters)
|
|
|
|
if misses == 6:
|
|
send_message += self.bot.long_strings["Hangman lost game"]
|
|
remaining_letters = []
|
|
elif all(guessed):
|
|
self.bot.money.addMoney(f"#{ctx.author_id}", 15)
|
|
send_message += self.bot.long_strings["Hangman guessed word"]
|
|
remaining_letters = []
|
|
|
|
|
|
if remaining_letters != []:
|
|
await ctx.channel.send(send_message)
|
|
boards_path = "gwendolyn/resources/games/hangman_boards/"
|
|
file_path = f"{boards_path}hangman_board{game_id}.png"
|
|
image_message = await ctx.channel.send(
|
|
file=discord.File(file_path)
|
|
)
|
|
blank_message_one = await ctx.channel.send("_ _")
|
|
blank_message_two = await ctx.channel.send("_ _")
|
|
buttons = []
|
|
for letter in list(string.ascii_uppercase):
|
|
custom_id = encode_id([
|
|
"hangman",
|
|
"guess",
|
|
str(ctx.author.id),
|
|
letter,
|
|
word,
|
|
guessed_letters,
|
|
game_id,
|
|
str(image_message.id),
|
|
str(blank_message_one.id),
|
|
str(blank_message_two.id)
|
|
])
|
|
buttons.append(create_button(
|
|
style=ButtonStyle.blue,
|
|
label=letter,
|
|
custom_id=custom_id,
|
|
disabled=(letter not in remaining_letters)
|
|
))
|
|
custom_id = encode_id([
|
|
"hangman",
|
|
"end",
|
|
str(ctx.author.id),
|
|
game_id,
|
|
str(image_message.id),
|
|
str(blank_message_one.id),
|
|
str(blank_message_two.id)
|
|
])
|
|
buttons.append(create_button(
|
|
style=ButtonStyle.red,
|
|
label="End game",
|
|
custom_id=custom_id
|
|
))
|
|
|
|
action_row_lists = []
|
|
for i in range(((len(buttons)-1)//20)+1):
|
|
action_rows = []
|
|
available_buttons = buttons[(i*20):(min(len(buttons),i*20+20))]
|
|
for x in range(((len(available_buttons)-1)//4)+1):
|
|
row_buttons = available_buttons[
|
|
(x*4):(min(len(available_buttons),x*4+4))
|
|
]
|
|
action_rows.append(create_actionrow(*row_buttons))
|
|
action_row_lists.append(action_rows)
|
|
|
|
await blank_message_one.edit(components=action_row_lists[0])
|
|
await blank_message_two.edit(components=action_row_lists[1])
|
|
else:
|
|
boards_path = "gwendolyn/resources/games/hangman_boards/"
|
|
file_path = f"{boards_path}hangman_board{game_id}.png"
|
|
await ctx.channel.send(send_message, file=discord.File(file_path))
|
|
|
|
os.remove(file_path)
|
|
|
|
|
|
|
|
|
|
class DrawHangman():
|
|
"""
|
|
Draws the image of the hangman game.
|
|
|
|
*Methods*
|
|
---------
|
|
draw_image(channel: str)
|
|
"""
|
|
|
|
def __init__(self, bot):
|
|
"""
|
|
Initialize the class.
|
|
|
|
*Attributes*
|
|
------------
|
|
CIRCLESIZE
|
|
LINEWIDTH
|
|
BODYSIZE
|
|
LIMBSIZE
|
|
ARMPOSITION
|
|
MANX, MANY
|
|
LETTERLINELENGTH
|
|
LETTERLINEDISTANCE
|
|
GALLOWX, GALLOWY
|
|
PHI
|
|
FONT
|
|
SMALLFONT
|
|
"""
|
|
self._bot = bot
|
|
# pylint: disable=invalid-name
|
|
self._CIRCLESIZE = 120
|
|
self._LINEWIDTH = 12
|
|
|
|
self._BODYSIZE = 210
|
|
self._LIMBSIZE = 60
|
|
self._ARMPOSITION = 60
|
|
|
|
self._MANX = (self._LIMBSIZE*2)
|
|
self._MANY = (self._CIRCLESIZE+self._BODYSIZE+self._LIMBSIZE)
|
|
MANPADDING = self._LINEWIDTH*4
|
|
self._MANX += MANPADDING
|
|
self._MANY += MANPADDING
|
|
|
|
self._LETTERLINELENGTH = 90
|
|
self._LETTERLINEDISTANCE = 30
|
|
|
|
self._GALLOWX, self._GALLOWY = 360, 600
|
|
self._PHI = 1-(1 / ((1 + 5 ** 0.5) / 2))
|
|
|
|
LETTERSIZE = 75 # Wrong guesses letter size
|
|
WORDSIZE = 70 # Correct guesses letter size
|
|
|
|
FONTPATH = "gwendolyn/resources/fonts/comic-sans-bold.ttf"
|
|
self._FONT = ImageFont.truetype(FONTPATH, LETTERSIZE)
|
|
self._SMALLFONT = ImageFont.truetype(FONTPATH, WORDSIZE)
|
|
# pylint: enable=invalid-name
|
|
|
|
def _deviate(self, pre_deviance: int, pre_deviance_accuracy: int,
|
|
position_change: float, maxmin: int,
|
|
max_acceleration: float):
|
|
random_deviance = random.uniform(-position_change, position_change)
|
|
deviance_accuracy = pre_deviance_accuracy + random_deviance
|
|
if deviance_accuracy > maxmin * max_acceleration:
|
|
deviance_accuracy = maxmin * max_acceleration
|
|
elif deviance_accuracy < -maxmin * max_acceleration:
|
|
deviance_accuracy = -maxmin * max_acceleration
|
|
|
|
deviance = pre_deviance + deviance_accuracy
|
|
if deviance > maxmin:
|
|
deviance = maxmin
|
|
elif deviance < -maxmin:
|
|
deviance = -maxmin
|
|
return deviance, deviance_accuracy
|
|
|
|
def _bad_circle(self):
|
|
circle_padding = (self._LINEWIDTH*3)
|
|
image_width = self._CIRCLESIZE+circle_padding
|
|
image_size = (image_width, image_width)
|
|
background = Image.new("RGBA", image_size, color=(0, 0, 0, 0))
|
|
|
|
drawer = ImageDraw.Draw(background, "RGBA")
|
|
middle = (self._CIRCLESIZE+(self._LINEWIDTH*3))/2
|
|
deviance_x = 0
|
|
deviance_y = 0
|
|
deviance_accuracy_x = 0
|
|
deviance_accuracy_y = 0
|
|
start = random.randint(-100, -80)
|
|
degrees_amount = 360 + random.randint(-10, 30)
|
|
|
|
for degree in range(degrees_amount):
|
|
deviance_x, deviance_accuracy_x = self._deviate(
|
|
deviance_x,
|
|
deviance_accuracy_x,
|
|
self._LINEWIDTH/100,
|
|
self._LINEWIDTH,
|
|
0.03
|
|
)
|
|
deviance_y, deviance_accuracy_y = self._deviate(
|
|
deviance_y,
|
|
deviance_accuracy_y,
|
|
self._LINEWIDTH/100,
|
|
self._LINEWIDTH,
|
|
0.03
|
|
)
|
|
|
|
radians = math.radians(degree+start)
|
|
circle_x = (math.cos(radians) * (self._CIRCLESIZE/2))
|
|
circle_y = (math.sin(radians) * (self._CIRCLESIZE/2))
|
|
|
|
position_x = middle + circle_x - (self._LINEWIDTH/2) + deviance_x
|
|
position_y = middle + circle_y - (self._LINEWIDTH/2) + deviance_y
|
|
|
|
circle_position = [
|
|
(position_x, position_y),
|
|
(position_x+self._LINEWIDTH, position_y+self._LINEWIDTH)
|
|
]
|
|
drawer.ellipse(circle_position, fill=(0, 0, 0, 255))
|
|
|
|
return background
|
|
|
|
def _bad_line(self, length: int, rotated: bool = False):
|
|
if rotated:
|
|
width, height = length+self._LINEWIDTH*3, self._LINEWIDTH*3
|
|
else:
|
|
width, height = self._LINEWIDTH*3, length+self._LINEWIDTH*3
|
|
background = Image.new("RGBA", (width, height), color=(0, 0, 0, 0))
|
|
|
|
drawer = ImageDraw.Draw(background, "RGBA")
|
|
|
|
possible_deviance = int(self._LINEWIDTH/3)
|
|
deviance_x = random.randint(-possible_deviance, possible_deviance)
|
|
deviance_y = 0
|
|
deviance_accuracy_x = 0
|
|
deviance_accuracy_y = 0
|
|
|
|
for pixel in range(length):
|
|
deviance_x, deviance_accuracy_x = self._deviate(
|
|
deviance_x,
|
|
deviance_accuracy_x,
|
|
self._LINEWIDTH/1000,
|
|
self._LINEWIDTH,
|
|
0.004
|
|
)
|
|
deviance_y, deviance_accuracy_y = self._deviate(
|
|
deviance_y,
|
|
deviance_accuracy_y,
|
|
self._LINEWIDTH/1000,
|
|
self._LINEWIDTH,
|
|
0.004
|
|
)
|
|
|
|
if rotated:
|
|
position_x = self._LINEWIDTH + pixel + deviance_x
|
|
position_y = self._LINEWIDTH + deviance_y
|
|
else:
|
|
position_x = self._LINEWIDTH + deviance_x
|
|
position_y = self._LINEWIDTH + pixel + deviance_y
|
|
|
|
circle_position = [
|
|
(position_x, position_y),
|
|
(position_x+self._LINEWIDTH, position_y+self._LINEWIDTH)
|
|
]
|
|
drawer.ellipse(circle_position, fill=(0, 0, 0, 255))
|
|
|
|
return background
|
|
|
|
def _draw_man(self, misses: int, seed: str):
|
|
random.seed(seed)
|
|
man_size = (self._MANX, self._MANY)
|
|
background = Image.new("RGBA", man_size, color=(0, 0, 0, 0))
|
|
|
|
if misses >= 1:
|
|
head = self._bad_circle()
|
|
paste_x = (self._MANX-(self._CIRCLESIZE+(self._LINEWIDTH*3)))//2
|
|
paste_position = (paste_x, 0)
|
|
background.paste(head, paste_position, head)
|
|
if misses >= 2:
|
|
body = self._bad_line(self._BODYSIZE)
|
|
paste_x = (self._MANX-(self._LINEWIDTH*3))//2
|
|
paste_position = (paste_x, self._CIRCLESIZE)
|
|
background.paste(body, paste_position, body)
|
|
|
|
if misses >= 3:
|
|
limbs = random.sample(["rl", "ll", "ra", "la"], min(misses-2, 4))
|
|
else:
|
|
limbs = []
|
|
|
|
random.seed(seed)
|
|
|
|
for limb in limbs:
|
|
limb_drawing = self._bad_line(self._LIMBSIZE, True)
|
|
x_position = (self._MANX-(self._LINEWIDTH*3))//2
|
|
|
|
if limb[1] == "a":
|
|
rotation = random.randint(-45, 45)
|
|
shift = math.sin(math.radians(rotation))
|
|
line_length = self._LIMBSIZE+(self._LINEWIDTH*3)
|
|
compensation = int(shift*line_length)
|
|
limb_drawing = limb_drawing.rotate(rotation, expand=1)
|
|
y_position = self._CIRCLESIZE + self._ARMPOSITION
|
|
if limb == "ra":
|
|
compensation = min(-compensation, 0)
|
|
else:
|
|
x_position -= self._LIMBSIZE
|
|
compensation = min(compensation, 0)
|
|
|
|
y_position += compensation
|
|
else:
|
|
rotation = random.randint(-15, 15)
|
|
y_position = self._CIRCLESIZE+self._BODYSIZE-self._LINEWIDTH
|
|
if limb == "rl":
|
|
limb_drawing = limb_drawing.rotate(rotation-45, expand=1)
|
|
else:
|
|
x_position += -limb_drawing.size[0]+self._LINEWIDTH*3
|
|
limb_drawing = limb_drawing.rotate(rotation+45, expand=1)
|
|
|
|
paste_position = (x_position, y_position)
|
|
background.paste(limb_drawing, paste_position, limb_drawing)
|
|
|
|
return background
|
|
|
|
def _bad_text(self, text: str, big: bool, color: tuple = (0, 0, 0, 255)):
|
|
if big:
|
|
font = self._FONT
|
|
else:
|
|
font = self._SMALLFONT
|
|
width, height = font.getsize(text)
|
|
img = Image.new("RGBA", (width, height), color=(0, 0, 0, 0))
|
|
drawer = ImageDraw.Draw(img, "RGBA")
|
|
|
|
drawer.text((0, 0), text, font=font, fill=color)
|
|
return img
|
|
|
|
def _draw_gallows(self):
|
|
gallow_size = (self._GALLOWX, self._GALLOWY)
|
|
background = Image.new("RGBA", gallow_size, color=(0, 0, 0, 0))
|
|
|
|
bottom_line = self._bad_line(int(self._GALLOWX * 0.75), True)
|
|
bottom_line_x = int(self._GALLOWX * 0.125)
|
|
bottom_line_y = self._GALLOWY-(self._LINEWIDTH*4)
|
|
paste_position = (bottom_line_x, bottom_line_y)
|
|
background.paste(bottom_line, paste_position, bottom_line)
|
|
|
|
line_two = self._bad_line(self._GALLOWY-self._LINEWIDTH*6)
|
|
line_two_x = int(self._GALLOWX*(0.75*self._PHI))
|
|
line_two_y = self._LINEWIDTH*2
|
|
paste_position = (line_two_x, line_two_y)
|
|
background.paste(line_two, paste_position, line_two)
|
|
|
|
top_line = self._bad_line(int(self._GALLOWY*0.30), True)
|
|
paste_x = int(self._GALLOWX*(0.75*self._PHI))-self._LINEWIDTH
|
|
paste_position = (paste_x, self._LINEWIDTH*3)
|
|
background.paste(top_line, paste_position, top_line)
|
|
|
|
last_line = self._bad_line(int(self._GALLOWY*0.125))
|
|
paste_x += int(self._GALLOWY*0.30)
|
|
background.paste(last_line, (paste_x, self._LINEWIDTH*3), last_line)
|
|
return background
|
|
|
|
def _draw_letter_lines(self, word: str, guessed: list, misses: int):
|
|
letter_width = self._LETTERLINELENGTH+self._LETTERLINEDISTANCE
|
|
image_width = letter_width*len(word)
|
|
image_size = (image_width, self._LETTERLINELENGTH+self._LINEWIDTH*3)
|
|
letter_lines = Image.new("RGBA", image_size, color=(0, 0, 0, 0))
|
|
for i, letter in enumerate(word):
|
|
line = self._bad_line(self._LETTERLINELENGTH, True)
|
|
paste_x = i*(self._LETTERLINELENGTH+self._LETTERLINEDISTANCE)
|
|
paste_position = (paste_x, self._LETTERLINELENGTH)
|
|
letter_lines.paste(line, paste_position, line)
|
|
if guessed[i]:
|
|
letter_drawing = self._bad_text(letter, True)
|
|
letter_width = self._FONT.getsize(letter)[0]
|
|
letter_x = i*(self._LETTERLINELENGTH+self._LETTERLINEDISTANCE)
|
|
letter_x -= (letter_width//2)
|
|
letter_x += (self._LETTERLINELENGTH//2)+(self._LINEWIDTH*2)
|
|
letter_lines.paste(
|
|
letter_drawing,
|
|
(letter_x, 0),
|
|
letter_drawing
|
|
)
|
|
elif misses == 6:
|
|
letter_drawing = self._bad_text(letter, True, (242, 66, 54))
|
|
letter_width = self._FONT.getsize(letter)[0]
|
|
letter_x = i*(self._LETTERLINELENGTH+self._LETTERLINEDISTANCE)
|
|
letter_x -= (letter_width//2)
|
|
letter_x += (self._LETTERLINELENGTH//2)+(self._LINEWIDTH*2)
|
|
letter_lines.paste(
|
|
letter_drawing,
|
|
(letter_x, 0),
|
|
letter_drawing
|
|
)
|
|
|
|
return letter_lines
|
|
|
|
def _shortest_dist(self, positions: list, new_position: tuple):
|
|
shortest_dist = math.inf
|
|
for i, j in positions:
|
|
x_distance = abs(i-new_position[0])
|
|
y_distance = abs(j-new_position[1])
|
|
dist = math.sqrt(x_distance**2+y_distance**2)
|
|
if shortest_dist > dist:
|
|
shortest_dist = dist
|
|
return shortest_dist
|
|
|
|
def _draw_misses(self, guesses: list, word: str):
|
|
background = Image.new("RGBA", (600, 400), color=(0, 0, 0, 0))
|
|
pos = []
|
|
for guess in guesses:
|
|
if guess not in word:
|
|
placed = False
|
|
while not placed:
|
|
letter = self._bad_text(guess, True)
|
|
w, h = self._FONT.getsize(guess)
|
|
x = random.randint(0, 600-w)
|
|
y = random.randint(0, 400-h)
|
|
if self._shortest_dist(pos, (x, y)) > 70:
|
|
pos.append((x, y))
|
|
background.paste(letter, (x, y), letter)
|
|
placed = True
|
|
return background
|
|
|
|
def draw_image(self, game_id: str, misses: int, word: str,
|
|
guessed: list[bool], guessed_letters: str):
|
|
"""
|
|
Draw a hangman Image.
|
|
|
|
*Parameters*
|
|
------------
|
|
channel: str
|
|
The id of the channel the game is in.
|
|
"""
|
|
self._bot.log("Drawing hangman image")
|
|
|
|
random.seed(game_id)
|
|
|
|
background = Image.open("gwendolyn/resources/games/default_images/hangman.png")
|
|
gallow = self._draw_gallows()
|
|
man = self._draw_man(misses, game_id)
|
|
|
|
random.seed(game_id)
|
|
letter_line_parameters = [word, guessed, misses]
|
|
letter_lines = self._draw_letter_lines(*letter_line_parameters)
|
|
|
|
random.seed(game_id)
|
|
misses = self._draw_misses(list(guessed_letters), word)
|
|
|
|
background.paste(gallow, (100, 100), gallow)
|
|
background.paste(man, (300, 210), man)
|
|
background.paste(letter_lines, (120, 840), letter_lines)
|
|
background.paste(misses, (600, 150), misses)
|
|
|
|
misses_text = self._bad_text("MISSES", False)
|
|
misses_text_width = misses_text.size[0]
|
|
background.paste(misses_text, (850-misses_text_width//2, 50), misses_text)
|
|
|
|
boards_path = "gwendolyn/resources/games/hangman_boards/"
|
|
image_path = f"{boards_path}hangman_board{game_id}.png"
|
|
background.save(image_path)
|