first commit

This commit is contained in:
RayExo
2026-07-12 15:16:07 +05:30
commit 2289301f26
915 changed files with 69400 additions and 0 deletions

186
bot/games/tictactoe.py Normal file
View File

@@ -0,0 +1,186 @@
from __future__ import annotations
from typing import Optional, ClassVar
import asyncio
import discord
from discord.ext import commands
from utils.emoji import ERROR
from .utils import DiscordColor, DEFAULT_COLOR
class Tictactoe:
"""
TicTacToe Game
"""
BLANK: ClassVar[str] = ""
CIRCLE: ClassVar[str] = ""
CROSS: ClassVar[str] = ERROR
_conversion: ClassVar[dict[str, tuple[int, int]]] = {
"1": (0, 0),
"2": (0, 1),
"3": (0, 2),
"4": (1, 0),
"5": (1, 1),
"6": (1, 2),
"7": (2, 0),
"8": (2, 1),
"9": (2, 2),
}
_WINNERS: ClassVar[tuple[tuple[tuple[int, int], ...], ...]] = (
((0, 0), (0, 1), (0, 2)),
((1, 0), (1, 1), (1, 2)),
((2, 0), (2, 1), (2, 2)),
((0, 0), (1, 0), (2, 0)),
((0, 1), (1, 1), (2, 1)),
((0, 2), (1, 2), (2, 2)),
((0, 0), (1, 1), (2, 2)),
((0, 2), (1, 1), (2, 0)),
)
def __init__(self, cross: discord.User, circle: discord.User) -> None:
self.cross = cross
self.circle = circle
self.board: list[list[str]] = [[self.BLANK for _ in range(3)] for _ in range(3)]
self.turn: discord.User = self.cross
self.winner: Optional[discord.User] = None
self.winning_indexes: list[tuple[int, int]] = []
self.message: Optional[discord.Message] = None
self._controls: list[str] = [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
]
self.emoji_to_player: dict[discord.User, str] = {
self.CIRCLE: self.circle,
self.CROSS: self.cross,
}
self.player_to_emoji: dict[str, discord.User] = {
v: k for k, v in self.emoji_to_player.items()
}
def board_string(self) -> str:
board = ""
for row in self.board:
board += "".join(row) + "\n"
return board
def make_embed(self, *, game_over: bool = False) -> discord.Embed:
embed = discord.Embed(color=discord.Color.random())
if game_over:
status = f"{self.winner.mention} won!" if self.winner else "Tie"
embed.description = f"**Game over**\n{status}"
else:
embed.description = f"**Turn:** {self.turn.mention}\n**Piece:** `{self.player_to_emoji[self.turn]}`"
return embed
def make_move(self, emoji: str, user: discord.User) -> list:
if emoji not in self._controls:
raise KeyError("Provided emoji is not one of the valid controls")
else:
x, y = self._conversion[emoji]
piece = self.player_to_emoji[user]
self.board[x][y] = piece
self.turn = self.circle if user == self.cross else self.cross
self._conversion.pop(emoji)
self._controls.remove(emoji)
return self.board
def is_game_over(self, *, tie: bool = False) -> bool:
for possibility in self._WINNERS:
row = [self.board[r][c] for r, c in possibility]
if len(set(row)) == 1 and row[0] != self.BLANK:
self.winner = self.emoji_to_player[row[0]]
self.winning_indexes = possibility
return True
if not self._controls or tie:
return True
return False
async def start(
self,
ctx: commands.Context[commands.Bot],
*,
timeout: Optional[float] = None,
embed_color: DiscordColor = discord.Color.random(),
remove_reaction_after: bool = False,
**kwargs,
) -> discord.Message:
"""
starts the tictactoe game
Parameters
----------
ctx : commands.Context
the context of the invokation command
timeout : Optional[float], optional
the timeout for when waiting, by default None
embed_color : DiscordColor, optional
the color of the game embed, by default DEFAULT_COLOR
remove_reaction_after : bool, optional
specifies whether or not to remove the move reaction each time, by default False
Returns
-------
discord.Message
returns the game emssage
"""
self.embed_color = embed_color
embed = self.make_embed()
self.message = await ctx.send(self.board_string(), embed=embed, **kwargs)
for button in self._controls:
await self.message.add_reaction(button)
while not ctx.bot.is_closed():
def check(reaction: discord.Reaction, user: discord.User) -> bool:
return (
str(reaction.emoji) in self._controls
and user == self.turn
and reaction.message == self.message
)
try:
reaction, user = await ctx.bot.wait_for(
"reaction_add", timeout=timeout, check=check
)
except asyncio.TimeoutError:
break
if self.is_game_over():
break
emoji = str(reaction.emoji)
self.make_move(emoji, user)
embed = self.make_embed()
if remove_reaction_after:
await self.message.remove_reaction(emoji, user)
await self.message.edit(content=self.board_string(), embed=embed)
embed = self.make_embed(game_over=True)
await self.message.edit(content=self.board_string(), embed=embed)
return self.message