✨ game stuff
This commit is contained in:
@ -52,26 +52,6 @@ class BlackjackCog(commands.Cog):
|
|||||||
"""Enter the game of blackjack with a bet."""
|
"""Enter the game of blackjack with a bet."""
|
||||||
await self.bot.games.blackjack.enter_game(ctx, bet)
|
await self.bot.games.blackjack.enter_game(ctx, bet)
|
||||||
|
|
||||||
@cog_ext.cog_subcommand(**params["blackjack_stand"])
|
|
||||||
async def blackjack_stand(self, ctx, hand=0):
|
|
||||||
"""Stand on your hand in blackjack."""
|
|
||||||
await self.bot.games.blackjack.stand(ctx, hand)
|
|
||||||
|
|
||||||
@cog_ext.cog_subcommand(**params["blackjack_hit"])
|
|
||||||
async def blackjack_hit(self, ctx, hand=0):
|
|
||||||
"""Hit on your hand in blackjack."""
|
|
||||||
await self.bot.games.blackjack.hit(ctx, hand)
|
|
||||||
|
|
||||||
@cog_ext.cog_subcommand(**params["blackjack_double"])
|
|
||||||
async def blackjack_double(self, ctx, hand=0):
|
|
||||||
"""Double in blackjack."""
|
|
||||||
await self.bot.games.blackjack.double(ctx, hand)
|
|
||||||
|
|
||||||
@cog_ext.cog_subcommand(**params["blackjack_split"])
|
|
||||||
async def blackjack_split(self, ctx, hand=0):
|
|
||||||
"""Split your hand in blackjack."""
|
|
||||||
await self.bot.games.blackjack.split(ctx, hand)
|
|
||||||
|
|
||||||
@cog_ext.cog_subcommand(**params["blackjack_hilo"])
|
@cog_ext.cog_subcommand(**params["blackjack_hilo"])
|
||||||
async def blackjack_hilo(self, ctx):
|
async def blackjack_hilo(self, ctx):
|
||||||
"""Get the hilo value for the deck in blackjack."""
|
"""Get the hilo value for the deck in blackjack."""
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Contains the MiscCog, which deals with miscellaneous commands."""
|
"""Contains the MiscCog, which deals with miscellaneous commands."""
|
||||||
from discord.ext import commands # Has the cog class
|
from discord.ext import commands # Has the cog class
|
||||||
from discord_slash import cog_ext # Used for slash commands
|
from discord_slash import cog_ext # Used for slash commands
|
||||||
|
from discord_slash.context import SlashContext
|
||||||
|
|
||||||
from gwendolyn.utils import get_params # pylint: disable=import-error
|
from gwendolyn.utils import get_params # pylint: disable=import-error
|
||||||
|
|
||||||
@ -19,77 +20,77 @@ class MiscCog(commands.Cog):
|
|||||||
self.nerd_shit = bot.other.nerd_shit
|
self.nerd_shit = bot.other.nerd_shit
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["ping"])
|
@cog_ext.cog_slash(**params["ping"])
|
||||||
async def ping(self, ctx):
|
async def ping(self, ctx: SlashContext):
|
||||||
"""Send the bot's latency."""
|
"""Send the bot's latency."""
|
||||||
await ctx.send(f"Pong!\nLatency is {round(self.bot.latency * 1000)}ms")
|
await ctx.send(f"Pong!\nLatency is {round(self.bot.latency * 1000)}ms")
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["stop"])
|
@cog_ext.cog_slash(**params["stop"])
|
||||||
async def stop(self, ctx):
|
async def stop(self, ctx: SlashContext):
|
||||||
"""Stop the bot."""
|
"""Stop the bot."""
|
||||||
await self.bot.stop(ctx)
|
await self.bot.stop(ctx)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["help"])
|
@cog_ext.cog_slash(**params["help"])
|
||||||
async def help_command(self, ctx, command=""):
|
async def help_command(self, ctx: SlashContext, command=""):
|
||||||
"""Get help for commands."""
|
"""Get help for commands."""
|
||||||
await self.bot.other.helpFunc(ctx, command)
|
await self.bot.other.helpFunc(ctx, command)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["thank"])
|
@cog_ext.cog_slash(**params["thank"])
|
||||||
async def thank(self, ctx):
|
async def thank(self, ctx: SlashContext):
|
||||||
"""Thank the bot."""
|
"""Thank the bot."""
|
||||||
await ctx.send("You're welcome :blush:")
|
await ctx.send("You're welcome :blush:")
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["hello"])
|
@cog_ext.cog_slash(**params["hello"])
|
||||||
async def hello(self, ctx):
|
async def hello(self, ctx: SlashContext):
|
||||||
"""Greet the bot."""
|
"""Greet the bot."""
|
||||||
await self.bot.other.helloFunc(ctx)
|
await self.bot.other.helloFunc(ctx)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["roll"])
|
@cog_ext.cog_slash(**params["roll"])
|
||||||
async def roll(self, ctx, dice="1d20"):
|
async def roll(self, ctx: SlashContext, dice="1d20"):
|
||||||
"""Roll dice."""
|
"""Roll dice."""
|
||||||
await self.bot.other.rollDice(ctx, dice)
|
await self.bot.other.rollDice(ctx, dice)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["image"])
|
@cog_ext.cog_slash(**params["image"])
|
||||||
async def image(self, ctx):
|
async def image(self, ctx: SlashContext):
|
||||||
"""Get a random image from Bing."""
|
"""Get a random image from Bing."""
|
||||||
await self.bot.other.imageFunc(ctx)
|
await self.bot.other.imageFunc(ctx)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["movie"])
|
@cog_ext.cog_slash(**params["movie"])
|
||||||
async def movie(self, ctx):
|
async def movie(self, ctx: SlashContext):
|
||||||
"""Get a random movie from the Plex server."""
|
"""Get a random movie from the Plex server."""
|
||||||
await self.bot.other.movieFunc(ctx)
|
await self.bot.other.movieFunc(ctx)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["name"])
|
@cog_ext.cog_slash(**params["name"])
|
||||||
async def name(self, ctx):
|
async def name(self, ctx: SlashContext):
|
||||||
"""Generate a random name."""
|
"""Generate a random name."""
|
||||||
await self.generators.nameGen(ctx)
|
await self.generators.nameGen(ctx)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["tavern"])
|
@cog_ext.cog_slash(**params["tavern"])
|
||||||
async def tavern(self, ctx):
|
async def tavern(self, ctx: SlashContext):
|
||||||
"""Generate a random tavern name."""
|
"""Generate a random tavern name."""
|
||||||
await self.generators.tavernGen(ctx)
|
await self.generators.tavernGen(ctx)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["wiki"])
|
@cog_ext.cog_slash(**params["wiki"])
|
||||||
async def wiki(self, ctx, wiki_page=""):
|
async def wiki(self, ctx: SlashContext, wiki_page=""):
|
||||||
"""Get a page on a fandom wiki."""
|
"""Get a page on a fandom wiki."""
|
||||||
await self.bot.other.findWikiPage(ctx, wiki_page)
|
await self.bot.other.findWikiPage(ctx, wiki_page)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["add_movie"])
|
@cog_ext.cog_slash(**params["add_movie"])
|
||||||
async def add_movie(self, ctx, movie):
|
async def add_movie(self, ctx: SlashContext, movie):
|
||||||
"""Search for a movie and add it to the Plex server."""
|
"""Search for a movie and add it to the Plex server."""
|
||||||
await self.plex.request_movie(ctx, movie)
|
await self.plex.request_movie(ctx, movie)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["add_show"])
|
@cog_ext.cog_slash(**params["add_show"])
|
||||||
async def add_show(self, ctx, show):
|
async def add_show(self, ctx: SlashContext, show):
|
||||||
"""Search for a show and add it to the Plex server."""
|
"""Search for a show and add it to the Plex server."""
|
||||||
await self.plex.request_show(ctx, show)
|
await self.plex.request_show(ctx, show)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["downloading"])
|
@cog_ext.cog_slash(**params["downloading"])
|
||||||
async def downloading(self, ctx, parameters="-d"):
|
async def downloading(self, ctx: SlashContext, parameters="-d"):
|
||||||
"""Get the current downloading torrents."""
|
"""Get the current downloading torrents."""
|
||||||
await self.plex.downloading(ctx, parameters)
|
await self.plex.downloading(ctx, parameters)
|
||||||
|
|
||||||
@cog_ext.cog_slash(**params["wolf"])
|
@cog_ext.cog_slash(**params["wolf"])
|
||||||
async def wolf(self, ctx, query):
|
async def wolf(self, ctx: SlashContext, query):
|
||||||
"""Perform a search on Wolfram Alpha."""
|
"""Perform a search on Wolfram Alpha."""
|
||||||
await self.nerd_shit.wolfSearch(ctx, query)
|
await self.nerd_shit.wolfSearch(ctx, query)
|
||||||
|
|
||||||
|
9
gwendolyn/exceptions.py
Normal file
9
gwendolyn/exceptions.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
class GameNotInDatabase(Exception):
|
||||||
|
def __init__(self, game: str, channel: str):
|
||||||
|
self.message = f"There is no {game} game in channel {channel}"
|
||||||
|
super().__init__(self.message)
|
||||||
|
|
||||||
|
class InvalidInteraction(Exception):
|
||||||
|
def __init__(self, custom_id: str, decoded: str):
|
||||||
|
self.message = f"{custom_id = }, {decoded = }"
|
||||||
|
super().__init__(self.message)
|
@ -12,7 +12,10 @@ import math # Used for flooring decimal numbers
|
|||||||
import datetime # Used to generate the game id
|
import datetime # Used to generate the game id
|
||||||
import asyncio # Used for sleeping
|
import asyncio # Used for sleeping
|
||||||
|
|
||||||
from discord_slash.context import SlashContext # Used for typehints
|
from discord_slash.context import InteractionContext as IntCont # Used for
|
||||||
|
# typehints
|
||||||
|
from discord_slash.context import ComponentContext
|
||||||
|
from discord.abc import Messageable
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from gwendolyn.utils import replace_multiple
|
from gwendolyn.utils import replace_multiple
|
||||||
@ -38,15 +41,10 @@ def _is_round_done(game: dict):
|
|||||||
round_done: bool
|
round_done: bool
|
||||||
Whether the round is done.
|
Whether the round is done.
|
||||||
"""
|
"""
|
||||||
round_done = True
|
return all(
|
||||||
|
hand["hit"] or hand["standing"]
|
||||||
for user_hands in game["user hands"].values():
|
for user_hands in game["user hands"].values() for hand in user_hands
|
||||||
for hand in user_hands:
|
)
|
||||||
if (not hand["hit"]) and (not hand["standing"]):
|
|
||||||
round_done = False
|
|
||||||
break
|
|
||||||
|
|
||||||
return round_done
|
|
||||||
|
|
||||||
class Blackjack(CardGame):
|
class Blackjack(CardGame):
|
||||||
"""
|
"""
|
||||||
@ -54,53 +52,51 @@ class Blackjack(CardGame):
|
|||||||
|
|
||||||
*Methods*
|
*Methods*
|
||||||
---------
|
---------
|
||||||
hit(ctx: SlashContext, hand_number: int = 0)
|
hit(ctx: IntCont, hand_number: int = 0)
|
||||||
double(ctx: SlashContext, hand_number: int = 0)
|
double(ctx: IntCont, hand_number: int = 0)
|
||||||
stand(ctx: SlashContext, hand_number: int = 0)
|
stand(ctx: IntCont, hand_number: int = 0)
|
||||||
split(ctx: SlashContext, hand_number: int = 0)
|
split(ctx: IntCont, hand_number: int = 0)
|
||||||
enter_game(ctx: SlashContext, bet: int)
|
enter_game(ctx: IntCont, bet: int)
|
||||||
start(ctx: SlashContext)
|
start(ctx: IntCont)
|
||||||
hilo(ctx: SlashContext)
|
hilo(ctx: IntCont)
|
||||||
shuffle(ctx: SlashContext)
|
shuffle(ctx: IntCont)
|
||||||
cards(ctx: SlashContext)
|
cards(ctx: IntCont)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
"""Initialize the class."""
|
"""Initialize the class."""
|
||||||
super().__init__(bot)
|
super().__init__(bot, "blackjack", DrawBlackjack(bot, self), 4)
|
||||||
self.decks_used = 4
|
default_buttons = ["Hit", "Stand", "Double", "Split"]
|
||||||
self.game_name = "blackjack"
|
self.default_buttons = [(i, [i, "0"], 1) for i in default_buttons]
|
||||||
self.draw = DrawBlackjack(bot, self)
|
|
||||||
|
|
||||||
async def _test_valid_command(self, game: dict, user: str,
|
async def _test_command(self, game: dict, user: str, command: str,
|
||||||
hand_number: int, ctx: SlashContext):
|
hand_number: int, ctx: IntCont): #pylint:disable=too-many-arguments
|
||||||
valid_command = False
|
valid_command = False
|
||||||
|
|
||||||
if game is None:
|
if user not in game["user hands"]:
|
||||||
self.bot.log("There's no game going on")
|
self.bot.log(f"They tried to {command} without being in the game")
|
||||||
await ctx.send("There's no game going on")
|
send_msg = f"You have to enter the game before you can {command}"
|
||||||
elif user not in game["user hands"]:
|
|
||||||
self.bot.log("They tried something without being in the game")
|
|
||||||
await ctx.send("You have to enter the game before you can hit")
|
|
||||||
|
|
||||||
elif len(game["user hands"][user]) > 1 and hand_number == 0:
|
|
||||||
self.bot.log("They didn't specify a hand")
|
|
||||||
await ctx.send("You need to specify a hand")
|
|
||||||
elif len(game["user hands"][user]) < hand_number:
|
elif len(game["user hands"][user]) < hand_number:
|
||||||
self.bot.log("They tried something with a hand they don't have")
|
self.bot.log(f"They tried to {command} with a hand they don't have")
|
||||||
await ctx.send("You don't have that many hands")
|
send_msg = "You don't have that many hands"
|
||||||
elif game["round"] <= 0:
|
elif game["round"] <= 0:
|
||||||
self.bot.log("They tried to do something on the 0th round")
|
self.bot.log(f"They tried to {command} on the 0th round")
|
||||||
await ctx.send("You haven't seen your cards yet!")
|
send_msg = "You haven't seen your cards yet!"
|
||||||
|
elif len(game["user hands"][user]) > 1 and hand_number == 0:
|
||||||
|
self._get_hand_number(ctx, command, game["user hands"][user])
|
||||||
|
return False
|
||||||
elif game["user hands"][user][max(hand_number-1,0)]["hit"]:
|
elif game["user hands"][user][max(hand_number-1,0)]["hit"]:
|
||||||
self.bot.log("They've already hit this round")
|
self.bot.log("They've already hit this round")
|
||||||
await ctx.send("You've already hit this round")
|
send_msg = "You've already hit this round"
|
||||||
elif game["user hands"][user][max(hand_number-1,0)]["standing"]:
|
elif game["user hands"][user][max(hand_number-1,0)]["standing"]:
|
||||||
self.bot.log("They're already standing")
|
self.bot.log("They're already standing")
|
||||||
await ctx.send("You're already standing")
|
send_msg = "You're already standing"
|
||||||
else:
|
else:
|
||||||
valid_command = True
|
valid_command = True
|
||||||
|
|
||||||
|
if not valid_command:
|
||||||
|
await ctx.send(send_msg, hidden=True)
|
||||||
|
|
||||||
return valid_command
|
return valid_command
|
||||||
|
|
||||||
def _shuffle_cards(self, channel: str):
|
def _shuffle_cards(self, channel: str):
|
||||||
@ -158,12 +154,8 @@ class Blackjack(CardGame):
|
|||||||
|
|
||||||
def _draw_card(self, channel: str):
|
def _draw_card(self, channel: str):
|
||||||
drawn_card = super()._draw_card(channel)
|
drawn_card = super()._draw_card(channel)
|
||||||
value = self._calc_hand_value([drawn_card])
|
hilo_value = min(1,-1-((self._calc_hand_value([drawn_card])-10)//3))
|
||||||
|
self._update_document(channel, {"$inc": {"hilo": hilo_value}}, "hilo")
|
||||||
if value <= 6:
|
|
||||||
self._update_document(channel, {"$inc": {"hilo": 1}}, "hilo")
|
|
||||||
elif value >= 10:
|
|
||||||
self._update_document(channel, {"$inc": {"hilo": -1}}, "hilo")
|
|
||||||
|
|
||||||
return drawn_card
|
return drawn_card
|
||||||
|
|
||||||
@ -182,14 +174,13 @@ class Blackjack(CardGame):
|
|||||||
Whether the dealer is done drawing cards.
|
Whether the dealer is done drawing cards.
|
||||||
"""
|
"""
|
||||||
game = self.access_document(channel)
|
game = self.access_document(channel)
|
||||||
|
|
||||||
done = False
|
|
||||||
dealer_hand = game["dealer hand"]
|
dealer_hand = game["dealer hand"]
|
||||||
|
|
||||||
if self._calc_hand_value(dealer_hand) < 17:
|
if self._calc_hand_value(dealer_hand) < 17:
|
||||||
dealer_hand.append(self._draw_card(channel))
|
dealer_hand.append(self._draw_card(channel))
|
||||||
dealer_updater = {"$set": {"dealer hand": dealer_hand}}
|
dealer_updater = {"$set": {"dealer hand": dealer_hand}}
|
||||||
self._update_document(channel, dealer_updater)
|
self._update_document(channel, dealer_updater)
|
||||||
|
done = False
|
||||||
else:
|
else:
|
||||||
done = True
|
done = True
|
||||||
|
|
||||||
@ -219,31 +210,31 @@ class Blackjack(CardGame):
|
|||||||
self.bot.log("Continuing blackjack game")
|
self.bot.log("Continuing blackjack game")
|
||||||
game = self.access_document(channel)
|
game = self.access_document(channel)
|
||||||
|
|
||||||
done = False
|
|
||||||
|
|
||||||
self._update_document(channel, {"$inc": {"round": 1}})
|
self._update_document(channel, {"$inc": {"round": 1}})
|
||||||
|
message = self.long_strings["Blackjack all players standing"]
|
||||||
all_standing = True
|
all_standing = True
|
||||||
pre_all_standing = True
|
pre_all_standing = True
|
||||||
message = self.long_strings["Blackjack all players standing"]
|
done = False
|
||||||
|
|
||||||
if game["all standing"]:
|
if game["all standing"]:
|
||||||
self.bot.log("All are standing")
|
self.bot.log("All are standing")
|
||||||
|
|
||||||
done = self._dealer_draw(channel)
|
done = self._dealer_draw(channel)
|
||||||
message = "The dealer draws a card."
|
message = "The dealer draws a card."
|
||||||
|
|
||||||
self.bot.log("Testing if all are standing")
|
self.bot.log("Testing if all are standing")
|
||||||
for user, user_hands in game["user hands"].items():
|
for user, user_hands in game["user hands"].items():
|
||||||
standing_test = (self._test_if_standing(user_hands))
|
standing_test = (self._test_if_standing(user_hands))
|
||||||
new_hands, all_standing, pre_all_standing = standing_test
|
hand_updater = {"$set": {"user hands."+user: standing_test[0]}}
|
||||||
hand_updater = {"$set": {"user hands."+user: new_hands}}
|
|
||||||
self._update_document(channel, hand_updater)
|
self._update_document(channel, hand_updater)
|
||||||
|
if not standing_test[1]:
|
||||||
|
all_standing = False
|
||||||
|
if not standing_test[2]:
|
||||||
|
pre_all_standing = False
|
||||||
|
|
||||||
|
|
||||||
if all_standing:
|
if all_standing:
|
||||||
self._update_document(channel, {"$set": {"all standing": True}})
|
self._update_document(channel, {"$set": {"all standing": True}})
|
||||||
|
|
||||||
if all_standing:
|
|
||||||
if done:
|
if done:
|
||||||
message = "The dealer is done drawing cards"
|
message = "The dealer is done drawing cards"
|
||||||
|
|
||||||
@ -304,7 +295,7 @@ class Blackjack(CardGame):
|
|||||||
|
|
||||||
return user_hands, all_standing, pre_all_standing
|
return user_hands, all_standing, pre_all_standing
|
||||||
|
|
||||||
def _blackjack_finish(self, channel: str):
|
def _blackjack_finish(self, channel: Messageable):
|
||||||
"""
|
"""
|
||||||
Generate the winnings message after the blackjack game ends.
|
Generate the winnings message after the blackjack game ends.
|
||||||
|
|
||||||
@ -320,7 +311,7 @@ class Blackjack(CardGame):
|
|||||||
"""
|
"""
|
||||||
final_winnings = "*Final Winnings:*\n"
|
final_winnings = "*Final Winnings:*\n"
|
||||||
|
|
||||||
game = self.access_document(channel)
|
game = self.access_document(str(channel.id))
|
||||||
|
|
||||||
for user in game["user hands"]:
|
for user in game["user hands"]:
|
||||||
winnings, net_winnings, reason = self._calc_winning(
|
winnings, net_winnings, reason = self._calc_winning(
|
||||||
@ -333,7 +324,7 @@ class Blackjack(CardGame):
|
|||||||
user_name = self.bot.database_funcs.get_name(user)
|
user_name = self.bot.database_funcs.get_name(user)
|
||||||
|
|
||||||
if winnings < 0:
|
if winnings < 0:
|
||||||
final_winnings += "{user_name} lost"
|
final_winnings += f"{user_name} lost"
|
||||||
else:
|
else:
|
||||||
final_winnings += f"{user_name} won"
|
final_winnings += f"{user_name} won"
|
||||||
|
|
||||||
@ -342,11 +333,11 @@ class Blackjack(CardGame):
|
|||||||
else:
|
else:
|
||||||
final_winnings += f" {abs(winnings)} GwendoBucks"
|
final_winnings += f" {abs(winnings)} GwendoBucks"
|
||||||
|
|
||||||
winnings_text += f" {reason}\n"
|
final_winnings += f" {reason}\n"
|
||||||
|
|
||||||
self.bot.money.addMoney(user, net_winnings)
|
self.bot.money.addMoney(user, net_winnings)
|
||||||
|
|
||||||
self._delete_document(channel)
|
await self._end_game(channel)
|
||||||
|
|
||||||
return final_winnings
|
return final_winnings
|
||||||
|
|
||||||
@ -386,32 +377,27 @@ class Blackjack(CardGame):
|
|||||||
net_winnings = 0
|
net_winnings = 0
|
||||||
|
|
||||||
for hand in user_hands:
|
for hand in user_hands:
|
||||||
bet = hand["bet"]
|
winnings += -1 * hand["bet"]
|
||||||
winnings += -1 * bet
|
hand_value = self._calc_hand_value(hand["hand"])
|
||||||
|
|
||||||
if hand["blackjack"] and not dealer_blackjack:
|
states = [
|
||||||
reason += "\n(blackjack)"
|
(dealer_blackjack, "dealer blackjack", 0),
|
||||||
net_winnings += math.floor(2.5 * bet)
|
(hand["blackjack"], "blackjack", math.floor(2.5 * hand["bet"])),
|
||||||
elif dealer_blackjack:
|
(hand["busted"], "busted", 0),
|
||||||
reason += "\n(dealer blackjack)"
|
(dealer_busted, dealer_busted, 2*hand["bet"]),
|
||||||
elif hand["busted"]:
|
(hand_value == dealer_value, "pushed", hand["bet"]),
|
||||||
reason += "\n(busted)"
|
(hand_value > dealer_value, "highest value", 2 * hand["bet"]),
|
||||||
else:
|
(True, "highest value", 0)
|
||||||
hand_value = self._calc_hand_value(hand["hand"])
|
]
|
||||||
if dealer_busted:
|
|
||||||
reason += "\n(dealer busted)"
|
for state in states:
|
||||||
winnings += 2 * bet
|
if state[0]:
|
||||||
elif hand_value > dealer_value:
|
reason += f"({state[1]})"
|
||||||
winnings += 2 * bet
|
net_winnings += state[2]
|
||||||
reason += "\n(highest value)"
|
break
|
||||||
elif hand_value == dealer_value:
|
|
||||||
reason += "\n(pushed)"
|
|
||||||
winnings += bet
|
|
||||||
else:
|
|
||||||
reason += "\n(highest value)"
|
|
||||||
|
|
||||||
winnings += net_winnings
|
winnings += net_winnings
|
||||||
return winnings, net_winnings, reason[1:]
|
return winnings, net_winnings, reason
|
||||||
|
|
||||||
async def _blackjack_loop(self, channel, game_round: int, game_id: str):
|
async def _blackjack_loop(self, channel, game_round: int, game_id: str):
|
||||||
"""
|
"""
|
||||||
@ -437,22 +423,17 @@ class Blackjack(CardGame):
|
|||||||
|
|
||||||
if not game_done:
|
if not game_done:
|
||||||
await self._delete_old_image(channel)
|
await self._delete_old_image(channel)
|
||||||
await self._send_image(channel)
|
buttons = None if all_standing else self.default_buttons
|
||||||
|
await self._send_image(channel, buttons)
|
||||||
|
|
||||||
if all_standing:
|
await asyncio.sleep([120,5][all_standing])
|
||||||
await asyncio.sleep(5)
|
|
||||||
else:
|
|
||||||
await asyncio.sleep(120)
|
|
||||||
|
|
||||||
game = self.access_document(str(channel.id))
|
if not self._test_document(str(channel.id)):
|
||||||
|
|
||||||
if game is None:
|
|
||||||
self.bot.log(f"Ending loop on round {game_round}", str(channel.id))
|
self.bot.log(f"Ending loop on round {game_round}", str(channel.id))
|
||||||
return
|
return
|
||||||
|
|
||||||
real_round = game["round"] or -1
|
game = self.access_document(str(channel.id))
|
||||||
real_id = game["game_id"] or -1
|
if game_round != game["round"] or game_id != game["game_id"]:
|
||||||
if game_round != real_round or game_id != real_id:
|
|
||||||
self.bot.log(f"Ending loop on round {game_round}", str(channel.id))
|
self.bot.log(f"Ending loop on round {game_round}", str(channel.id))
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -461,17 +442,26 @@ class Blackjack(CardGame):
|
|||||||
self.bot.log(log_message, str(channel.id))
|
self.bot.log(log_message, str(channel.id))
|
||||||
await self._blackjack_loop(channel, game_round+1, game_id)
|
await self._blackjack_loop(channel, game_round+1, game_id)
|
||||||
else:
|
else:
|
||||||
new_message = self._blackjack_finish(str(channel.id))
|
await channel.send(self._blackjack_finish(channel))
|
||||||
await channel.send(new_message)
|
|
||||||
|
|
||||||
|
async def _get_hand_number(self, ctx: IntCont, command: str,
|
||||||
|
hands_amount: int):
|
||||||
|
buttons = [
|
||||||
|
(str(i+1), [command, "0", str(i+1)], 1) for i in range(hands_amount)
|
||||||
|
]
|
||||||
|
await ctx.send(
|
||||||
|
f"Which hand do you want to {command}?",
|
||||||
|
hidden=True,
|
||||||
|
components=self._get_action_rows(buttons)
|
||||||
|
)
|
||||||
|
|
||||||
async def hit(self, ctx: SlashContext, hand_number: int = 0):
|
async def _hit(self, ctx: IntCont, hand_number: int = 0):
|
||||||
"""
|
"""
|
||||||
Hit on a hand.
|
Hit on a hand.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
ctx: SlashContext
|
ctx: IntCont
|
||||||
The context of the command.
|
The context of the command.
|
||||||
hand_number: int = 1
|
hand_number: int = 1
|
||||||
The number of the hand to hit.
|
The number of the hand to hit.
|
||||||
@ -481,10 +471,7 @@ class Blackjack(CardGame):
|
|||||||
user = f"#{ctx.author.id}"
|
user = f"#{ctx.author.id}"
|
||||||
|
|
||||||
game = self.access_document(channel)
|
game = self.access_document(channel)
|
||||||
|
if not await self._test_command(game, user, "hit", hand_number, ctx):
|
||||||
valid_command = self._test_valid_command(game, user, hand_number, ctx)
|
|
||||||
|
|
||||||
if not valid_command:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
hand = game["user hands"][user][max(hand_number-1,0)]
|
hand = game["user hands"][user][max(hand_number-1,0)]
|
||||||
@ -511,13 +498,13 @@ class Blackjack(CardGame):
|
|||||||
ctx.channel, game["round"]+1, game_id
|
ctx.channel, game["round"]+1, game_id
|
||||||
)
|
)
|
||||||
|
|
||||||
async def double(self, ctx: SlashContext, hand_number: int = 0):
|
async def _double(self, ctx: IntCont, hand_number: int = 0):
|
||||||
"""
|
"""
|
||||||
Double a hand.
|
Double a hand.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
ctx: SlashContext
|
ctx: IntCont
|
||||||
The context of the command.
|
The context of the command.
|
||||||
hand_number: int = 0
|
hand_number: int = 0
|
||||||
The number of the hand to double.
|
The number of the hand to double.
|
||||||
@ -530,17 +517,15 @@ class Blackjack(CardGame):
|
|||||||
|
|
||||||
balance = self.bot.money.checkBalance(user)
|
balance = self.bot.money.checkBalance(user)
|
||||||
|
|
||||||
valid_command = self._test_valid_command(game, user, hand_number, ctx)
|
if not await self._test_command(game, user, "double", hand_number, ctx):
|
||||||
|
|
||||||
if not valid_command:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(game["user hands"][user][max(hand_number-1,0)]["hand"]) != 2:
|
if len(game["user hands"][user][max(hand_number-1,0)]["hand"]) != 2:
|
||||||
await ctx.send("They tried to double after round 1")
|
await ctx.send("You can only double on the first round")
|
||||||
self.bot.log("You can only double on the first round")
|
self.bot.log("They tried to double after round 1")
|
||||||
elif balance < game["user hands"][user][max(hand_number-1,0)]["bet"]:
|
elif balance < game["user hands"][user][max(hand_number-1,0)]["bet"]:
|
||||||
await ctx.send("They tried to double without having enough money")
|
await ctx.send("You can't double when you don't have enough money")
|
||||||
self.bot.log("You can't double when you don't have enough money")
|
self.bot.log("They tried to double without having enough money")
|
||||||
else:
|
else:
|
||||||
hand = game["user hands"][user][max(hand_number-1,0)]
|
hand = game["user hands"][user][max(hand_number-1,0)]
|
||||||
self.bot.money.addMoney(user, -1 * hand["bet"])
|
self.bot.money.addMoney(user, -1 * hand["bet"])
|
||||||
@ -573,13 +558,13 @@ class Blackjack(CardGame):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def stand(self, ctx: SlashContext, hand_number: int = 0):
|
async def _stand(self, ctx: IntCont, hand_number: int = 0):
|
||||||
"""
|
"""
|
||||||
Stand on a hand.
|
Stand on a hand.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
ctx: SlashContext
|
ctx: IntCont
|
||||||
The context of the command.
|
The context of the command.
|
||||||
hand_number: int = 0
|
hand_number: int = 0
|
||||||
The number of the hand to stand on.
|
The number of the hand to stand on.
|
||||||
@ -590,9 +575,7 @@ class Blackjack(CardGame):
|
|||||||
|
|
||||||
game = self.access_document(channel)
|
game = self.access_document(channel)
|
||||||
|
|
||||||
valid_command = self._test_valid_command(game, user, hand_number, ctx)
|
if not await self._test_command(game, user, "stand", hand_number, ctx):
|
||||||
|
|
||||||
if not valid_command:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
hand = game["user hands"][user][max(hand_number-1,0)]
|
hand = game["user hands"][user][max(hand_number-1,0)]
|
||||||
@ -613,13 +596,13 @@ class Blackjack(CardGame):
|
|||||||
await self._blackjack_loop(ctx.channel, game["round"]+1, game_id)
|
await self._blackjack_loop(ctx.channel, game["round"]+1, game_id)
|
||||||
|
|
||||||
|
|
||||||
async def split(self, ctx: SlashContext, hand_number: int = 0):
|
async def _split(self, ctx: IntCont, hand_number: int = 0):
|
||||||
"""
|
"""
|
||||||
Split a hand.
|
Split a hand.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
ctx: SlashContext
|
ctx: IntCont
|
||||||
The context of the command.
|
The context of the command.
|
||||||
hand_number: int = 0
|
hand_number: int = 0
|
||||||
The number of the hand to split.
|
The number of the hand to split.
|
||||||
@ -630,10 +613,7 @@ class Blackjack(CardGame):
|
|||||||
|
|
||||||
game = self.access_document(channel)
|
game = self.access_document(channel)
|
||||||
|
|
||||||
|
if not await self._test_command(game, user, "split", hand_number, ctx):
|
||||||
valid_command = self._test_valid_command(game, user, hand_number, ctx)
|
|
||||||
|
|
||||||
if not valid_command:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
old_hand = game["user hands"][user][max(hand_number-1,0)]
|
old_hand = game["user hands"][user][max(hand_number-1,0)]
|
||||||
@ -644,7 +624,7 @@ class Blackjack(CardGame):
|
|||||||
elif len(old_hand["hand"]) != 2:
|
elif len(old_hand["hand"]) != 2:
|
||||||
await ctx.send("You can only split on the first round")
|
await ctx.send("You can only split on the first round")
|
||||||
self.bot.log("They tried to split after the first round")
|
self.bot.log("They tried to split after the first round")
|
||||||
elif len({self._calc_hand_value([i]) for i in old_hand["hand"]}) == 1:
|
elif len({self._calc_hand_value([i]) for i in old_hand["hand"]}) != 1:
|
||||||
await ctx.send(self.long_strings["Blackjack different cards"])
|
await ctx.send(self.long_strings["Blackjack different cards"])
|
||||||
self.bot.log("They tried to split two different cards")
|
self.bot.log("They tried to split two different cards")
|
||||||
elif self.bot.money.checkBalance(user) < old_hand["bet"]:
|
elif self.bot.money.checkBalance(user) < old_hand["bet"]:
|
||||||
@ -689,13 +669,13 @@ class Blackjack(CardGame):
|
|||||||
ctx.channel, game["round"]+1, game_id
|
ctx.channel, game["round"]+1, game_id
|
||||||
)
|
)
|
||||||
|
|
||||||
async def enter_game(self, ctx: SlashContext, bet: int):
|
async def enter_game(self, ctx: IntCont, bet: int):
|
||||||
"""
|
"""
|
||||||
Enter the blackjack game.
|
Enter the blackjack game.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
ctx: SlashContext
|
ctx: IntCont
|
||||||
The context of the command.
|
The context of the command.
|
||||||
bet: int
|
bet: int
|
||||||
The bet to enter with.
|
The bet to enter with.
|
||||||
@ -709,24 +689,21 @@ class Blackjack(CardGame):
|
|||||||
|
|
||||||
self.bot.log(f"{user_name} is trying to join the Blackjack game")
|
self.bot.log(f"{user_name} is trying to join the Blackjack game")
|
||||||
|
|
||||||
if game is None:
|
if user in game["user hands"]:
|
||||||
send_message = "There is no game going on in this channel"
|
await ctx.send("You're already in the game!")
|
||||||
log_message = send_message
|
self.bot.log("They're already in the game")
|
||||||
elif user in game["user hands"]:
|
|
||||||
send_message = "You're already in the game!"
|
|
||||||
log_message = "They're already in the game"
|
|
||||||
elif len(game["user hands"]) >= 5:
|
elif len(game["user hands"]) >= 5:
|
||||||
send_message = "There can't be more than 5 players in a game"
|
await ctx.send("There can't be more than 5 players in a game")
|
||||||
log_message = "There were already 5 players in the game"
|
self.bot.log("There were already 5 players in the game")
|
||||||
elif game["round"] != 0:
|
elif game["round"] != 0:
|
||||||
send_message = "The table is no longer taking bets"
|
await ctx.send("The table is no longer taking bets")
|
||||||
log_message = "They tried to join after the game begun"
|
self.bot.log("They tried to join after the game begun")
|
||||||
elif bet < 0:
|
elif bet < 0:
|
||||||
send_message = "You can't bet a negative amount"
|
await ctx.send("You can't bet a negative amount")
|
||||||
log_message = "They tried to bet a negative amount"
|
self.bot.log("They tried to bet a negative amount")
|
||||||
elif self.bot.money.checkBalance(user) < bet:
|
elif self.bot.money.checkBalance(user) < bet:
|
||||||
send_message = "You don't have enough GwendoBucks"
|
await ctx.send("You don't have enough GwendoBucks")
|
||||||
log_message = "They didn't have enough GwendoBucks"
|
self.bot.log("They didn't have enough GwendoBucks")
|
||||||
else:
|
else:
|
||||||
self.bot.money.addMoney(user, -1 * bet)
|
self.bot.money.addMoney(user, -1 * bet)
|
||||||
player_hand = [self._draw_card(channel) for _ in range(2)]
|
player_hand = [self._draw_card(channel) for _ in range(2)]
|
||||||
@ -742,19 +719,16 @@ class Blackjack(CardGame):
|
|||||||
self._update_document(channel, updater)
|
self._update_document(channel, updater)
|
||||||
|
|
||||||
send_message = "{} entered the game with a bet of {} GwendoBucks"
|
send_message = "{} entered the game with a bet of {} GwendoBucks"
|
||||||
send_message = send_message.format(user_name, bet)
|
await ctx.send(send_message.format(user_name, bet))
|
||||||
log_message = send_message
|
self.bot.log(send_message.format(user_name, bet))
|
||||||
|
|
||||||
self.bot.log(log_message)
|
async def start(self, ctx: IntCont):
|
||||||
await ctx.send(send_message)
|
|
||||||
|
|
||||||
async def start(self, ctx: SlashContext):
|
|
||||||
"""
|
"""
|
||||||
Start a blackjack game.
|
Start a blackjack game.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
ctx: SlashContext
|
ctx: IntCont
|
||||||
The context of the command.
|
The context of the command.
|
||||||
"""
|
"""
|
||||||
await self.bot.defer(ctx)
|
await self.bot.defer(ctx)
|
||||||
@ -764,21 +738,18 @@ class Blackjack(CardGame):
|
|||||||
self.bot.log("Starting blackjack game")
|
self.bot.log("Starting blackjack game")
|
||||||
await ctx.send("Starting a new game of blackjack")
|
await ctx.send("Starting a new game of blackjack")
|
||||||
cards_left = 0
|
cards_left = 0
|
||||||
cards = self.access_document(channel, "cards")
|
if self._test_document(channel, "cards"):
|
||||||
if cards is not None:
|
cards = self.access_document(channel, "cards")
|
||||||
cards_left = len(cards["cards"])
|
cards_left = len(cards["cards"])
|
||||||
|
|
||||||
# Shuffles if not enough cards
|
# Shuffles if not enough cards
|
||||||
if cards_left < blackjack_min_cards:
|
if cards_left < blackjack_min_cards:
|
||||||
self._shuffle_cards(channel)
|
self._shuffle_cards(channel)
|
||||||
self.bot.log("Shuffling the blackjack deck...", channel)
|
|
||||||
await ctx.channel.send("Shuffling the deck...")
|
await ctx.channel.send("Shuffling the deck...")
|
||||||
|
|
||||||
game = self.access_document(channel)
|
|
||||||
|
|
||||||
self.bot.log(f"Trying to start a blackjack game in {channel}")
|
self.bot.log(f"Trying to start a blackjack game in {channel}")
|
||||||
|
|
||||||
if game is not None:
|
if self._test_document(channel):
|
||||||
await ctx.channel.send(self.long_strings["Blackjack going on"])
|
await ctx.channel.send(self.long_strings["Blackjack going on"])
|
||||||
self.bot.log("There was already a game going on")
|
self.bot.log("There was already a game going on")
|
||||||
return
|
return
|
||||||
@ -794,14 +765,18 @@ class Blackjack(CardGame):
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ctx.channel.send(self.long_strings["Blackjack started"])
|
await ctx.channel.send(self.long_strings["Blackjack started"])
|
||||||
await self._start_new(ctx.channel, new_game)
|
labels = [0] + [10**i for i in range(4)]
|
||||||
|
betting_buttons = [
|
||||||
|
(f"Bet {i}", ["bet", "0", str(i)], 1) for i in labels
|
||||||
|
]
|
||||||
|
await self._start_new(ctx.channel, new_game, betting_buttons)
|
||||||
|
|
||||||
await asyncio.sleep(30)
|
await asyncio.sleep(30)
|
||||||
game = self.access_document(channel)
|
game = self.access_document(channel)
|
||||||
|
|
||||||
if len(game["user hands"]) == 0:
|
if len(game["user hands"]) == 0:
|
||||||
await ctx.channel.send("No one entered the game. Ending the game.")
|
await ctx.channel.send("No one entered the game. Ending the game.")
|
||||||
await ctx.channel.send(self._blackjack_finish(channel))
|
await ctx.channel.send(self._blackjack_finish(ctx.channel))
|
||||||
return
|
return
|
||||||
|
|
||||||
game_id = game["game_id"]
|
game_id = game["game_id"]
|
||||||
@ -810,49 +785,60 @@ class Blackjack(CardGame):
|
|||||||
self.bot.log("start() calling _blackjack_loop()", channel)
|
self.bot.log("start() calling _blackjack_loop()", channel)
|
||||||
await self._blackjack_loop(ctx.channel, 1, game_id)
|
await self._blackjack_loop(ctx.channel, 1, game_id)
|
||||||
|
|
||||||
async def hilo(self, ctx: SlashContext):
|
async def hilo(self, ctx: IntCont):
|
||||||
"""
|
"""
|
||||||
Get the hilo of the blackjack game.
|
Get the hilo of the blackjack game.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
ctx: SlashContext
|
ctx: IntCont
|
||||||
The context of the command.
|
The context of the command.
|
||||||
"""
|
"""
|
||||||
data = self.access_document(str(ctx.channel_id), "hilo")
|
data = self.access_document(str(ctx.channel_id), "hilo", False)
|
||||||
hilo = data["hilo"] if data else 0
|
hilo = data["hilo"] if data else 0
|
||||||
|
|
||||||
await ctx.send(f"Hi-lo value: {hilo}", hidden=True)
|
await ctx.send(f"Hi-lo value: {hilo}", hidden=True)
|
||||||
|
|
||||||
async def shuffle(self, ctx: SlashContext):
|
async def shuffle(self, ctx: IntCont):
|
||||||
"""
|
"""
|
||||||
Shuffle the cards used for blackjack.
|
Shuffle the cards used for blackjack.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
ctx: SlashContext
|
ctx: IntCont
|
||||||
The context of the command.
|
The context of the command.
|
||||||
"""
|
"""
|
||||||
self._shuffle_cards(str(ctx.channel_id))
|
self._shuffle_cards(str(ctx.channel_id))
|
||||||
self.bot.log("Shuffling the blackjack deck...", str(ctx.channel_id))
|
|
||||||
await ctx.send("Shuffling the deck...")
|
await ctx.send("Shuffling the deck...")
|
||||||
|
|
||||||
async def cards(self, ctx: SlashContext):
|
async def cards(self, ctx: IntCont):
|
||||||
"""
|
"""
|
||||||
Get how many cards are left for blackjack.
|
Get how many cards are left for blackjack.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
ctx: SlashContext
|
ctx: IntCont
|
||||||
The context of the command.
|
The context of the command.
|
||||||
"""
|
"""
|
||||||
cards = self.access_document(str(ctx.channel_id), "cards")
|
cards = self.access_document(str(ctx.channel_id), "cards", False)
|
||||||
cards_left = len(cards["cards"]) if cards else 0
|
cards_left = len(cards["cards"]) if cards else 0
|
||||||
decks_left = round(cards_left/52, 1)
|
decks_left = round(cards_left/52, 1) if cards else 0
|
||||||
|
|
||||||
send_message = f"Cards left:\n{cards_left} cards, {decks_left} decks"
|
send_message = f"Cards left:\n{cards_left} cards, {decks_left} decks"
|
||||||
await ctx.send(send_message, hidden=True)
|
await ctx.send(send_message, hidden=True)
|
||||||
|
|
||||||
|
async def decode_interaction(self, ctx: ComponentContext, info: list[str]):
|
||||||
|
if info[0].lower() == "bet":
|
||||||
|
await self.enter_game(ctx, int(info[2]))
|
||||||
|
elif info[0].lower() == "hit":
|
||||||
|
await self._hit(ctx, int(info[2]) if len(info) > 2 else 0)
|
||||||
|
elif info[0].lower() == "stand":
|
||||||
|
await self._stand(ctx, int(info[2]) if len(info) > 2 else 0)
|
||||||
|
elif info[0].lower() == "double":
|
||||||
|
await self._double(ctx, int(info[2]) if len(info) > 2 else 0)
|
||||||
|
elif info[0].lower() == "split":
|
||||||
|
await self._split(ctx, int(info[2]) if len(info) > 2 else 0)
|
||||||
|
|
||||||
|
|
||||||
class DrawBlackjack(CardDrawer):
|
class DrawBlackjack(CardDrawer):
|
||||||
"""
|
"""
|
||||||
@ -882,7 +868,7 @@ class DrawBlackjack(CardDrawer):
|
|||||||
def _draw_dealer_hand(self, game: dict, table: Image.Image):
|
def _draw_dealer_hand(self, game: dict, table: Image.Image):
|
||||||
dealer_hand = self._draw_hand(
|
dealer_hand = self._draw_hand(
|
||||||
game["dealer hand"],
|
game["dealer hand"],
|
||||||
game["all standing"],
|
not game["all standing"],
|
||||||
game["dealer busted"] if game["all standing"] else False,
|
game["dealer busted"] if game["all standing"] else False,
|
||||||
game["dealer blackjack"] if game["all standing"] else False
|
game["dealer blackjack"] if game["all standing"] else False
|
||||||
)
|
)
|
||||||
@ -972,10 +958,9 @@ class DrawBlackjack(CardDrawer):
|
|||||||
|
|
||||||
colors = [BLACK, WHITE, last_color]
|
colors = [BLACK, WHITE, last_color]
|
||||||
text_image = self._draw_shadow_text(text, colors, font_size)
|
text_image = self._draw_shadow_text(text, colors, font_size)
|
||||||
background.paste(text_image, text_position, text_image)
|
|
||||||
|
|
||||||
width = background.size[0]
|
width = background.size[0]
|
||||||
text_width = self._get_font(font_size).getsize(text)[0]
|
text_width = self._get_font(font_size).getsize(text)[0]
|
||||||
text_position = (int(width/2)-int(text_width/2), 85)
|
text_position = (int(width/2)-int(text_width/2), 85)
|
||||||
|
background.paste(text_image, text_position, text_image)
|
||||||
|
|
||||||
return background
|
return background
|
||||||
|
@ -22,12 +22,46 @@ create_actionrow)
|
|||||||
from discord_slash.model import ButtonStyle
|
from discord_slash.model import ButtonStyle
|
||||||
|
|
||||||
from gwendolyn.utils import encode_id
|
from gwendolyn.utils import encode_id
|
||||||
|
from .game_base import BoardGame
|
||||||
|
|
||||||
ROWCOUNT = 6
|
ROWCOUNT = 6
|
||||||
COLUMNCOUNT = 7
|
COLUMNCOUNT = 7
|
||||||
|
|
||||||
|
def _encode_board_string(board: list):
|
||||||
|
string = [str(i) for row in board for i in row]
|
||||||
|
|
||||||
class ConnectFour():
|
while len(string) > 0 and string[0] == "0":
|
||||||
|
string = string[1:]
|
||||||
|
|
||||||
|
if string == "":
|
||||||
|
string = "0"
|
||||||
|
|
||||||
|
dec = 0
|
||||||
|
for i, digit in enumerate(string[::-1]):
|
||||||
|
dec += (3**i)*int(digit)
|
||||||
|
|
||||||
|
return str(dec)
|
||||||
|
|
||||||
|
def _decode_board_string(board_string: str):
|
||||||
|
dec = int(board_string)
|
||||||
|
string = []
|
||||||
|
while dec:
|
||||||
|
string.append(str(dec % 3))
|
||||||
|
dec = dec // 3
|
||||||
|
|
||||||
|
while len(string) < ROWCOUNT * COLUMNCOUNT:
|
||||||
|
string.append("0")
|
||||||
|
|
||||||
|
string = string[::-1]
|
||||||
|
|
||||||
|
board = [
|
||||||
|
[int(x) for x in string[i*COLUMNCOUNT:i*COLUMNCOUNT+COLUMNCOUNT]]
|
||||||
|
for i in range(ROWCOUNT)]
|
||||||
|
|
||||||
|
return board
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectFour(BoardGame):
|
||||||
"""
|
"""
|
||||||
Deals with connect four commands and logic.
|
Deals with connect four commands and logic.
|
||||||
|
|
||||||
@ -41,8 +75,7 @@ class ConnectFour():
|
|||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
"""Initialize the class."""
|
"""Initialize the class."""
|
||||||
self.bot = bot
|
super().__init__(bot, "connectfour", DrawConnectFour(bot))
|
||||||
self.draw = DrawConnectFour(bot)
|
|
||||||
self.get_name = self.bot.database_funcs.get_name
|
self.get_name = self.bot.database_funcs.get_name
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
self.AISCORES = {
|
self.AISCORES = {
|
||||||
@ -57,39 +90,6 @@ class ConnectFour():
|
|||||||
}
|
}
|
||||||
# pylint: enable=invalid-name
|
# pylint: enable=invalid-name
|
||||||
|
|
||||||
def _encode_board_string(self, board: list):
|
|
||||||
string = [str(i) for row in board for i in row]
|
|
||||||
|
|
||||||
while len(string) > 0 and string[0] == "0":
|
|
||||||
string = string[1:]
|
|
||||||
|
|
||||||
if string == "":
|
|
||||||
string = "0"
|
|
||||||
|
|
||||||
dec = 0
|
|
||||||
for i, digit in enumerate(string[::-1]):
|
|
||||||
dec += (3**i)*int(digit)
|
|
||||||
|
|
||||||
return str(dec)
|
|
||||||
|
|
||||||
def _decode_board_string(self, board_string: str):
|
|
||||||
dec = int(board_string)
|
|
||||||
string = []
|
|
||||||
while dec:
|
|
||||||
string.append(str(dec % 3))
|
|
||||||
dec = dec // 3
|
|
||||||
|
|
||||||
while len(string) < ROWCOUNT * COLUMNCOUNT:
|
|
||||||
string.append("0")
|
|
||||||
|
|
||||||
string = string[::-1]
|
|
||||||
|
|
||||||
board = [
|
|
||||||
[int(x) for x in string[i*COLUMNCOUNT:i*COLUMNCOUNT+COLUMNCOUNT]]
|
|
||||||
for i in range(ROWCOUNT)]
|
|
||||||
|
|
||||||
return board
|
|
||||||
|
|
||||||
async def start(self, ctx: SlashContext,
|
async def start(self, ctx: SlashContext,
|
||||||
opponent: Union[int, discord.User]):
|
opponent: Union[int, discord.User]):
|
||||||
"""
|
"""
|
||||||
@ -106,128 +106,90 @@ class ConnectFour():
|
|||||||
searches when minimaxing.
|
searches when minimaxing.
|
||||||
"""
|
"""
|
||||||
await self.bot.defer(ctx)
|
await self.bot.defer(ctx)
|
||||||
user = ctx.author.id
|
|
||||||
channel = str(ctx.channel_id)
|
channel = str(ctx.channel_id)
|
||||||
|
|
||||||
started_game = False
|
opponent_info = self._test_opponent(ctx, opponent)
|
||||||
can_start = True
|
if not opponent_info:
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(opponent, int):
|
difficulty = opponent_info[0]
|
||||||
# Opponent is Gwendolyn
|
difficulty_text = opponent_info[1]
|
||||||
if opponent in range(1, 6):
|
|
||||||
difficulty = int(opponent)
|
|
||||||
difficulty_text = f" with difficulty {difficulty}"
|
|
||||||
opponent = self.bot.user.id
|
|
||||||
else:
|
|
||||||
send_message = "Difficulty doesn't exist"
|
|
||||||
log_message = "They challenged a difficulty that doesn't exist"
|
|
||||||
can_start = False
|
|
||||||
elif isinstance(opponent, discord.User):
|
|
||||||
if opponent.bot:
|
|
||||||
# User has challenged a bot
|
|
||||||
if opponent == self.bot.user:
|
|
||||||
# It was Gwendolyn
|
|
||||||
difficulty = 3
|
|
||||||
difficulty_text = f" with difficulty {difficulty}"
|
|
||||||
opponent = self.bot.user.id
|
|
||||||
else:
|
|
||||||
send_message = "You can't challenge a bot!"
|
|
||||||
log_message = "They tried to challenge a bot"
|
|
||||||
can_start = False
|
|
||||||
else:
|
|
||||||
# Opponent is another player
|
|
||||||
if ctx.author != opponent:
|
|
||||||
opponent = opponent.id
|
|
||||||
difficulty = 5
|
|
||||||
difficulty_text = ""
|
|
||||||
else:
|
|
||||||
send_message = "You can't play against yourself"
|
|
||||||
log_message = "They tried to play against themself"
|
|
||||||
can_start = False
|
|
||||||
|
|
||||||
if can_start:
|
board = [[0 for _ in range(COLUMNCOUNT)] for _ in range(ROWCOUNT)]
|
||||||
board = [[0 for _ in range(COLUMNCOUNT)] for _ in range(ROWCOUNT)]
|
players = [ctx.author.id, opponent]
|
||||||
players = [user, opponent]
|
random.shuffle(players)
|
||||||
random.shuffle(players)
|
|
||||||
|
|
||||||
self.draw.draw_image(channel, board, 0, [0,0], "", players)
|
self.draw.draw_image(channel, board, players, [0, [0,0], ""], players)
|
||||||
|
|
||||||
gwendolyn_turn = (players[0] == self.bot.user.id)
|
opponent_name = self.get_name(f"#{opponent}")
|
||||||
started_game = True
|
turn_name = self.get_name(f"#{players[0]}")
|
||||||
|
|
||||||
opponent_name = self.get_name(f"#{opponent}")
|
started_text = "Started game against {}{}.".format(
|
||||||
turn_name = self.get_name(f"#{players[0]}")
|
opponent_name,
|
||||||
|
difficulty_text
|
||||||
started_text = "Started game against {}{}.".format(
|
)
|
||||||
opponent_name,
|
turn_text = f"It's {turn_name}'s turn"
|
||||||
difficulty_text
|
await ctx.send(f"{started_text} {turn_text}")
|
||||||
)
|
self.bot.log("They started a game")
|
||||||
turn_text = f"It's {turn_name}'s turn"
|
|
||||||
send_message = f"{started_text} {turn_text}"
|
|
||||||
log_message = "They started a game"
|
|
||||||
|
|
||||||
self.bot.log(log_message)
|
|
||||||
await ctx.send(send_message)
|
|
||||||
|
|
||||||
# Sets the whole game in motion
|
# Sets the whole game in motion
|
||||||
if started_game:
|
boards_path = "gwendolyn/resources/games/connect_four_boards/"
|
||||||
boards_path = "gwendolyn/resources/games/connect_four_boards/"
|
file_path = f"{boards_path}board{ctx.channel_id}.png"
|
||||||
file_path = f"{boards_path}board{ctx.channel_id}.png"
|
image_message = await ctx.send(file=discord.File(file_path))
|
||||||
image_message = await ctx.send(file=discord.File(file_path))
|
|
||||||
|
|
||||||
board_string = self._encode_board_string(board)
|
|
||||||
if gwendolyn_turn:
|
|
||||||
await self._connect_four_ai(
|
|
||||||
ctx, board_string, players, difficulty, image_message.id
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
buttons = []
|
|
||||||
for i in range(7):
|
|
||||||
custom_id = encode_id(
|
|
||||||
[
|
|
||||||
"connectfour",
|
|
||||||
"place",
|
|
||||||
str(players[0]),
|
|
||||||
board_string,
|
|
||||||
str(i),
|
|
||||||
str(players[0]),
|
|
||||||
str(players[1]),
|
|
||||||
str(difficulty),
|
|
||||||
str(image_message.id)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
buttons.append(create_button(
|
|
||||||
style=ButtonStyle.blue,
|
|
||||||
label=str(i+1),
|
|
||||||
custom_id=custom_id,
|
|
||||||
disabled=(board[0][i] != 0)
|
|
||||||
))
|
|
||||||
|
|
||||||
|
board_string = _encode_board_string(board)
|
||||||
|
if (players[0] == self.bot.user.id):
|
||||||
|
await self._connect_four_ai(
|
||||||
|
ctx, board_string, players, difficulty, image_message.id
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
buttons = []
|
||||||
|
for i in range(7):
|
||||||
custom_id = encode_id(
|
custom_id = encode_id(
|
||||||
[
|
[
|
||||||
"connectfour",
|
"connectfour",
|
||||||
"end",
|
"place",
|
||||||
|
str(players[0]),
|
||||||
|
board_string,
|
||||||
|
str(i),
|
||||||
str(players[0]),
|
str(players[0]),
|
||||||
str(players[1]),
|
str(players[1]),
|
||||||
|
str(difficulty),
|
||||||
str(image_message.id)
|
str(image_message.id)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
buttons.append(create_button(
|
buttons.append(create_button(
|
||||||
style=ButtonStyle.red,
|
style=ButtonStyle.blue,
|
||||||
label="Surrender",
|
label=str(i+1),
|
||||||
custom_id=custom_id
|
custom_id=custom_id,
|
||||||
|
disabled=(board[0][i] != 0)
|
||||||
))
|
))
|
||||||
|
|
||||||
action_rows = []
|
custom_id = encode_id(
|
||||||
for x in range(((len(buttons)-1)//4)+1):
|
[
|
||||||
row_buttons = buttons[
|
"connectfour",
|
||||||
(x*4):(min(len(buttons),x*4+4))
|
"end",
|
||||||
]
|
str(players[0]),
|
||||||
action_rows.append(create_actionrow(*row_buttons))
|
str(players[1]),
|
||||||
|
str(image_message.id)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
buttons.append(create_button(
|
||||||
|
style=ButtonStyle.red,
|
||||||
|
label="Surrender",
|
||||||
|
custom_id=custom_id
|
||||||
|
))
|
||||||
|
|
||||||
await image_message.edit(
|
action_rows = []
|
||||||
components=action_rows
|
for x in range(((len(buttons)-1)//4)+1):
|
||||||
)
|
row_buttons = buttons[
|
||||||
|
(x*4):(min(len(buttons),x*4+4))
|
||||||
|
]
|
||||||
|
action_rows.append(create_actionrow(*row_buttons))
|
||||||
|
|
||||||
|
await image_message.edit(
|
||||||
|
components=action_rows
|
||||||
|
)
|
||||||
|
|
||||||
async def place_piece(self, ctx: ComponentContext, board_string: str,
|
async def place_piece(self, ctx: ComponentContext, board_string: str,
|
||||||
column: int, players: list[int], difficulty: int,
|
column: int, players: list[int], difficulty: int,
|
||||||
@ -246,7 +208,7 @@ class ConnectFour():
|
|||||||
user_name = self.get_name(f"#{placer}")
|
user_name = self.get_name(f"#{placer}")
|
||||||
placed_piece = False
|
placed_piece = False
|
||||||
|
|
||||||
board = self._decode_board_string(board_string)
|
board = _decode_board_string(board_string)
|
||||||
board = self._place_on_board(board, player_number, column)
|
board = self._place_on_board(board, player_number, column)
|
||||||
|
|
||||||
if board is None:
|
if board is None:
|
||||||
@ -300,7 +262,7 @@ class ConnectFour():
|
|||||||
if game_won:
|
if game_won:
|
||||||
self._end_game(winner, players, difficulty)
|
self._end_game(winner, players, difficulty)
|
||||||
else:
|
else:
|
||||||
board_string = self._encode_board_string(board)
|
board_string = _encode_board_string(board)
|
||||||
if gwendolyn_turn:
|
if gwendolyn_turn:
|
||||||
await self._connect_four_ai(
|
await self._connect_four_ai(
|
||||||
ctx, board_string, players, difficulty, image_message.id
|
ctx, board_string, players, difficulty, image_message.id
|
||||||
@ -513,7 +475,7 @@ class ConnectFour():
|
|||||||
|
|
||||||
self.bot.log("Figuring out best move")
|
self.bot.log("Figuring out best move")
|
||||||
|
|
||||||
board = self._decode_board_string(board_string)
|
board = _decode_board_string(board_string)
|
||||||
player = players.index(self.bot.user.id)+1
|
player = players.index(self.bot.user.id)+1
|
||||||
|
|
||||||
scores = [-math.inf for _ in range(COLUMNCOUNT)]
|
scores = [-math.inf for _ in range(COLUMNCOUNT)]
|
||||||
@ -797,8 +759,8 @@ class DrawConnectFour():
|
|||||||
# pylint: enable=invalid-name
|
# pylint: enable=invalid-name
|
||||||
|
|
||||||
# Draws the whole thing
|
# Draws the whole thing
|
||||||
def draw_image(self, channel: str, board: list, winner: int,
|
def draw_image(self, channel: str, board: list, players: list,
|
||||||
win_coordinates: list, win_direction: str, players: list):
|
win_info: list):
|
||||||
"""
|
"""
|
||||||
Draw an image of the connect four board.
|
Draw an image of the connect four board.
|
||||||
|
|
||||||
@ -815,8 +777,8 @@ class DrawConnectFour():
|
|||||||
|
|
||||||
self._draw_pieces(drawer, board)
|
self._draw_pieces(drawer, board)
|
||||||
|
|
||||||
if winner != 0:
|
if win_info[0] != 0:
|
||||||
self._draw_win(background, win_coordinates, win_direction)
|
self._draw_win(background, win_info[1], win_info[2])
|
||||||
|
|
||||||
self._draw_footer(drawer, players)
|
self._draw_footer(drawer, players)
|
||||||
|
|
||||||
|
@ -1,28 +1,80 @@
|
|||||||
"""Base class for the games."""
|
"""Base class for the games."""
|
||||||
import random
|
import random
|
||||||
from shutil import copyfile
|
from typing import Union
|
||||||
|
|
||||||
from discord import File
|
from discord import File, User
|
||||||
from discord.abc import Messageable
|
from discord.abc import Messageable
|
||||||
|
from discord_slash.utils.manage_components import (create_button,
|
||||||
|
create_actionrow)
|
||||||
|
from discord_slash.context import InteractionContext as IntCont
|
||||||
|
|
||||||
from PIL import ImageFont, Image, ImageDraw
|
from PIL import ImageFont, Image, ImageDraw
|
||||||
|
|
||||||
|
from gwendolyn.exceptions import GameNotInDatabase
|
||||||
|
from gwendolyn.utils import encode_id
|
||||||
|
|
||||||
class GameBase():
|
class GameBase():
|
||||||
"""The base class for the games."""
|
"""The base class for the games."""
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot, game_name: int, drawer):
|
||||||
"""Initialize the class."""
|
"""Initialize the class."""
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.database = self.bot.database
|
|
||||||
self.long_strings = self.bot.long_strings
|
self.long_strings = self.bot.long_strings
|
||||||
self.resources = "gwendolyn/resources/games/"
|
self.resources = "gwendolyn/resources/games/"
|
||||||
self.game_name = ""
|
self.game_name = game_name
|
||||||
self.old_images_path = f"{self.resources}old_images/{self.game_name}"
|
self.draw = drawer
|
||||||
self.draw = BaseDrawer(bot, self)
|
|
||||||
|
|
||||||
def access_document(self, channel: str, collection_name: str="games"):
|
def _get_action_rows(self, buttons: list[tuple[str, list]]):
|
||||||
|
self.bot.log("Generation action rows")
|
||||||
|
button_objects = []
|
||||||
|
for label, data, style in buttons:
|
||||||
|
custom_id = encode_id([self.game_name] + data)
|
||||||
|
button_objects.append(create_button(
|
||||||
|
style=style,
|
||||||
|
label=label,
|
||||||
|
custom_id=custom_id
|
||||||
|
))
|
||||||
|
|
||||||
|
action_rows = []
|
||||||
|
for i in range(((len(button_objects)-1)//5)+1):
|
||||||
|
action_rows.append(create_actionrow(*button_objects[i*5:i*5+5]))
|
||||||
|
|
||||||
|
return action_rows
|
||||||
|
|
||||||
|
async def _send_image(self, channel: Messageable,
|
||||||
|
buttons: list[tuple[str, list]] = None, delete=True):
|
||||||
|
self.draw.draw(str(channel.id))
|
||||||
|
file_path = f"{self.resources}images/{self.game_name}{channel.id}.png"
|
||||||
|
old_image = await channel.send(
|
||||||
|
file=File(file_path), delete_after=120 if delete else None)
|
||||||
|
|
||||||
|
if buttons is not None and len(buttons) < 25:
|
||||||
|
await old_image.edit(components = self._get_action_rows(buttons))
|
||||||
|
|
||||||
|
return old_image
|
||||||
|
|
||||||
|
class DatabaseGame(GameBase):
|
||||||
|
"""The base class for the games."""
|
||||||
|
|
||||||
|
def __init__(self, bot, game_name, drawer):
|
||||||
|
"""Initialize the class."""
|
||||||
|
super().__init__(bot, game_name, drawer)
|
||||||
|
self.database = self.bot.database
|
||||||
|
self.old_images_path = f"{self.resources}old_images/{self.game_name}"
|
||||||
|
|
||||||
|
def access_document(self, channel: str, collection_name: str="games",
|
||||||
|
raise_missing_error: bool=True):
|
||||||
collection = self.bot.database[f"{self.game_name} {collection_name}"]
|
collection = self.bot.database[f"{self.game_name} {collection_name}"]
|
||||||
return collection.find_one({"_id": channel})
|
game = collection.find_one({"_id": channel})
|
||||||
|
if game is None and raise_missing_error:
|
||||||
|
raise GameNotInDatabase(self.game_name, channel)
|
||||||
|
|
||||||
|
return game
|
||||||
|
|
||||||
|
def _test_document(self, channel: str, collection_name: str="games"):
|
||||||
|
collection = self.bot.database[f"{self.game_name} {collection_name}"]
|
||||||
|
game = collection.find_one({"_id": channel})
|
||||||
|
return game is not None
|
||||||
|
|
||||||
def _update_document(self, channel: str, updater: dict,
|
def _update_document(self, channel: str, updater: dict,
|
||||||
collection_name: str="games"):
|
collection_name: str="games"):
|
||||||
@ -46,28 +98,29 @@ class GameBase():
|
|||||||
|
|
||||||
await old_image.delete()
|
await old_image.delete()
|
||||||
|
|
||||||
async def _send_image(self, channel: Messageable):
|
async def _send_image(self, channel: Messageable,
|
||||||
self.draw.draw(str(channel.id))
|
buttons: list[tuple[str, list]] = None, delete=True):
|
||||||
file_path = f"{self.resources}images/blackjack{channel.id}.png"
|
old_image = super()._send_image(channel, buttons, delete)
|
||||||
old_image = await channel.send(file=File(file_path))
|
|
||||||
with open(self.old_images_path + str(channel.id), "w") as file_pointer:
|
with open(self.old_images_path + str(channel.id), "w") as file_pointer:
|
||||||
file_pointer.write(str(old_image.id))
|
file_pointer.write(str(old_image.id))
|
||||||
|
|
||||||
async def _start_new(self, channel: Messageable, new_game: dict):
|
async def _start_new(self, channel: Messageable, new_game: dict,
|
||||||
|
buttons: list[tuple[str, list]] = None):
|
||||||
self._insert_document(new_game)
|
self._insert_document(new_game)
|
||||||
default_image = f"{self.resources}default_images/{self.game_name}.png"
|
await self._send_image(channel, buttons)
|
||||||
new_image = f"{self.resources}images/{self.game_name}{channel.id}.png"
|
|
||||||
copyfile(default_image, new_image)
|
|
||||||
|
|
||||||
await self._send_image(channel)
|
async def _end_game(self, channel: Messageable):
|
||||||
|
await self._delete_old_image(channel)
|
||||||
|
await self._send_image(channel, delete=False)
|
||||||
|
self._delete_document(str(channel.id))
|
||||||
|
|
||||||
|
|
||||||
class CardGame(GameBase):
|
class CardGame(DatabaseGame):
|
||||||
"""The class for card games."""
|
"""The class for card games."""
|
||||||
def __init__(self, bot):
|
def __init__(self, bot, game_name, drawer, deck_used):
|
||||||
"""Initialize the class."""
|
"""Initialize the class."""
|
||||||
super().__init__(bot)
|
super().__init__(bot, game_name, drawer)
|
||||||
self.decks_used = 1
|
self.decks_used = deck_used
|
||||||
|
|
||||||
def _shuffle_cards(self, channel: str):
|
def _shuffle_cards(self, channel: str):
|
||||||
self.bot.log(f"Shuffling cards for {self.game_name}")
|
self.bot.log(f"Shuffling cards for {self.game_name}")
|
||||||
@ -99,6 +152,43 @@ class CardGame(GameBase):
|
|||||||
|
|
||||||
return drawn_card
|
return drawn_card
|
||||||
|
|
||||||
|
class BoardGame(GameBase):
|
||||||
|
def _test_opponent(self, ctx: IntCont, opponent: Union[int, User]):
|
||||||
|
if isinstance(opponent, int):
|
||||||
|
# Opponent is Gwendolyn
|
||||||
|
if opponent in range(1, 6):
|
||||||
|
difficulty = int(opponent)
|
||||||
|
difficulty_text = f" with difficulty {difficulty}"
|
||||||
|
opponent = self.bot.user.id
|
||||||
|
else:
|
||||||
|
await ctx.send("Difficulty doesn't exist")
|
||||||
|
self.bot.log("They challenged a difficulty that doesn't exist")
|
||||||
|
return False
|
||||||
|
elif isinstance(opponent, User):
|
||||||
|
if opponent.bot:
|
||||||
|
# User has challenged a bot
|
||||||
|
if opponent == self.bot.user:
|
||||||
|
# It was Gwendolyn
|
||||||
|
difficulty = 3
|
||||||
|
difficulty_text = f" with difficulty {difficulty}"
|
||||||
|
opponent = self.bot.user.id
|
||||||
|
else:
|
||||||
|
await ctx.send("You can't challenge a bot!")
|
||||||
|
self.bot.log("They tried to challenge a bot")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
# Opponent is another player
|
||||||
|
if ctx.author != opponent:
|
||||||
|
opponent = opponent.id
|
||||||
|
difficulty = 5
|
||||||
|
difficulty_text = ""
|
||||||
|
else:
|
||||||
|
await ctx.send("You can't play against yourself")
|
||||||
|
self.bot.log("They tried to play against themself")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return difficulty, difficulty_text
|
||||||
|
|
||||||
class BaseDrawer():
|
class BaseDrawer():
|
||||||
"""Class for drawing games."""
|
"""Class for drawing games."""
|
||||||
def __init__(self, bot, game: GameBase):
|
def __init__(self, bot, game: GameBase):
|
||||||
|
@ -8,13 +8,13 @@ Deals with commands and logic for hangman games.
|
|||||||
DrawHangman()
|
DrawHangman()
|
||||||
Draws the image shown to the player.
|
Draws the image shown to the player.
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
import datetime # Used for generating the game id
|
import datetime # Used for generating the game id
|
||||||
import string # string.ascii_uppercase used
|
import string # string.ascii_uppercase used
|
||||||
import math # Used by DrawHangman(), mainly for drawing circles
|
import math # Used by DrawHangman(), mainly for drawing circles
|
||||||
import random # Used to draw poorly
|
import random # Used to draw poorly
|
||||||
import requests # Used for getting the word in Hangman.start()
|
import requests # Used for getting the word in Hangman.start()
|
||||||
import discord # Used for discord.file and type hints
|
import discord # Used for discord.file and type hints
|
||||||
import os
|
|
||||||
|
|
||||||
from discord_slash.utils.manage_components import (create_button,
|
from discord_slash.utils.manage_components import (create_button,
|
||||||
create_actionrow)
|
create_actionrow)
|
||||||
@ -50,10 +50,10 @@ class Hangman():
|
|||||||
The parameters to pass to every api call.
|
The parameters to pass to every api call.
|
||||||
"""
|
"""
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.__draw = DrawHangman(bot)
|
self._draw = DrawHangman(bot)
|
||||||
self.__API_url = "https://api.wordnik.com/v4/words.json/randomWords?" # pylint: disable=invalid-name
|
self._API_url = "https://api.wordnik.com/v4/words.json/randomWords?" # pylint: disable=invalid-name
|
||||||
api_key = self.bot.credentials["wordnik_key"]
|
api_key = self.bot.credentials["wordnik_key"]
|
||||||
self.__APIPARAMS = { # pylint: disable=invalid-name
|
self._APIPARAMS = { # pylint: disable=invalid-name
|
||||||
"hasDictionaryDef": True,
|
"hasDictionaryDef": True,
|
||||||
"minCorpusCount": 5000,
|
"minCorpusCount": 5000,
|
||||||
"maxCorpusCount": -1,
|
"maxCorpusCount": -1,
|
||||||
@ -78,7 +78,7 @@ class Hangman():
|
|||||||
|
|
||||||
word = "-"
|
word = "-"
|
||||||
while "-" in word or "." in word:
|
while "-" in word or "." in word:
|
||||||
response = requests.get(self.__API_url, params=self.__APIPARAMS)
|
response = requests.get(self._API_url, params=self._APIPARAMS)
|
||||||
word = list(response.json()[0]["word"].upper())
|
word = list(response.json()[0]["word"].upper())
|
||||||
|
|
||||||
self.bot.log("Found the word \""+"".join(word)+"\"")
|
self.bot.log("Found the word \""+"".join(word)+"\"")
|
||||||
@ -87,7 +87,7 @@ class Hangman():
|
|||||||
|
|
||||||
remaining_letters = list(string.ascii_uppercase)
|
remaining_letters = list(string.ascii_uppercase)
|
||||||
|
|
||||||
self.__draw.draw_image(game_id, 0, word, guessed, [])
|
self._draw.draw_image(game_id, 0, word, guessed, [])
|
||||||
|
|
||||||
send_message = f"{ctx.author.display_name} started a game of hangman."
|
send_message = f"{ctx.author.display_name} started a game of hangman."
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ class Hangman():
|
|||||||
send_message = "Guessed {}. There were {} {}s in the word."
|
send_message = "Guessed {}. There were {} {}s in the word."
|
||||||
send_message = send_message.format(guess, correct_guess, guess)
|
send_message = send_message.format(guess, correct_guess, guess)
|
||||||
|
|
||||||
self.__draw.draw_image(game_id, misses, word, guessed, guessed_letters)
|
self._draw.draw_image(game_id, misses, word, guessed, guessed_letters)
|
||||||
|
|
||||||
if misses == 6:
|
if misses == 6:
|
||||||
send_message += self.bot.long_strings["Hangman lost game"]
|
send_message += self.bot.long_strings["Hangman lost game"]
|
||||||
@ -325,36 +325,36 @@ class DrawHangman():
|
|||||||
FONT
|
FONT
|
||||||
SMALLFONT
|
SMALLFONT
|
||||||
"""
|
"""
|
||||||
self.__bot = bot
|
self._bot = bot
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
self.__CIRCLESIZE = 120
|
self._CIRCLESIZE = 120
|
||||||
self.__LINEWIDTH = 12
|
self._LINEWIDTH = 12
|
||||||
|
|
||||||
self.__BODYSIZE = 210
|
self._BODYSIZE = 210
|
||||||
self.__LIMBSIZE = 60
|
self._LIMBSIZE = 60
|
||||||
self.__ARMPOSITION = 60
|
self._ARMPOSITION = 60
|
||||||
|
|
||||||
self.__MANX = (self.__LIMBSIZE*2)
|
self._MANX = (self._LIMBSIZE*2)
|
||||||
self.__MANY = (self.__CIRCLESIZE+self.__BODYSIZE+self.__LIMBSIZE)
|
self._MANY = (self._CIRCLESIZE+self._BODYSIZE+self._LIMBSIZE)
|
||||||
MANPADDING = self.__LINEWIDTH*4
|
MANPADDING = self._LINEWIDTH*4
|
||||||
self.__MANX += MANPADDING
|
self._MANX += MANPADDING
|
||||||
self.__MANY += MANPADDING
|
self._MANY += MANPADDING
|
||||||
|
|
||||||
self.__LETTERLINELENGTH = 90
|
self._LETTERLINELENGTH = 90
|
||||||
self.__LETTERLINEDISTANCE = 30
|
self._LETTERLINEDISTANCE = 30
|
||||||
|
|
||||||
self.__GALLOWX, self.__GALLOWY = 360, 600
|
self._GALLOWX, self._GALLOWY = 360, 600
|
||||||
self.__PHI = 1-(1 / ((1 + 5 ** 0.5) / 2))
|
self._PHI = 1-(1 / ((1 + 5 ** 0.5) / 2))
|
||||||
|
|
||||||
LETTERSIZE = 75 # Wrong guesses letter size
|
LETTERSIZE = 75 # Wrong guesses letter size
|
||||||
WORDSIZE = 70 # Correct guesses letter size
|
WORDSIZE = 70 # Correct guesses letter size
|
||||||
|
|
||||||
FONTPATH = "gwendolyn/resources/fonts/comic-sans-bold.ttf"
|
FONTPATH = "gwendolyn/resources/fonts/comic-sans-bold.ttf"
|
||||||
self.__FONT = ImageFont.truetype(FONTPATH, LETTERSIZE)
|
self._FONT = ImageFont.truetype(FONTPATH, LETTERSIZE)
|
||||||
self.__SMALLFONT = ImageFont.truetype(FONTPATH, WORDSIZE)
|
self._SMALLFONT = ImageFont.truetype(FONTPATH, WORDSIZE)
|
||||||
# pylint: enable=invalid-name
|
# pylint: enable=invalid-name
|
||||||
|
|
||||||
def __deviate(self, pre_deviance: int, pre_deviance_accuracy: int,
|
def _deviate(self, pre_deviance: int, pre_deviance_accuracy: int,
|
||||||
position_change: float, maxmin: int,
|
position_change: float, maxmin: int,
|
||||||
max_acceleration: float):
|
max_acceleration: float):
|
||||||
random_deviance = random.uniform(-position_change, position_change)
|
random_deviance = random.uniform(-position_change, position_change)
|
||||||
@ -371,14 +371,14 @@ class DrawHangman():
|
|||||||
deviance = -maxmin
|
deviance = -maxmin
|
||||||
return deviance, deviance_accuracy
|
return deviance, deviance_accuracy
|
||||||
|
|
||||||
def __bad_circle(self):
|
def _bad_circle(self):
|
||||||
circle_padding = (self.__LINEWIDTH*3)
|
circle_padding = (self._LINEWIDTH*3)
|
||||||
image_width = self.__CIRCLESIZE+circle_padding
|
image_width = self._CIRCLESIZE+circle_padding
|
||||||
image_size = (image_width, image_width)
|
image_size = (image_width, image_width)
|
||||||
background = Image.new("RGBA", image_size, color=(0, 0, 0, 0))
|
background = Image.new("RGBA", image_size, color=(0, 0, 0, 0))
|
||||||
|
|
||||||
drawer = ImageDraw.Draw(background, "RGBA")
|
drawer = ImageDraw.Draw(background, "RGBA")
|
||||||
middle = (self.__CIRCLESIZE+(self.__LINEWIDTH*3))/2
|
middle = (self._CIRCLESIZE+(self._LINEWIDTH*3))/2
|
||||||
deviance_x = 0
|
deviance_x = 0
|
||||||
deviance_y = 0
|
deviance_y = 0
|
||||||
deviance_accuracy_x = 0
|
deviance_accuracy_x = 0
|
||||||
@ -387,96 +387,96 @@ class DrawHangman():
|
|||||||
degrees_amount = 360 + random.randint(-10, 30)
|
degrees_amount = 360 + random.randint(-10, 30)
|
||||||
|
|
||||||
for degree in range(degrees_amount):
|
for degree in range(degrees_amount):
|
||||||
deviance_x, deviance_accuracy_x = self.__deviate(
|
deviance_x, deviance_accuracy_x = self._deviate(
|
||||||
deviance_x,
|
deviance_x,
|
||||||
deviance_accuracy_x,
|
deviance_accuracy_x,
|
||||||
self.__LINEWIDTH/100,
|
self._LINEWIDTH/100,
|
||||||
self.__LINEWIDTH,
|
self._LINEWIDTH,
|
||||||
0.03
|
0.03
|
||||||
)
|
)
|
||||||
deviance_y, deviance_accuracy_y = self.__deviate(
|
deviance_y, deviance_accuracy_y = self._deviate(
|
||||||
deviance_y,
|
deviance_y,
|
||||||
deviance_accuracy_y,
|
deviance_accuracy_y,
|
||||||
self.__LINEWIDTH/100,
|
self._LINEWIDTH/100,
|
||||||
self.__LINEWIDTH,
|
self._LINEWIDTH,
|
||||||
0.03
|
0.03
|
||||||
)
|
)
|
||||||
|
|
||||||
radians = math.radians(degree+start)
|
radians = math.radians(degree+start)
|
||||||
circle_x = (math.cos(radians) * (self.__CIRCLESIZE/2))
|
circle_x = (math.cos(radians) * (self._CIRCLESIZE/2))
|
||||||
circle_y = (math.sin(radians) * (self.__CIRCLESIZE/2))
|
circle_y = (math.sin(radians) * (self._CIRCLESIZE/2))
|
||||||
|
|
||||||
position_x = middle + circle_x - (self.__LINEWIDTH/2) + deviance_x
|
position_x = middle + circle_x - (self._LINEWIDTH/2) + deviance_x
|
||||||
position_y = middle + circle_y - (self.__LINEWIDTH/2) + deviance_y
|
position_y = middle + circle_y - (self._LINEWIDTH/2) + deviance_y
|
||||||
|
|
||||||
circle_position = [
|
circle_position = [
|
||||||
(position_x, position_y),
|
(position_x, position_y),
|
||||||
(position_x+self.__LINEWIDTH, position_y+self.__LINEWIDTH)
|
(position_x+self._LINEWIDTH, position_y+self._LINEWIDTH)
|
||||||
]
|
]
|
||||||
drawer.ellipse(circle_position, fill=(0, 0, 0, 255))
|
drawer.ellipse(circle_position, fill=(0, 0, 0, 255))
|
||||||
|
|
||||||
return background
|
return background
|
||||||
|
|
||||||
def __bad_line(self, length: int, rotated: bool = False):
|
def _bad_line(self, length: int, rotated: bool = False):
|
||||||
if rotated:
|
if rotated:
|
||||||
width, height = length+self.__LINEWIDTH*3, self.__LINEWIDTH*3
|
width, height = length+self._LINEWIDTH*3, self._LINEWIDTH*3
|
||||||
else:
|
else:
|
||||||
width, height = self.__LINEWIDTH*3, length+self.__LINEWIDTH*3
|
width, height = self._LINEWIDTH*3, length+self._LINEWIDTH*3
|
||||||
background = Image.new("RGBA", (width, height), color=(0, 0, 0, 0))
|
background = Image.new("RGBA", (width, height), color=(0, 0, 0, 0))
|
||||||
|
|
||||||
drawer = ImageDraw.Draw(background, "RGBA")
|
drawer = ImageDraw.Draw(background, "RGBA")
|
||||||
|
|
||||||
possible_deviance = int(self.__LINEWIDTH/3)
|
possible_deviance = int(self._LINEWIDTH/3)
|
||||||
deviance_x = random.randint(-possible_deviance, possible_deviance)
|
deviance_x = random.randint(-possible_deviance, possible_deviance)
|
||||||
deviance_y = 0
|
deviance_y = 0
|
||||||
deviance_accuracy_x = 0
|
deviance_accuracy_x = 0
|
||||||
deviance_accuracy_y = 0
|
deviance_accuracy_y = 0
|
||||||
|
|
||||||
for pixel in range(length):
|
for pixel in range(length):
|
||||||
deviance_x, deviance_accuracy_x = self.__deviate(
|
deviance_x, deviance_accuracy_x = self._deviate(
|
||||||
deviance_x,
|
deviance_x,
|
||||||
deviance_accuracy_x,
|
deviance_accuracy_x,
|
||||||
self.__LINEWIDTH/1000,
|
self._LINEWIDTH/1000,
|
||||||
self.__LINEWIDTH,
|
self._LINEWIDTH,
|
||||||
0.004
|
0.004
|
||||||
)
|
)
|
||||||
deviance_y, deviance_accuracy_y = self.__deviate(
|
deviance_y, deviance_accuracy_y = self._deviate(
|
||||||
deviance_y,
|
deviance_y,
|
||||||
deviance_accuracy_y,
|
deviance_accuracy_y,
|
||||||
self.__LINEWIDTH/1000,
|
self._LINEWIDTH/1000,
|
||||||
self.__LINEWIDTH,
|
self._LINEWIDTH,
|
||||||
0.004
|
0.004
|
||||||
)
|
)
|
||||||
|
|
||||||
if rotated:
|
if rotated:
|
||||||
position_x = self.__LINEWIDTH + pixel + deviance_x
|
position_x = self._LINEWIDTH + pixel + deviance_x
|
||||||
position_y = self.__LINEWIDTH + deviance_y
|
position_y = self._LINEWIDTH + deviance_y
|
||||||
else:
|
else:
|
||||||
position_x = self.__LINEWIDTH + deviance_x
|
position_x = self._LINEWIDTH + deviance_x
|
||||||
position_y = self.__LINEWIDTH + pixel + deviance_y
|
position_y = self._LINEWIDTH + pixel + deviance_y
|
||||||
|
|
||||||
circle_position = [
|
circle_position = [
|
||||||
(position_x, position_y),
|
(position_x, position_y),
|
||||||
(position_x+self.__LINEWIDTH, position_y+self.__LINEWIDTH)
|
(position_x+self._LINEWIDTH, position_y+self._LINEWIDTH)
|
||||||
]
|
]
|
||||||
drawer.ellipse(circle_position, fill=(0, 0, 0, 255))
|
drawer.ellipse(circle_position, fill=(0, 0, 0, 255))
|
||||||
|
|
||||||
return background
|
return background
|
||||||
|
|
||||||
def __draw_man(self, misses: int, seed: str):
|
def _draw_man(self, misses: int, seed: str):
|
||||||
random.seed(seed)
|
random.seed(seed)
|
||||||
man_size = (self.__MANX, self.__MANY)
|
man_size = (self._MANX, self._MANY)
|
||||||
background = Image.new("RGBA", man_size, color=(0, 0, 0, 0))
|
background = Image.new("RGBA", man_size, color=(0, 0, 0, 0))
|
||||||
|
|
||||||
if misses >= 1:
|
if misses >= 1:
|
||||||
head = self.__bad_circle()
|
head = self._bad_circle()
|
||||||
paste_x = (self.__MANX-(self.__CIRCLESIZE+(self.__LINEWIDTH*3)))//2
|
paste_x = (self._MANX-(self._CIRCLESIZE+(self._LINEWIDTH*3)))//2
|
||||||
paste_position = (paste_x, 0)
|
paste_position = (paste_x, 0)
|
||||||
background.paste(head, paste_position, head)
|
background.paste(head, paste_position, head)
|
||||||
if misses >= 2:
|
if misses >= 2:
|
||||||
body = self.__bad_line(self.__BODYSIZE)
|
body = self._bad_line(self._BODYSIZE)
|
||||||
paste_x = (self.__MANX-(self.__LINEWIDTH*3))//2
|
paste_x = (self._MANX-(self._LINEWIDTH*3))//2
|
||||||
paste_position = (paste_x, self.__CIRCLESIZE)
|
paste_position = (paste_x, self._CIRCLESIZE)
|
||||||
background.paste(body, paste_position, body)
|
background.paste(body, paste_position, body)
|
||||||
|
|
||||||
if misses >= 3:
|
if misses >= 3:
|
||||||
@ -487,30 +487,30 @@ class DrawHangman():
|
|||||||
random.seed(seed)
|
random.seed(seed)
|
||||||
|
|
||||||
for limb in limbs:
|
for limb in limbs:
|
||||||
limb_drawing = self.__bad_line(self.__LIMBSIZE, True)
|
limb_drawing = self._bad_line(self._LIMBSIZE, True)
|
||||||
x_position = (self.__MANX-(self.__LINEWIDTH*3))//2
|
x_position = (self._MANX-(self._LINEWIDTH*3))//2
|
||||||
|
|
||||||
if limb[1] == "a":
|
if limb[1] == "a":
|
||||||
rotation = random.randint(-45, 45)
|
rotation = random.randint(-45, 45)
|
||||||
shift = math.sin(math.radians(rotation))
|
shift = math.sin(math.radians(rotation))
|
||||||
line_length = self.__LIMBSIZE+(self.__LINEWIDTH*3)
|
line_length = self._LIMBSIZE+(self._LINEWIDTH*3)
|
||||||
compensation = int(shift*line_length)
|
compensation = int(shift*line_length)
|
||||||
limb_drawing = limb_drawing.rotate(rotation, expand=1)
|
limb_drawing = limb_drawing.rotate(rotation, expand=1)
|
||||||
y_position = self.__CIRCLESIZE + self.__ARMPOSITION
|
y_position = self._CIRCLESIZE + self._ARMPOSITION
|
||||||
if limb == "ra":
|
if limb == "ra":
|
||||||
compensation = min(-compensation, 0)
|
compensation = min(-compensation, 0)
|
||||||
else:
|
else:
|
||||||
x_position -= self.__LIMBSIZE
|
x_position -= self._LIMBSIZE
|
||||||
compensation = min(compensation, 0)
|
compensation = min(compensation, 0)
|
||||||
|
|
||||||
y_position += compensation
|
y_position += compensation
|
||||||
else:
|
else:
|
||||||
rotation = random.randint(-15, 15)
|
rotation = random.randint(-15, 15)
|
||||||
y_position = self.__CIRCLESIZE+self.__BODYSIZE-self.__LINEWIDTH
|
y_position = self._CIRCLESIZE+self._BODYSIZE-self._LINEWIDTH
|
||||||
if limb == "rl":
|
if limb == "rl":
|
||||||
limb_drawing = limb_drawing.rotate(rotation-45, expand=1)
|
limb_drawing = limb_drawing.rotate(rotation-45, expand=1)
|
||||||
else:
|
else:
|
||||||
x_position += -limb_drawing.size[0]+self.__LINEWIDTH*3
|
x_position += -limb_drawing.size[0]+self._LINEWIDTH*3
|
||||||
limb_drawing = limb_drawing.rotate(rotation+45, expand=1)
|
limb_drawing = limb_drawing.rotate(rotation+45, expand=1)
|
||||||
|
|
||||||
paste_position = (x_position, y_position)
|
paste_position = (x_position, y_position)
|
||||||
@ -518,11 +518,11 @@ class DrawHangman():
|
|||||||
|
|
||||||
return background
|
return background
|
||||||
|
|
||||||
def __bad_text(self, text: str, big: bool, color: tuple = (0, 0, 0, 255)):
|
def _bad_text(self, text: str, big: bool, color: tuple = (0, 0, 0, 255)):
|
||||||
if big:
|
if big:
|
||||||
font = self.__FONT
|
font = self._FONT
|
||||||
else:
|
else:
|
||||||
font = self.__SMALLFONT
|
font = self._SMALLFONT
|
||||||
width, height = font.getsize(text)
|
width, height = font.getsize(text)
|
||||||
img = Image.new("RGBA", (width, height), color=(0, 0, 0, 0))
|
img = Image.new("RGBA", (width, height), color=(0, 0, 0, 0))
|
||||||
drawer = ImageDraw.Draw(img, "RGBA")
|
drawer = ImageDraw.Draw(img, "RGBA")
|
||||||
@ -530,59 +530,59 @@ class DrawHangman():
|
|||||||
drawer.text((0, 0), text, font=font, fill=color)
|
drawer.text((0, 0), text, font=font, fill=color)
|
||||||
return img
|
return img
|
||||||
|
|
||||||
def __draw_gallows(self):
|
def _draw_gallows(self):
|
||||||
gallow_size = (self.__GALLOWX, self.__GALLOWY)
|
gallow_size = (self._GALLOWX, self._GALLOWY)
|
||||||
background = Image.new("RGBA", gallow_size, color=(0, 0, 0, 0))
|
background = Image.new("RGBA", gallow_size, color=(0, 0, 0, 0))
|
||||||
|
|
||||||
bottom_line = self.__bad_line(int(self.__GALLOWX * 0.75), True)
|
bottom_line = self._bad_line(int(self._GALLOWX * 0.75), True)
|
||||||
bottom_line_x = int(self.__GALLOWX * 0.125)
|
bottom_line_x = int(self._GALLOWX * 0.125)
|
||||||
bottom_line_y = self.__GALLOWY-(self.__LINEWIDTH*4)
|
bottom_line_y = self._GALLOWY-(self._LINEWIDTH*4)
|
||||||
paste_position = (bottom_line_x, bottom_line_y)
|
paste_position = (bottom_line_x, bottom_line_y)
|
||||||
background.paste(bottom_line, paste_position, bottom_line)
|
background.paste(bottom_line, paste_position, bottom_line)
|
||||||
|
|
||||||
line_two = self.__bad_line(self.__GALLOWY-self.__LINEWIDTH*6)
|
line_two = self._bad_line(self._GALLOWY-self._LINEWIDTH*6)
|
||||||
line_two_x = int(self.__GALLOWX*(0.75*self.__PHI))
|
line_two_x = int(self._GALLOWX*(0.75*self._PHI))
|
||||||
line_two_y = self.__LINEWIDTH*2
|
line_two_y = self._LINEWIDTH*2
|
||||||
paste_position = (line_two_x, line_two_y)
|
paste_position = (line_two_x, line_two_y)
|
||||||
background.paste(line_two, paste_position, line_two)
|
background.paste(line_two, paste_position, line_two)
|
||||||
|
|
||||||
top_line = self.__bad_line(int(self.__GALLOWY*0.30), True)
|
top_line = self._bad_line(int(self._GALLOWY*0.30), True)
|
||||||
paste_x = int(self.__GALLOWX*(0.75*self.__PHI))-self.__LINEWIDTH
|
paste_x = int(self._GALLOWX*(0.75*self._PHI))-self._LINEWIDTH
|
||||||
paste_position = (paste_x, self.__LINEWIDTH*3)
|
paste_position = (paste_x, self._LINEWIDTH*3)
|
||||||
background.paste(top_line, paste_position, top_line)
|
background.paste(top_line, paste_position, top_line)
|
||||||
|
|
||||||
last_line = self.__bad_line(int(self.__GALLOWY*0.125))
|
last_line = self._bad_line(int(self._GALLOWY*0.125))
|
||||||
paste_x += int(self.__GALLOWY*0.30)
|
paste_x += int(self._GALLOWY*0.30)
|
||||||
background.paste(last_line, (paste_x, self.__LINEWIDTH*3), last_line)
|
background.paste(last_line, (paste_x, self._LINEWIDTH*3), last_line)
|
||||||
return background
|
return background
|
||||||
|
|
||||||
def __draw_letter_lines(self, word: str, guessed: list, misses: int):
|
def _draw_letter_lines(self, word: str, guessed: list, misses: int):
|
||||||
letter_width = self.__LETTERLINELENGTH+self.__LETTERLINEDISTANCE
|
letter_width = self._LETTERLINELENGTH+self._LETTERLINEDISTANCE
|
||||||
image_width = letter_width*len(word)
|
image_width = letter_width*len(word)
|
||||||
image_size = (image_width, self.__LETTERLINELENGTH+self.__LINEWIDTH*3)
|
image_size = (image_width, self._LETTERLINELENGTH+self._LINEWIDTH*3)
|
||||||
letter_lines = Image.new("RGBA", image_size, color=(0, 0, 0, 0))
|
letter_lines = Image.new("RGBA", image_size, color=(0, 0, 0, 0))
|
||||||
for i, letter in enumerate(word):
|
for i, letter in enumerate(word):
|
||||||
line = self.__bad_line(self.__LETTERLINELENGTH, True)
|
line = self._bad_line(self._LETTERLINELENGTH, True)
|
||||||
paste_x = i*(self.__LETTERLINELENGTH+self.__LETTERLINEDISTANCE)
|
paste_x = i*(self._LETTERLINELENGTH+self._LETTERLINEDISTANCE)
|
||||||
paste_position = (paste_x, self.__LETTERLINELENGTH)
|
paste_position = (paste_x, self._LETTERLINELENGTH)
|
||||||
letter_lines.paste(line, paste_position, line)
|
letter_lines.paste(line, paste_position, line)
|
||||||
if guessed[i]:
|
if guessed[i]:
|
||||||
letter_drawing = self.__bad_text(letter, True)
|
letter_drawing = self._bad_text(letter, True)
|
||||||
letter_width = self.__FONT.getsize(letter)[0]
|
letter_width = self._FONT.getsize(letter)[0]
|
||||||
letter_x = i*(self.__LETTERLINELENGTH+self.__LETTERLINEDISTANCE)
|
letter_x = i*(self._LETTERLINELENGTH+self._LETTERLINEDISTANCE)
|
||||||
letter_x -= (letter_width//2)
|
letter_x -= (letter_width//2)
|
||||||
letter_x += (self.__LETTERLINELENGTH//2)+(self.__LINEWIDTH*2)
|
letter_x += (self._LETTERLINELENGTH//2)+(self._LINEWIDTH*2)
|
||||||
letter_lines.paste(
|
letter_lines.paste(
|
||||||
letter_drawing,
|
letter_drawing,
|
||||||
(letter_x, 0),
|
(letter_x, 0),
|
||||||
letter_drawing
|
letter_drawing
|
||||||
)
|
)
|
||||||
elif misses == 6:
|
elif misses == 6:
|
||||||
letter_drawing = self.__bad_text(letter, True, (242, 66, 54))
|
letter_drawing = self._bad_text(letter, True, (242, 66, 54))
|
||||||
letter_width = self.__FONT.getsize(letter)[0]
|
letter_width = self._FONT.getsize(letter)[0]
|
||||||
letter_x = i*(self.__LETTERLINELENGTH+self.__LETTERLINEDISTANCE)
|
letter_x = i*(self._LETTERLINELENGTH+self._LETTERLINEDISTANCE)
|
||||||
letter_x -= (letter_width//2)
|
letter_x -= (letter_width//2)
|
||||||
letter_x += (self.__LETTERLINELENGTH//2)+(self.__LINEWIDTH*2)
|
letter_x += (self._LETTERLINELENGTH//2)+(self._LINEWIDTH*2)
|
||||||
letter_lines.paste(
|
letter_lines.paste(
|
||||||
letter_drawing,
|
letter_drawing,
|
||||||
(letter_x, 0),
|
(letter_x, 0),
|
||||||
@ -591,7 +591,7 @@ class DrawHangman():
|
|||||||
|
|
||||||
return letter_lines
|
return letter_lines
|
||||||
|
|
||||||
def __shortest_dist(self, positions: list, new_position: tuple):
|
def _shortest_dist(self, positions: list, new_position: tuple):
|
||||||
shortest_dist = math.inf
|
shortest_dist = math.inf
|
||||||
for i, j in positions:
|
for i, j in positions:
|
||||||
x_distance = abs(i-new_position[0])
|
x_distance = abs(i-new_position[0])
|
||||||
@ -601,18 +601,18 @@ class DrawHangman():
|
|||||||
shortest_dist = dist
|
shortest_dist = dist
|
||||||
return shortest_dist
|
return shortest_dist
|
||||||
|
|
||||||
def __draw_misses(self, guesses: list, word: str):
|
def _draw_misses(self, guesses: list, word: str):
|
||||||
background = Image.new("RGBA", (600, 400), color=(0, 0, 0, 0))
|
background = Image.new("RGBA", (600, 400), color=(0, 0, 0, 0))
|
||||||
pos = []
|
pos = []
|
||||||
for guess in guesses:
|
for guess in guesses:
|
||||||
if guess not in word:
|
if guess not in word:
|
||||||
placed = False
|
placed = False
|
||||||
while not placed:
|
while not placed:
|
||||||
letter = self.__bad_text(guess, True)
|
letter = self._bad_text(guess, True)
|
||||||
w, h = self.__FONT.getsize(guess)
|
w, h = self._FONT.getsize(guess)
|
||||||
x = random.randint(0, 600-w)
|
x = random.randint(0, 600-w)
|
||||||
y = random.randint(0, 400-h)
|
y = random.randint(0, 400-h)
|
||||||
if self.__shortest_dist(pos, (x, y)) > 70:
|
if self._shortest_dist(pos, (x, y)) > 70:
|
||||||
pos.append((x, y))
|
pos.append((x, y))
|
||||||
background.paste(letter, (x, y), letter)
|
background.paste(letter, (x, y), letter)
|
||||||
placed = True
|
placed = True
|
||||||
@ -628,27 +628,27 @@ class DrawHangman():
|
|||||||
channel: str
|
channel: str
|
||||||
The id of the channel the game is in.
|
The id of the channel the game is in.
|
||||||
"""
|
"""
|
||||||
self.__bot.log("Drawing hangman image")
|
self._bot.log("Drawing hangman image")
|
||||||
|
|
||||||
random.seed(game_id)
|
random.seed(game_id)
|
||||||
|
|
||||||
background = Image.open("gwendolyn/resources/paper.jpg")
|
background = Image.open("gwendolyn/resources/paper.jpg")
|
||||||
gallow = self.__draw_gallows()
|
gallow = self._draw_gallows()
|
||||||
man = self.__draw_man(misses, game_id)
|
man = self._draw_man(misses, game_id)
|
||||||
|
|
||||||
random.seed(game_id)
|
random.seed(game_id)
|
||||||
letter_line_parameters = [word, guessed, misses]
|
letter_line_parameters = [word, guessed, misses]
|
||||||
letter_lines = self.__draw_letter_lines(*letter_line_parameters)
|
letter_lines = self._draw_letter_lines(*letter_line_parameters)
|
||||||
|
|
||||||
random.seed(game_id)
|
random.seed(game_id)
|
||||||
misses = self.__draw_misses(list(guessed_letters), word)
|
misses = self._draw_misses(list(guessed_letters), word)
|
||||||
|
|
||||||
background.paste(gallow, (100, 100), gallow)
|
background.paste(gallow, (100, 100), gallow)
|
||||||
background.paste(man, (300, 210), man)
|
background.paste(man, (300, 210), man)
|
||||||
background.paste(letter_lines, (120, 840), letter_lines)
|
background.paste(letter_lines, (120, 840), letter_lines)
|
||||||
background.paste(misses, (600, 150), misses)
|
background.paste(misses, (600, 150), misses)
|
||||||
|
|
||||||
misses_text = self.__bad_text("MISSES", False)
|
misses_text = self._bad_text("MISSES", False)
|
||||||
misses_text_width = misses_text.size[0]
|
misses_text_width = misses_text.size[0]
|
||||||
background.paste(misses_text, (850-misses_text_width//2, 50), misses_text)
|
background.paste(misses_text, (850-misses_text_width//2, 50), misses_text)
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 610 KiB |
BIN
gwendolyn/resources/games/default_images/hangman.png
Normal file
BIN
gwendolyn/resources/games/default_images/hangman.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
@ -7,7 +7,7 @@
|
|||||||
"Blackjack double": "Adding another {} GwendoBucks to {}'s bet and drawing another card.",
|
"Blackjack double": "Adding another {} GwendoBucks to {}'s bet and drawing another card.",
|
||||||
"Blackjack different cards": "You can only split if your cards have the same value",
|
"Blackjack different cards": "You can only split if your cards have the same value",
|
||||||
"Blackjack split": "Splitting {}'s hand into 2. Adding their original bet to the second hand. You can use \"/blackjack hit/stand/double 1\" and \"/blackjack hit/stand/double 2\" to play the different hands.",
|
"Blackjack split": "Splitting {}'s hand into 2. Adding their original bet to the second hand. You can use \"/blackjack hit/stand/double 1\" and \"/blackjack hit/stand/double 2\" to play the different hands.",
|
||||||
"Blackjack started": "Blackjack game started. Use \"/blackjack bet [amount]\" to enter the game within the next 30 seconds.",
|
"Blackjack started": "Blackjack game started. Use the buttons or \"/blackjack bet [amount]\" to enter the game within the next 30 seconds.",
|
||||||
"Blackjack going on": "There's already a blackjack game going on. Try again in a few minutes.",
|
"Blackjack going on": "There's already a blackjack game going on. Try again in a few minutes.",
|
||||||
"Stock value": "The current {} stock is valued at **{}** GwendoBucks",
|
"Stock value": "The current {} stock is valued at **{}** GwendoBucks",
|
||||||
"Stock parameters": "You must give both a stock name and an amount of GwendoBucks you wish to spend.",
|
"Stock parameters": "You must give both a stock name and an amount of GwendoBucks you wish to spend.",
|
||||||
|
@ -45,68 +45,16 @@
|
|||||||
"name" : "cards",
|
"name" : "cards",
|
||||||
"description" : "Get a count of the cards used in blackjack games"
|
"description" : "Get a count of the cards used in blackjack games"
|
||||||
},
|
},
|
||||||
"blackjack_double" : {
|
|
||||||
"base" : "blackjack",
|
|
||||||
"name" : "double",
|
|
||||||
"description" : "Double your bet in blackjack",
|
|
||||||
"options" : [
|
|
||||||
{
|
|
||||||
"name" : "hand",
|
|
||||||
"description" : "The number of the hand to double your bet on",
|
|
||||||
"type" : 4,
|
|
||||||
"required" : "false"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"blackjack_hilo" : {
|
"blackjack_hilo" : {
|
||||||
"base" : "blackjack",
|
"base" : "blackjack",
|
||||||
"name" : "hilo",
|
"name" : "hilo",
|
||||||
"description" : "Get the current hi-lo value for the cards used in blackjack games"
|
"description" : "Get the current hi-lo value for the cards used in blackjack games"
|
||||||
},
|
},
|
||||||
"blackjack_hit" : {
|
|
||||||
"base" : "blackjack",
|
|
||||||
"name" : "hit",
|
|
||||||
"description" : "Hit on your hand in blackjack",
|
|
||||||
"options" : [
|
|
||||||
{
|
|
||||||
"name" : "hand",
|
|
||||||
"description" : "The number of the hand to hit if you've split",
|
|
||||||
"type" : 4,
|
|
||||||
"required" : "false"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"blackjack_shuffle" : {
|
"blackjack_shuffle" : {
|
||||||
"base" : "blackjack",
|
"base" : "blackjack",
|
||||||
"name" : "shuffle",
|
"name" : "shuffle",
|
||||||
"description" : "Shuffle the cards used in blackjack games"
|
"description" : "Shuffle the cards used in blackjack games"
|
||||||
},
|
},
|
||||||
"blackjack_split" : {
|
|
||||||
"base" : "blackjack",
|
|
||||||
"name" : "split",
|
|
||||||
"description" : "Split your hand in blackjack",
|
|
||||||
"options" : [
|
|
||||||
{
|
|
||||||
"name" : "hand",
|
|
||||||
"description" : "The number of the hand to split, in case you've already split once",
|
|
||||||
"type" : 4,
|
|
||||||
"required" : "false"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"blackjack_stand" : {
|
|
||||||
"base" : "blackjack",
|
|
||||||
"name" : "stand",
|
|
||||||
"description" : "Stand on your hand in blackjack",
|
|
||||||
"options" : [
|
|
||||||
{
|
|
||||||
"name" : "hand",
|
|
||||||
"description" : "The number of the hand to stand if you've split",
|
|
||||||
"type" : 4,
|
|
||||||
"required" : "false"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"blackjack_start" : {
|
"blackjack_start" : {
|
||||||
"base" : "blackjack",
|
"base" : "blackjack",
|
||||||
"name" : "start",
|
"name" : "start",
|
||||||
|
@ -15,6 +15,7 @@ from discord.ext import commands # Used to compare errors with command
|
|||||||
|
|
||||||
from discord_slash.context import SlashContext, ComponentContext
|
from discord_slash.context import SlashContext, ComponentContext
|
||||||
from gwendolyn.utils.util_functions import decode_id
|
from gwendolyn.utils.util_functions import decode_id
|
||||||
|
from gwendolyn.exceptions import InvalidInteraction
|
||||||
|
|
||||||
|
|
||||||
class EventHandler():
|
class EventHandler():
|
||||||
@ -64,7 +65,8 @@ class EventHandler():
|
|||||||
async def on_component(self, ctx: ComponentContext):
|
async def on_component(self, ctx: ComponentContext):
|
||||||
info = decode_id(ctx.custom_id)
|
info = decode_id(ctx.custom_id)
|
||||||
self.bot.log(f"Component action with info {info}")
|
self.bot.log(f"Component action with info {info}")
|
||||||
channel = ctx.origin_message.channel
|
channel = ctx.channel
|
||||||
|
author = str(ctx.author_id)
|
||||||
|
|
||||||
if info[0].lower() == "plex":
|
if info[0].lower() == "plex":
|
||||||
if info[1].lower() == "movie":
|
if info[1].lower() == "movie":
|
||||||
@ -73,25 +75,29 @@ class EventHandler():
|
|||||||
info[2],
|
info[2],
|
||||||
not isinstance(channel, discord.DMChannel)
|
not isinstance(channel, discord.DMChannel)
|
||||||
)
|
)
|
||||||
|
return
|
||||||
|
|
||||||
elif info[1].lower() == "show":
|
if info[1].lower() == "show":
|
||||||
await self.bot.other.plex.add_show(
|
await self.bot.other.plex.add_show(
|
||||||
ctx.origin_message,
|
ctx.origin_message,
|
||||||
info[2],
|
info[2],
|
||||||
not isinstance(channel, discord.DMChannel)
|
not isinstance(channel, discord.DMChannel)
|
||||||
)
|
)
|
||||||
|
return
|
||||||
|
|
||||||
elif info[0].lower() == "hangman":
|
elif info[0].lower() == "hangman" and author == info[2]:
|
||||||
if str(ctx.author_id) == info[2]:
|
if info[1].lower() == "guess":
|
||||||
if info[1].lower() == "guess":
|
await self.bot.games.hangman.guess(ctx, *info[3:])
|
||||||
await self.bot.games.hangman.guess(ctx, *info[3:])
|
return
|
||||||
elif info[1].lower() == "end":
|
|
||||||
await self.bot.games.hangman.stop(ctx, *info[3:])
|
if info[1].lower() == "end":
|
||||||
|
await self.bot.games.hangman.stop(ctx, *info[3:])
|
||||||
|
return
|
||||||
|
|
||||||
elif info[0].lower() == "connectfour":
|
elif info[0].lower() == "connectfour":
|
||||||
connect_four = self.bot.games.connect_four
|
connect_four = self.bot.games.connect_four
|
||||||
if info[1].lower() == "place" and str(ctx.author_id) == info[2]:
|
if info[1].lower() == "place" and author == info[2]:
|
||||||
params = [
|
await connect_four.place_piece(
|
||||||
ctx,
|
ctx,
|
||||||
info[3],
|
info[3],
|
||||||
int(info[4]),
|
int(info[4]),
|
||||||
@ -99,14 +105,20 @@ class EventHandler():
|
|||||||
int(info[7]),
|
int(info[7]),
|
||||||
ctx.author_id,
|
ctx.author_id,
|
||||||
int(info[8])
|
int(info[8])
|
||||||
]
|
)
|
||||||
await connect_four.place_piece(*params)
|
return
|
||||||
if info[1].lower() == "end":
|
|
||||||
if str(ctx.author_id) in [info[2], info[3]]:
|
if info[1].lower() == "end" and author in [info[2], info[3]]:
|
||||||
params = [
|
await connect_four.surrender(
|
||||||
ctx, [int(info[2]), int(info[3])], info[4], info[5]
|
ctx, [int(info[2]), int(info[3])], info[4], info[5]
|
||||||
]
|
)
|
||||||
await connect_four.surrender(*params)
|
return
|
||||||
|
|
||||||
|
elif info[0].lower() == "blackjack":
|
||||||
|
await self.bot.games.blackjack.decode_interaction(ctx, info[1:])
|
||||||
|
return
|
||||||
|
|
||||||
|
raise InvalidInteraction(ctx.custom_id, info)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user