✨ Converted hangman to use buttons
This commit is contained in:
@ -29,11 +29,6 @@ class EventCog(commands.Cog):
|
|||||||
"""Log when an error occurs."""
|
"""Log when an error occurs."""
|
||||||
await self.bot.error_handler.on_error(method)
|
await self.bot.error_handler.on_error(method)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
|
||||||
async def on_reaction_add(self, reaction, user):
|
|
||||||
"""Handle when someone reacts to a message."""
|
|
||||||
await self.bot.event_handler.on_reaction_add(reaction, user)
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_component(self, ctx):
|
async def on_component(self, ctx):
|
||||||
"""Handle when someone reacts to a message."""
|
"""Handle when someone reacts to a message."""
|
||||||
|
@ -105,11 +105,6 @@ class ConnectFourCog(commands.Cog):
|
|||||||
"""Start a game of connect four against Gwendolyn."""
|
"""Start a game of connect four against Gwendolyn."""
|
||||||
await self.bot.games.connect_four.start(ctx, difficulty)
|
await self.bot.games.connect_four.start(ctx, difficulty)
|
||||||
|
|
||||||
@cog_ext.cog_subcommand(**params["connect_four_surrender"])
|
|
||||||
async def connect_four_surrender(self, ctx):
|
|
||||||
"""Surrender the game of connect four."""
|
|
||||||
await self.bot.games.connect_four.surrender(ctx)
|
|
||||||
|
|
||||||
|
|
||||||
class HangmanCog(commands.Cog):
|
class HangmanCog(commands.Cog):
|
||||||
"""Contains all the hangman commands."""
|
"""Contains all the hangman commands."""
|
||||||
|
@ -15,7 +15,13 @@ import discord # Used for typehints, discord.file and to check whether
|
|||||||
# the opponent in ConnectFour.start is a discord.User
|
# the opponent in ConnectFour.start is a discord.User
|
||||||
|
|
||||||
from PIL import Image, ImageDraw, ImageFont # Used by DrawConnectFour()
|
from PIL import Image, ImageDraw, ImageFont # Used by DrawConnectFour()
|
||||||
from discord_slash.context import SlashContext # Used for typehints
|
from discord_slash.context import SlashContext, ComponentContext # Used for
|
||||||
|
# typehints
|
||||||
|
from discord_slash.utils.manage_components import (create_button,
|
||||||
|
create_actionrow)
|
||||||
|
from discord_slash.model import ButtonStyle
|
||||||
|
|
||||||
|
from gwendolyn.utils import encode_id
|
||||||
|
|
||||||
ROWCOUNT = 6
|
ROWCOUNT = 6
|
||||||
COLUMNCOUNT = 7
|
COLUMNCOUNT = 7
|
||||||
@ -37,7 +43,7 @@ class ConnectFour():
|
|||||||
"""Initialize the class."""
|
"""Initialize the class."""
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.draw = DrawConnectFour(bot)
|
self.draw = DrawConnectFour(bot)
|
||||||
self.database_funcs = self.bot.database_funcs
|
self.get_name = self.bot.database_funcs.get_name
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
self.AISCORES = {
|
self.AISCORES = {
|
||||||
"middle": 3,
|
"middle": 3,
|
||||||
@ -49,9 +55,41 @@ class ConnectFour():
|
|||||||
"win": 10000,
|
"win": 10000,
|
||||||
"avoid losing": 100
|
"avoid losing": 100
|
||||||
}
|
}
|
||||||
self.REACTIONS = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣"]
|
|
||||||
# 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]):
|
||||||
"""
|
"""
|
||||||
@ -68,23 +106,18 @@ class ConnectFour():
|
|||||||
searches when minimaxing.
|
searches when minimaxing.
|
||||||
"""
|
"""
|
||||||
await self.bot.defer(ctx)
|
await self.bot.defer(ctx)
|
||||||
user = f"#{ctx.author.id}"
|
user = ctx.author.id
|
||||||
channel = str(ctx.channel_id)
|
channel = str(ctx.channel_id)
|
||||||
game = self.bot.database["connect 4 games"].find_one({"_id": channel})
|
|
||||||
|
|
||||||
started_game = False
|
started_game = False
|
||||||
can_start = True
|
can_start = True
|
||||||
|
|
||||||
if game is not None:
|
if isinstance(opponent, int):
|
||||||
send_message = self.bot.long_strings["Connect 4 going on"]
|
|
||||||
log_message = "There was already a game going on"
|
|
||||||
can_start = False
|
|
||||||
elif isinstance(opponent, int):
|
|
||||||
# Opponent is Gwendolyn
|
# Opponent is Gwendolyn
|
||||||
if opponent in range(1, 6):
|
if opponent in range(1, 6):
|
||||||
difficulty = int(opponent)
|
difficulty = int(opponent)
|
||||||
difficulty_text = f" with difficulty {difficulty}"
|
difficulty_text = f" with difficulty {difficulty}"
|
||||||
opponent = f"#{self.bot.user.id}"
|
opponent = self.bot.user.id
|
||||||
else:
|
else:
|
||||||
send_message = "Difficulty doesn't exist"
|
send_message = "Difficulty doesn't exist"
|
||||||
log_message = "They challenged a difficulty that doesn't exist"
|
log_message = "They challenged a difficulty that doesn't exist"
|
||||||
@ -96,7 +129,7 @@ class ConnectFour():
|
|||||||
# It was Gwendolyn
|
# It was Gwendolyn
|
||||||
difficulty = 3
|
difficulty = 3
|
||||||
difficulty_text = f" with difficulty {difficulty}"
|
difficulty_text = f" with difficulty {difficulty}"
|
||||||
opponent = f"#{self.bot.user.id}"
|
opponent = self.bot.user.id
|
||||||
else:
|
else:
|
||||||
send_message = "You can't challenge a bot!"
|
send_message = "You can't challenge a bot!"
|
||||||
log_message = "They tried to challenge a bot"
|
log_message = "They tried to challenge a bot"
|
||||||
@ -104,7 +137,7 @@ class ConnectFour():
|
|||||||
else:
|
else:
|
||||||
# Opponent is another player
|
# Opponent is another player
|
||||||
if ctx.author != opponent:
|
if ctx.author != opponent:
|
||||||
opponent = f"#{opponent.id}"
|
opponent = opponent.id
|
||||||
difficulty = 5
|
difficulty = 5
|
||||||
difficulty_text = ""
|
difficulty_text = ""
|
||||||
else:
|
else:
|
||||||
@ -117,26 +150,13 @@ class ConnectFour():
|
|||||||
players = [user, opponent]
|
players = [user, opponent]
|
||||||
random.shuffle(players)
|
random.shuffle(players)
|
||||||
|
|
||||||
new_game = {
|
self.draw.draw_image(channel, board, 0, [0,0], "", players)
|
||||||
"_id": channel,
|
|
||||||
"board": board,
|
|
||||||
"winner": 0,
|
|
||||||
"win direction": "",
|
|
||||||
"win coordinates": [0, 0],
|
|
||||||
"players": players,
|
|
||||||
"turn": 0,
|
|
||||||
"difficulty": difficulty
|
|
||||||
}
|
|
||||||
|
|
||||||
self.bot.database["connect 4 games"].insert_one(new_game)
|
gwendolyn_turn = (players[0] == self.bot.user.id)
|
||||||
|
|
||||||
self.draw.draw_image(channel)
|
|
||||||
|
|
||||||
gwendolyn_turn = (players[0] == f"#{self.bot.user.id}")
|
|
||||||
started_game = True
|
started_game = True
|
||||||
|
|
||||||
opponent_name = self.database_funcs.get_name(opponent)
|
opponent_name = self.get_name(f"#{opponent}")
|
||||||
turn_name = self.database_funcs.get_name(players[0])
|
turn_name = self.get_name(f"#{players[0]}")
|
||||||
|
|
||||||
started_text = "Started game against {}{}.".format(
|
started_text = "Started game against {}{}.".format(
|
||||||
opponent_name,
|
opponent_name,
|
||||||
@ -147,138 +167,202 @@ class ConnectFour():
|
|||||||
log_message = "They started a game"
|
log_message = "They started a game"
|
||||||
|
|
||||||
self.bot.log(log_message)
|
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:
|
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"
|
||||||
old_image = await ctx.channel.send(file=discord.File(file_path))
|
image_message = await ctx.send(
|
||||||
|
send_message, file=discord.File(file_path)
|
||||||
old_images_path = "gwendolyn/resources/games/old_images/"
|
)
|
||||||
old_image_path = f"{old_images_path}connect_four{ctx.channel_id}"
|
|
||||||
with open(old_image_path, "w") as file_pointer:
|
|
||||||
file_pointer.write(str(old_image.id))
|
|
||||||
|
|
||||||
|
board_string = self._encode_board_string(board)
|
||||||
if gwendolyn_turn:
|
if gwendolyn_turn:
|
||||||
await self._connect_four_ai(ctx)
|
await self._connect_four_ai(
|
||||||
|
ctx, board_string, players, difficulty, image_message.id
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
for reaction in self.REACTIONS:
|
buttons = []
|
||||||
await old_image.add_reaction(reaction)
|
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)
|
||||||
|
))
|
||||||
|
|
||||||
async def place_piece(self, ctx: Union[SlashContext, discord.Message],
|
custom_id = encode_id(
|
||||||
user: int, column: int):
|
[
|
||||||
|
"connectfour",
|
||||||
|
"end",
|
||||||
|
str(players[0]),
|
||||||
|
str(players[1]),
|
||||||
|
str(image_message.id)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
buttons.append(create_button(
|
||||||
|
style=ButtonStyle.red,
|
||||||
|
label="Surrender",
|
||||||
|
custom_id=custom_id
|
||||||
|
))
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await ctx.send(send_message)
|
||||||
|
|
||||||
|
async def place_piece(self, ctx: ComponentContext, board_string: str,
|
||||||
|
column: int, players: list[int], difficulty: int,
|
||||||
|
placer: int, message_id: int):
|
||||||
"""
|
"""
|
||||||
Place a piece on the board.
|
Place a piece on the board.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
|
||||||
ctx: Union[SlashContext, discord.Message]
|
|
||||||
The context of the command/reaction. ctx can only be
|
|
||||||
SlashContext if the piece is placed by the bot
|
|
||||||
immediately after the player starts the game with a
|
|
||||||
command. Otherwise, ctx will be the message the player
|
|
||||||
reacted to when placing a piece.
|
|
||||||
user: int
|
|
||||||
The player-number of the player placing the piece.
|
|
||||||
column: int
|
column: int
|
||||||
The column the player is placing the piece in.
|
The column the player is placing the piece in.
|
||||||
"""
|
"""
|
||||||
channel = str(ctx.channel.id)
|
|
||||||
connect_four_games = self.bot.database["connect 4 games"]
|
player_number = players.index(placer)+1
|
||||||
game = connect_four_games.find_one({"_id": channel})
|
user_name = self.get_name(f"#{placer}")
|
||||||
player_number = game["players"].index(user)+1
|
|
||||||
user_name = self.database_funcs.get_name(user)
|
|
||||||
placed_piece = False
|
placed_piece = False
|
||||||
|
|
||||||
if game is None:
|
board = self._decode_board_string(board_string)
|
||||||
send_message = "There's no game in this channel"
|
board = self._place_on_board(board, player_number, column)
|
||||||
log_message = "There was no game in the channel"
|
|
||||||
|
if board is None:
|
||||||
|
send_message = "There isn't any room in that column"
|
||||||
|
log_message = "There wasn't any room in the column"
|
||||||
else:
|
else:
|
||||||
board = game["board"]
|
turn = player_number % 2
|
||||||
board = self._place_on_board(board, player_number, column)
|
|
||||||
|
|
||||||
if board is None:
|
self.bot.log("Checking for win")
|
||||||
send_message = "There isn't any room in that column"
|
winner, win_direction, win_coordinates = self._is_won(board)
|
||||||
log_message = "There wasn't any room in the column"
|
|
||||||
|
if winner != 0:
|
||||||
|
game_won = True
|
||||||
|
send_message = "{} placed a piece in column {} and won. "
|
||||||
|
send_message = send_message.format(user_name, column+1)
|
||||||
|
log_message = f"{user_name} won"
|
||||||
|
win_amount = difficulty**2+5
|
||||||
|
if players[winner-1] != self.bot.user.id:
|
||||||
|
send_message += "Adding {} GwendoBucks to their account"
|
||||||
|
send_message = send_message.format(win_amount)
|
||||||
|
elif 0 not in board[0]:
|
||||||
|
game_won = True
|
||||||
|
send_message = "It's a draw!"
|
||||||
|
log_message = "The game ended in a draw"
|
||||||
else:
|
else:
|
||||||
updater = {"$set": {"board": board}}
|
game_won = False
|
||||||
connect_four_games.update_one({"_id": channel}, updater)
|
other_user_id = f"#{players[turn]}"
|
||||||
turn = (game["turn"]+1) % 2
|
other_user_name = self.get_name(other_user_id)
|
||||||
updater = {"$set": {"turn": turn}}
|
send_message = self.bot.long_strings["Connect 4 placed"]
|
||||||
connect_four_games.update_one({"_id": channel}, updater)
|
format_parameters = [user_name, column+1, other_user_name]
|
||||||
|
send_message = send_message.format(*format_parameters)
|
||||||
|
log_message = "They placed the piece"
|
||||||
|
|
||||||
self.bot.log("Checking for win")
|
gwendolyn_turn = (players[turn] == self.bot.user.id)
|
||||||
won, win_direction, win_coordinates = self._is_won(board)
|
|
||||||
|
|
||||||
if won != 0:
|
placed_piece = True
|
||||||
game_won = True
|
|
||||||
updater = {"$set": {"winner": won}}
|
|
||||||
connect_four_games.update_one({"_id": channel}, updater)
|
|
||||||
updater = {"$set": {"win direction": win_direction}}
|
|
||||||
connect_four_games.update_one({"_id": channel}, updater)
|
|
||||||
updater = {"$set": {"win coordinates": win_coordinates}}
|
|
||||||
connect_four_games.update_one({"_id": channel}, updater)
|
|
||||||
|
|
||||||
send_message = "{} placed a piece in column {} and won. "
|
|
||||||
send_message = send_message.format(user_name, column+1)
|
|
||||||
log_message = f"{user_name} won"
|
|
||||||
win_amount = int(game["difficulty"])**2+5
|
|
||||||
if game["players"][won-1] != f"#{self.bot.user.id}":
|
|
||||||
send_message += "Adding {} GwendoBucks to their account"
|
|
||||||
send_message = send_message.format(win_amount)
|
|
||||||
elif 0 not in board[0]:
|
|
||||||
game_won = True
|
|
||||||
send_message = "It's a draw!"
|
|
||||||
log_message = "The game ended in a draw"
|
|
||||||
else:
|
|
||||||
game_won = False
|
|
||||||
other_user_id = game["players"][turn]
|
|
||||||
other_user_name = self.database_funcs.get_name(
|
|
||||||
other_user_id)
|
|
||||||
send_message = self.bot.long_strings["Connect 4 placed"]
|
|
||||||
format_parameters = [user_name, column+1, other_user_name]
|
|
||||||
send_message = send_message.format(*format_parameters)
|
|
||||||
log_message = "They placed the piece"
|
|
||||||
|
|
||||||
gwendolyn_turn = (
|
|
||||||
game["players"][turn] == f"#{self.bot.user.id}")
|
|
||||||
|
|
||||||
placed_piece = True
|
|
||||||
|
|
||||||
await ctx.channel.send(send_message)
|
|
||||||
self.bot.log(log_message)
|
self.bot.log(log_message)
|
||||||
|
|
||||||
if placed_piece:
|
if placed_piece:
|
||||||
self.draw.draw_image(channel)
|
channel = str(ctx.channel)
|
||||||
|
self.draw.draw_image(
|
||||||
|
channel, board, winner, win_coordinates, win_direction, players)
|
||||||
|
|
||||||
old_images_path = "gwendolyn/resources/games/old_images/"
|
|
||||||
old_image_path = old_images_path + f"connect_four{channel}"
|
|
||||||
with open(old_image_path, "r") as file_pointer:
|
|
||||||
old_image_id = int(file_pointer.read())
|
|
||||||
old_image = await ctx.channel.fetch_message(old_image_id)
|
|
||||||
|
|
||||||
if old_image is not None:
|
|
||||||
await old_image.delete()
|
|
||||||
else:
|
|
||||||
self.bot.log("The old image was already deleted")
|
|
||||||
|
|
||||||
boards_path = "gwendolyn/resources/games/connect_four_boards/"
|
boards_path = "gwendolyn/resources/games/connect_four_boards/"
|
||||||
file_path = boards_path + f"board{channel}.png"
|
file_path = boards_path + f"board{channel}.png"
|
||||||
old_image = await ctx.channel.send(file=discord.File(file_path))
|
image_message = await ctx.channel.send(
|
||||||
|
send_message, file=discord.File(file_path))
|
||||||
|
|
||||||
if game_won:
|
if game_won:
|
||||||
self._end_game(channel)
|
self._end_game(winner, players, difficulty)
|
||||||
else:
|
else:
|
||||||
with open(old_image_path, "w") as file_pointer:
|
board_string = self._encode_board_string(board)
|
||||||
file_pointer.write(str(old_image.id))
|
|
||||||
if gwendolyn_turn:
|
if gwendolyn_turn:
|
||||||
await self._connect_four_ai(ctx)
|
await self._connect_four_ai(
|
||||||
|
ctx, board_string, players, difficulty, image_message.id
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
for reaction in self.REACTIONS:
|
buttons = []
|
||||||
await old_image.add_reaction(reaction)
|
for i in range(7):
|
||||||
|
custom_id = encode_id(
|
||||||
|
[
|
||||||
|
"connectfour",
|
||||||
|
"place",
|
||||||
|
str(players[turn]),
|
||||||
|
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)
|
||||||
|
))
|
||||||
|
|
||||||
async def surrender(self, ctx: SlashContext):
|
custom_id = encode_id(
|
||||||
|
[
|
||||||
|
"connectfour",
|
||||||
|
"end",
|
||||||
|
str(players[0]),
|
||||||
|
str(players[1]),
|
||||||
|
str(difficulty),
|
||||||
|
str(image_message.id)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
buttons.append(create_button(
|
||||||
|
style=ButtonStyle.red,
|
||||||
|
label="Surrender",
|
||||||
|
custom_id=custom_id
|
||||||
|
))
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await ctx.channel.send(send_message)
|
||||||
|
|
||||||
|
old_message = await ctx.channel.fetch_message(message_id)
|
||||||
|
await old_message.delete()
|
||||||
|
|
||||||
|
async def surrender(self, ctx: ComponentContext, players: list,
|
||||||
|
difficulty: int, message_id: int):
|
||||||
"""
|
"""
|
||||||
Surrender a connect four game.
|
Surrender a connect four game.
|
||||||
|
|
||||||
@ -287,38 +371,25 @@ class ConnectFour():
|
|||||||
ctx: SlashContext
|
ctx: SlashContext
|
||||||
The context of the command.
|
The context of the command.
|
||||||
"""
|
"""
|
||||||
await self.bot.defer(ctx)
|
|
||||||
channel = str(ctx.channel_id)
|
|
||||||
game = self.bot.database["connect 4 games"].find_one({"_id": channel})
|
|
||||||
|
|
||||||
if f"#{ctx.author.id}" in game["players"]:
|
loser_index = players.index(ctx.author.id)
|
||||||
loser_index = game["players"].index(f"#{ctx.author.id}")
|
winner_index = (loser_index+1) % 2
|
||||||
winner_index = (loser_index+1) % 2
|
winner_id = players[winner_index]
|
||||||
winner_id = game["players"][winner_index]
|
winner_name = self.get_name(f"#{winner_id}")
|
||||||
winner_name = self.database_funcs.get_name(winner_id)
|
|
||||||
|
|
||||||
send_message = f"{ctx.author.display_name} surrenders."
|
send_message = f"{ctx.author.display_name} surrenders."
|
||||||
send_message += f" This means {winner_name} is the winner."
|
send_message += f" This means {winner_name} is the winner."
|
||||||
if winner_id != f"#{self.bot.user.id}":
|
if winner_id != self.bot.user.id:
|
||||||
difficulty = int(game["difficulty"])
|
difficulty = int(difficulty)
|
||||||
reward = difficulty**2 + 5
|
reward = difficulty**2 + 5
|
||||||
send_message += f" Adding {reward} to their account"
|
send_message += f" Adding {reward} to their account"
|
||||||
|
|
||||||
await ctx.send(send_message)
|
await ctx.channel.send(send_message)
|
||||||
old_images_path = "gwendolyn/resources/games/old_images/"
|
|
||||||
old_image_path = old_images_path + f"connect_four{channel}"
|
|
||||||
with open(old_image_path, "r") as file_pointer:
|
|
||||||
old_image_id = int(file_pointer.read())
|
|
||||||
old_image = await ctx.channel.fetch_message(old_image_id)
|
|
||||||
|
|
||||||
if old_image is not None:
|
self._end_game(winner_index+1, players, difficulty)
|
||||||
await old_image.delete()
|
old_image = await ctx.channel.fetch_message(message_id)
|
||||||
else:
|
await old_image.delete()
|
||||||
self.bot.log("The old image was already deleted")
|
|
||||||
|
|
||||||
self._end_game(channel)
|
|
||||||
else:
|
|
||||||
await ctx.send("You can't surrender when you're not a player")
|
|
||||||
|
|
||||||
def _place_on_board(self, board: dict, player: int, column: int):
|
def _place_on_board(self, board: dict, player: int, column: int):
|
||||||
"""
|
"""
|
||||||
@ -355,17 +426,12 @@ class ConnectFour():
|
|||||||
else:
|
else:
|
||||||
return board
|
return board
|
||||||
|
|
||||||
def _end_game(self, channel: str):
|
def _end_game(self, winner: int, players: list, difficulty: int):
|
||||||
game = self.bot.database["connect 4 games"].find_one({"_id": channel})
|
|
||||||
|
|
||||||
winner = game["winner"]
|
|
||||||
if winner != 0:
|
if winner != 0:
|
||||||
if game["players"][winner-1] != f"#{self.bot.user.id}":
|
if players[winner-1] != f"#{self.bot.user.id}":
|
||||||
difficulty = int(game["difficulty"])
|
difficulty = int(difficulty)
|
||||||
reward = difficulty**2 + 5
|
reward = difficulty**2 + 5
|
||||||
self.bot.money.addMoney(game["players"][winner-1], reward)
|
self.bot.money.addMoney(f"#{players[winner-1]}", reward)
|
||||||
|
|
||||||
self.database_funcs.delete_game("connect 4 games", channel)
|
|
||||||
|
|
||||||
def _is_won(self, board: dict):
|
def _is_won(self, board: dict):
|
||||||
won = 0
|
won = 0
|
||||||
@ -441,20 +507,19 @@ class ConnectFour():
|
|||||||
|
|
||||||
return won, win_direction, win_coordinates
|
return won, win_direction, win_coordinates
|
||||||
|
|
||||||
async def _connect_four_ai(self, ctx: SlashContext):
|
async def _connect_four_ai(self,
|
||||||
|
ctx: Union[SlashContext, ComponentContext],
|
||||||
|
board_string: str, players: list,
|
||||||
|
difficulty: int, message_id: int):
|
||||||
def out_of_range(possible_scores: list):
|
def out_of_range(possible_scores: list):
|
||||||
allowed_range = max(possible_scores)*(1-0.1)
|
allowed_range = max(possible_scores)*(1-0.1)
|
||||||
more_than_one = len(possible_scores) != 1
|
more_than_one = len(possible_scores) != 1
|
||||||
return (min(possible_scores) <= allowed_range) and more_than_one
|
return (min(possible_scores) <= allowed_range) and more_than_one
|
||||||
|
|
||||||
channel = str(ctx.channel.id)
|
|
||||||
|
|
||||||
self.bot.log("Figuring out best move")
|
self.bot.log("Figuring out best move")
|
||||||
game = self.bot.database["connect 4 games"].find_one({"_id": channel})
|
|
||||||
|
|
||||||
board = game["board"]
|
board = self._decode_board_string(board_string)
|
||||||
player = game["players"].index(f"#{self.bot.user.id}")+1
|
player = players.index(self.bot.user.id)+1
|
||||||
difficulty = game["difficulty"]
|
|
||||||
|
|
||||||
scores = [-math.inf for _ in range(COLUMNCOUNT)]
|
scores = [-math.inf for _ in range(COLUMNCOUNT)]
|
||||||
for column in range(COLUMNCOUNT):
|
for column in range(COLUMNCOUNT):
|
||||||
@ -483,7 +548,15 @@ class ConnectFour():
|
|||||||
best_columns = [i for i, x in enumerate(scores) if x == highest_score]
|
best_columns = [i for i, x in enumerate(scores) if x == highest_score]
|
||||||
placement = random.choice(best_columns)
|
placement = random.choice(best_columns)
|
||||||
|
|
||||||
await self.place_piece(ctx, f"#{self.bot.user.id}", placement)
|
await self.place_piece(
|
||||||
|
ctx,
|
||||||
|
board_string,
|
||||||
|
placement,
|
||||||
|
players,
|
||||||
|
difficulty,
|
||||||
|
self.bot.user.id,
|
||||||
|
message_id
|
||||||
|
)
|
||||||
|
|
||||||
def _ai_calc_points(self, board: dict, player: int):
|
def _ai_calc_points(self, board: dict, player: int):
|
||||||
score = 0
|
score = 0
|
||||||
@ -683,7 +756,7 @@ class DrawConnectFour():
|
|||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
"""Initialize the class."""
|
"""Initialize the class."""
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.database_funcs = self.bot.database_funcs
|
self.get_name = self.bot.database_funcs.get_name
|
||||||
win_bar_alpha = 160
|
win_bar_alpha = 160
|
||||||
font_path = "gwendolyn/resources/fonts/futura-bold.ttf"
|
font_path = "gwendolyn/resources/fonts/futura-bold.ttf"
|
||||||
|
|
||||||
@ -729,19 +802,15 @@ class DrawConnectFour():
|
|||||||
# pylint: enable=invalid-name
|
# pylint: enable=invalid-name
|
||||||
|
|
||||||
# Draws the whole thing
|
# Draws the whole thing
|
||||||
def draw_image(self, channel: str):
|
def draw_image(self, channel: str, board: list, winner: int,
|
||||||
|
win_coordinates: list, win_direction: str, players: list):
|
||||||
"""
|
"""
|
||||||
Draw an image of the connect four board.
|
Draw an image of the connect four board.
|
||||||
|
|
||||||
*Parameters*
|
*Parameters*
|
||||||
------------
|
------------
|
||||||
channel: str
|
|
||||||
The id of the channel the game is in.
|
|
||||||
"""
|
"""
|
||||||
self.bot.log("Drawing connect four board")
|
self.bot.log("Drawing connect four board")
|
||||||
game = self.bot.database["connect 4 games"].find_one({"_id": channel})
|
|
||||||
|
|
||||||
board = game["board"]
|
|
||||||
|
|
||||||
image_size = (self.WIDTH, self.HEIGHT+self.BOTTOMBORDER)
|
image_size = (self.WIDTH, self.HEIGHT+self.BOTTOMBORDER)
|
||||||
background = Image.new("RGB", image_size, self.BACKGROUNDCOLOR)
|
background = Image.new("RGB", image_size, self.BACKGROUNDCOLOR)
|
||||||
@ -751,10 +820,10 @@ class DrawConnectFour():
|
|||||||
|
|
||||||
self._draw_pieces(drawer, board)
|
self._draw_pieces(drawer, board)
|
||||||
|
|
||||||
if game["winner"] != 0:
|
if winner != 0:
|
||||||
self._draw_win(background, game)
|
self._draw_win(background, win_coordinates, win_direction)
|
||||||
|
|
||||||
self._draw_footer(drawer, game)
|
self._draw_footer(drawer, players)
|
||||||
|
|
||||||
boards_path = "gwendolyn/resources/games/connect_four_boards/"
|
boards_path = "gwendolyn/resources/games/connect_four_boards/"
|
||||||
background.save(boards_path + f"board{channel}.png")
|
background.save(boards_path + f"board{channel}.png")
|
||||||
@ -897,7 +966,8 @@ class DrawConnectFour():
|
|||||||
}
|
}
|
||||||
drawer.ellipse(position, **piece_parameters)
|
drawer.ellipse(position, **piece_parameters)
|
||||||
|
|
||||||
def _draw_win(self, background: Image, game: dict):
|
def _draw_win(self, background: Image, win_coordinates: list,
|
||||||
|
win_direction: str):
|
||||||
"""
|
"""
|
||||||
Draw the bar that shows the winning pieces.
|
Draw the bar that shows the winning pieces.
|
||||||
|
|
||||||
@ -908,10 +978,9 @@ class DrawConnectFour():
|
|||||||
game: dict
|
game: dict
|
||||||
The game data.
|
The game data.
|
||||||
"""
|
"""
|
||||||
coordinates = game["win coordinates"]
|
|
||||||
start = self.BORDER + self.GRIDBORDER
|
start = self.BORDER + self.GRIDBORDER
|
||||||
start_x = start + self.PIECEGRIDSIZE[0]*coordinates[1]
|
start_x = start + self.PIECEGRIDSIZE[0]*win_coordinates[1]
|
||||||
start_y = start + self.PIECEGRIDSIZE[1]*coordinates[0]
|
start_y = start + self.PIECEGRIDSIZE[1]*win_coordinates[0]
|
||||||
diagonal_length = (
|
diagonal_length = (
|
||||||
(math.sqrt(
|
(math.sqrt(
|
||||||
(self.PIECEGRIDSIZE[0]*4-self.GRIDBORDER-self.BORDER)**2+
|
(self.PIECEGRIDSIZE[0]*4-self.GRIDBORDER-self.BORDER)**2+
|
||||||
@ -922,7 +991,7 @@ class DrawConnectFour():
|
|||||||
size_ratio = self.PIECEGRIDSIZE[1]/self.PIECEGRIDSIZE[0]
|
size_ratio = self.PIECEGRIDSIZE[1]/self.PIECEGRIDSIZE[0]
|
||||||
diagonal_angle = math.degrees(math.atan(size_ratio))
|
diagonal_angle = math.degrees(math.atan(size_ratio))
|
||||||
|
|
||||||
if game["win direction"] == "h":
|
if win_direction == "h":
|
||||||
image_size = (self.PIECEGRIDSIZE[0]*4, self.PIECEGRIDSIZE[1])
|
image_size = (self.PIECEGRIDSIZE[0]*4, self.PIECEGRIDSIZE[1])
|
||||||
win_bar = Image.new("RGBA", image_size, (0, 0, 0, 0))
|
win_bar = Image.new("RGBA", image_size, (0, 0, 0, 0))
|
||||||
win_drawer = ImageDraw.Draw(win_bar)
|
win_drawer = ImageDraw.Draw(win_bar)
|
||||||
@ -942,7 +1011,7 @@ class DrawConnectFour():
|
|||||||
win_drawer.ellipse(draw_position[1], fill=self.WINBARWHITE)
|
win_drawer.ellipse(draw_position[1], fill=self.WINBARWHITE)
|
||||||
win_drawer.rectangle(draw_position[2], fill=self.WINBARWHITE)
|
win_drawer.rectangle(draw_position[2], fill=self.WINBARWHITE)
|
||||||
|
|
||||||
elif game["win direction"] == "v":
|
elif win_direction == "v":
|
||||||
image_size = (self.PIECEGRIDSIZE[0], self.PIECEGRIDSIZE[1]*4)
|
image_size = (self.PIECEGRIDSIZE[0], self.PIECEGRIDSIZE[1]*4)
|
||||||
win_bar = Image.new("RGBA", image_size, (0, 0, 0, 0))
|
win_bar = Image.new("RGBA", image_size, (0, 0, 0, 0))
|
||||||
win_drawer = ImageDraw.Draw(win_bar)
|
win_drawer = ImageDraw.Draw(win_bar)
|
||||||
@ -962,7 +1031,7 @@ class DrawConnectFour():
|
|||||||
win_drawer.ellipse(draw_position[1], fill=self.WINBARWHITE)
|
win_drawer.ellipse(draw_position[1], fill=self.WINBARWHITE)
|
||||||
win_drawer.rectangle(draw_position[2], fill=self.WINBARWHITE)
|
win_drawer.rectangle(draw_position[2], fill=self.WINBARWHITE)
|
||||||
|
|
||||||
elif game["win direction"] == "r":
|
elif win_direction == "r":
|
||||||
image_width = int(self.PIECEGRIDSIZE[0]*diagonal_length)
|
image_width = int(self.PIECEGRIDSIZE[0]*diagonal_length)
|
||||||
image_size = (image_width, self.PIECEGRIDSIZE[1])
|
image_size = (image_width, self.PIECEGRIDSIZE[1])
|
||||||
win_bar = Image.new("RGBA", image_size, (0, 0, 0, 0))
|
win_bar = Image.new("RGBA", image_size, (0, 0, 0, 0))
|
||||||
@ -1001,7 +1070,7 @@ class DrawConnectFour():
|
|||||||
start_x -= 90
|
start_x -= 90
|
||||||
start_y -= 100
|
start_y -= 100
|
||||||
|
|
||||||
elif game["win direction"] == "l":
|
elif win_direction == "l":
|
||||||
image_width = int(self.PIECEGRIDSIZE[0]*diagonal_length)
|
image_width = int(self.PIECEGRIDSIZE[0]*diagonal_length)
|
||||||
image_size = (image_width, self.PIECEGRIDSIZE[1])
|
image_size = (image_width, self.PIECEGRIDSIZE[1])
|
||||||
win_bar = Image.new("RGBA", image_size, (0, 0, 0, 0))
|
win_bar = Image.new("RGBA", image_size, (0, 0, 0, 0))
|
||||||
@ -1040,16 +1109,16 @@ class DrawConnectFour():
|
|||||||
win_bar_image = Image.new("RGBA", mask.size, color=self.WINBARCOLOR)
|
win_bar_image = Image.new("RGBA", mask.size, color=self.WINBARCOLOR)
|
||||||
background.paste(win_bar_image, (start_x, start_y), mask)
|
background.paste(win_bar_image, (start_x, start_y), mask)
|
||||||
|
|
||||||
def _draw_footer(self, drawer: ImageDraw, game: dict):
|
def _draw_footer(self, drawer: ImageDraw, players: list):
|
||||||
if game["players"][0] == "Gwendolyn":
|
if players[0] == "Gwendolyn":
|
||||||
player1 = "Gwendolyn"
|
player1 = "Gwendolyn"
|
||||||
else:
|
else:
|
||||||
player1 = self.database_funcs.get_name(game["players"][0])
|
player1 = self.get_name(f"#{players[0]}")
|
||||||
|
|
||||||
if game["players"][1] == "Gwendolyn":
|
if players[1] == "Gwendolyn":
|
||||||
player2 = "Gwendolyn"
|
player2 = "Gwendolyn"
|
||||||
else:
|
else:
|
||||||
player2 = self.database_funcs.get_name(game["players"][1])
|
player2 = self.get_name(f"#{players[1]}")
|
||||||
|
|
||||||
circle_height = self.HEIGHT - self.BORDER
|
circle_height = self.HEIGHT - self.BORDER
|
||||||
circle_height += (self.BOTTOMBORDER+self.BORDER)//2 - self.TEXTSIZE//2
|
circle_height += (self.BOTTOMBORDER+self.BORDER)//2 - self.TEXTSIZE//2
|
||||||
|
@ -164,9 +164,6 @@ class Hangman():
|
|||||||
ctx: SlashContext
|
ctx: SlashContext
|
||||||
The context of the command.
|
The context of the command.
|
||||||
"""
|
"""
|
||||||
for msg_id in messages:
|
|
||||||
msg = await ctx.channel.fetch_message(msg_id)
|
|
||||||
await msg.delete()
|
|
||||||
|
|
||||||
self.bot.log("Deleting old messages")
|
self.bot.log("Deleting old messages")
|
||||||
|
|
||||||
@ -175,6 +172,10 @@ class Hangman():
|
|||||||
file_path = f"{boards_path}hangman_board{game_id}.png"
|
file_path = f"{boards_path}hangman_board{game_id}.png"
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
|
|
||||||
|
for msg_id in messages:
|
||||||
|
msg = await ctx.channel.fetch_message(msg_id)
|
||||||
|
await msg.delete()
|
||||||
|
|
||||||
async def guess(self, ctx: ComponentContext, guess: str, word: str,
|
async def guess(self, ctx: ComponentContext, guess: str, word: str,
|
||||||
guessed_letters: str, game_id: str, *messages: list[str]):
|
guessed_letters: str, game_id: str, *messages: list[str]):
|
||||||
"""
|
"""
|
||||||
@ -189,9 +190,6 @@ class Hangman():
|
|||||||
guess: str
|
guess: str
|
||||||
The guess.
|
The guess.
|
||||||
"""
|
"""
|
||||||
for msg_id in messages:
|
|
||||||
msg = await ctx.channel.fetch_message(msg_id)
|
|
||||||
await msg.delete()
|
|
||||||
|
|
||||||
misses = 0
|
misses = 0
|
||||||
guessed_letters += guess
|
guessed_letters += guess
|
||||||
@ -293,6 +291,10 @@ class Hangman():
|
|||||||
|
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
|
|
||||||
|
for msg_id in messages:
|
||||||
|
msg = await ctx.channel.fetch_message(msg_id)
|
||||||
|
await msg.delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,11 +140,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"connect_four_surrender" : {
|
|
||||||
"base" : "connect_four",
|
|
||||||
"name" : "surrender",
|
|
||||||
"description" : "Surrender the game of connect four"
|
|
||||||
},
|
|
||||||
"downloading" : {
|
"downloading" : {
|
||||||
"name" : "downloading",
|
"name" : "downloading",
|
||||||
"description" : "See current downloads for Plex",
|
"description" : "See current downloads for Plex",
|
||||||
|
@ -7,8 +7,6 @@ Classes used to handle bot events and errors.
|
|||||||
ErrorHandler
|
ErrorHandler
|
||||||
"""
|
"""
|
||||||
import traceback # Used to get the traceback of errors
|
import traceback # Used to get the traceback of errors
|
||||||
import sys # Used to get traceback when the specific error is not
|
|
||||||
# available
|
|
||||||
|
|
||||||
import discord # Used to init discord.Game and discord.Status, as well
|
import discord # Used to init discord.Game and discord.Status, as well
|
||||||
# as compare errors to discord errors and as typehints
|
# as compare errors to discord errors and as typehints
|
||||||
@ -16,7 +14,7 @@ from discord.ext import commands # Used to compare errors with command
|
|||||||
# errors
|
# errors
|
||||||
|
|
||||||
from discord_slash.context import SlashContext, ComponentContext
|
from discord_slash.context import SlashContext, ComponentContext
|
||||||
from gwendolyn.utils.util_functions import emoji_to_command, decode_id
|
from gwendolyn.utils.util_functions import decode_id
|
||||||
|
|
||||||
|
|
||||||
class EventHandler():
|
class EventHandler():
|
||||||
@ -63,24 +61,9 @@ class EventHandler():
|
|||||||
log_message = f"{ctx.author.display_name} ran {full_command}"
|
log_message = f"{ctx.author.display_name} ran {full_command}"
|
||||||
self.bot.log(log_message, str(ctx.channel_id), level=25)
|
self.bot.log(log_message, str(ctx.channel_id), level=25)
|
||||||
|
|
||||||
async def on_reaction_add(self, reaction: discord.Reaction,
|
|
||||||
user: discord.User):
|
|
||||||
"""Take action if the reaction is on a command message."""
|
|
||||||
if not user.bot:
|
|
||||||
tests = self.bot.database_funcs
|
|
||||||
message = reaction.message
|
|
||||||
channel = message.channel
|
|
||||||
reacted_message = f"{user.display_name} reacted to a message"
|
|
||||||
self.bot.log(reacted_message, str(channel.id))
|
|
||||||
reaction_test_parameters = [message, f"#{str(user.id)}"]
|
|
||||||
|
|
||||||
if tests.connect_four_reaction_test(*reaction_test_parameters):
|
|
||||||
column = emoji_to_command(reaction.emoji)
|
|
||||||
params = [message, f"#{user.id}", column-1]
|
|
||||||
await self.bot.games.connect_four.place_piece(*params)
|
|
||||||
|
|
||||||
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}")
|
||||||
channel = ctx.origin_message.channel
|
channel = ctx.origin_message.channel
|
||||||
|
|
||||||
if info[0].lower() == "plex":
|
if info[0].lower() == "plex":
|
||||||
@ -105,6 +88,27 @@ class EventHandler():
|
|||||||
elif info[1].lower() == "end":
|
elif info[1].lower() == "end":
|
||||||
await self.bot.games.hangman.stop(ctx, *info[3:])
|
await self.bot.games.hangman.stop(ctx, *info[3:])
|
||||||
|
|
||||||
|
elif info[0].lower() == "connectfour":
|
||||||
|
connect_four = self.bot.games.connect_four
|
||||||
|
if info[1].lower() == "place" and str(ctx.author_id) == info[2]:
|
||||||
|
params = [
|
||||||
|
ctx,
|
||||||
|
info[3],
|
||||||
|
int(info[4]),
|
||||||
|
[int(info[5]), int(info[6])],
|
||||||
|
int(info[7]),
|
||||||
|
ctx.author_id,
|
||||||
|
int(info[8])
|
||||||
|
]
|
||||||
|
await connect_four.place_piece(*params)
|
||||||
|
if info[1].lower() == "end":
|
||||||
|
if str(ctx.author_id) in [info[2], info[3]]:
|
||||||
|
params = [
|
||||||
|
ctx, [int(info[2]), int(info[3])], info[4], info[5]
|
||||||
|
]
|
||||||
|
await connect_four.surrender(*params)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ErrorHandler():
|
class ErrorHandler():
|
||||||
"""
|
"""
|
||||||
@ -145,12 +149,8 @@ class ErrorHandler():
|
|||||||
|
|
||||||
async def on_error(self, method: str):
|
async def on_error(self, method: str):
|
||||||
"""Log when there's an error."""
|
"""Log when there's an error."""
|
||||||
error_type = sys.exc_info()[0]
|
exception = traceback.format_exc()
|
||||||
if error_type == discord.errors.NotFound:
|
|
||||||
self.bot.log("Deleted message before I could add all reactions")
|
|
||||||
else:
|
|
||||||
exception = traceback.format_exc()
|
|
||||||
|
|
||||||
exception_string = "".join(exception)
|
exception_string = "".join(exception)
|
||||||
log_messages = [f"exception in {method}", f"{exception_string}"]
|
log_messages = [f"exception in {method}", f"{exception_string}"]
|
||||||
self.bot.log(log_messages, level=40)
|
self.bot.log(log_messages, level=40)
|
||||||
|
@ -359,7 +359,7 @@ def encode_id(info: list):
|
|||||||
try:
|
try:
|
||||||
dec += (37**i) * BASE_37.index(letter.upper())
|
dec += (37**i) * BASE_37.index(letter.upper())
|
||||||
except ValueError:
|
except ValueError:
|
||||||
log_this("Could not encode letter {letter}", level=30)
|
log_this(f"Could not encode letter {letter}", level=30)
|
||||||
|
|
||||||
custom_id = []
|
custom_id = []
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user