# ╔══════════════════════════════════════════════════════════════════╗ # ║ ║ # ║ +-+-+-+-+-+-+-+-+ ║ # ║ |H|e|x|a|H|o|s|t| ║ # ║ +-+-+-+-+-+-+-+-+ ║ # ║ ║ # ║ © 2026 HexaHost — All Rights Reserved ║ # ║ ║ # ║ discord ── https://discord.gg/hexahost ║ # ║ github ── https://github.com/theoneandonlymace ║ # ║ ║ # ╚══════════════════════════════════════════════════════════════════╝ import discord from discord.ext import commands import os import random from typing import List, Tuple, Union from PIL import Image from utils.Tools import * from utils.cv2 import CV2, build_container from discord.ui import LayoutView, TextDisplay, Separator, Container class CV2(LayoutView): def __init__(self, title, *sections): super().__init__(timeout=None) items = [TextDisplay(f"**{title}**")] for s in sections: if s: items.append(Separator(visible=True)) items.append(TextDisplay(str(s))) self.add_item(build_container(*items)) CARDS_PATH = 'data/cards/' class Card: suits = ["clubs", "diamonds", "hearts", "spades"] def __init__(self, suit: str, value: int, down=False): self.suit = suit self.value = value self.down = down self.symbol = self.name[0].upper() @property def name(self) -> str: if self.value <= 10: return str(self.value) else: return { 11: 'jack', 12: 'queen', 13: 'king', 14: 'ace', }[self.value] @property def image(self): return ( f"{self.symbol if self.name != '10' else '10'}" \ f"{self.suit[0].upper()}.png" \ if not self.down else "red_back.png" ) def flip(self): self.down = not self.down return self def __str__(self) -> str: return f'{self.name.title()} of {self.suit.title()}' def __repr__(self) -> str: return str(self) class Blackjack(commands.Cog): def __init__(self, bot): self.bot = bot @staticmethod def hand_to_images(hand: List[Card]) -> List[Image.Image]: return [Image.open(os.path.join(CARDS_PATH, card.image)) for card in hand] @staticmethod def center(*hands: Tuple[Image.Image]) -> Image.Image: bg: Image.Image = Image.open(os.path.join(CARDS_PATH, 'table.png')) bg_center_x = bg.size[0] // 2 bg_center_y = bg.size[1] // 2 img_w = hands[0][0].size[0] img_h = hands[0][0].size[1] start_y = bg_center_y - (((len(hands) * img_h) + ((len(hands) - 1) * 15)) // 2) for hand in hands: start_x = bg_center_x - (((len(hand) * img_w) + ((len(hand) - 1) * 10)) // 2) for card in hand: bg.alpha_composite(card, (start_x, start_y)) start_x += img_w + 10 start_y += img_h + 15 return bg def output(self, name, *hands: Tuple[List[Card]]) -> None: self.center(*map(self.hand_to_images, hands)).save(f'data/{name}.png') @staticmethod def calc_hand(hand: List[Card]) -> int: non_aces = [c for c in hand if c.symbol != 'A'] aces = [c for c in hand if c.symbol == 'A'] total_sum = 0 for card in non_aces: if not card.down: if card.symbol in 'JQK': total_sum += 10 else: total_sum += card.value for card in aces: if not card.down: if total_sum <= 10: total_sum += 11 else: total_sum += 1 return total_sum @commands.hybrid_command(aliases=['bj', 'blackjacks'], help="Play a simple game of blackjack.", usage="blackjack") @blacklist_check() @ignore_check() @commands.cooldown(1, 3, commands.BucketType.user) async def blackjack(self, ctx: commands.Context): try: deck = [Card(suit, num) for num in range(2, 15) for suit in Card.suits] random.shuffle(deck) player_hand: List[Card] = [] dealer_hand: List[Card] = [] player_hand.append(deck.pop()) dealer_hand.append(deck.pop()) player_hand.append(deck.pop()) dealer_hand.append(deck.pop()) dealer_hand[1] = dealer_hand[1].flip() player_score = self.calc_hand(player_hand) dealer_score = self.calc_hand(dealer_hand) async def out_table(**kwargs) -> discord.Message: self.output(ctx.author.id, dealer_hand, player_hand) file = discord.File(f"data/{ctx.author.id}.png", filename=f"{ctx.author.id}.png") view = CV2(kwargs.get('title', 'Blackjack'), kwargs.get('description', '')) msg: discord.Message = await ctx.send(file=file, view=view) return msg def check(reaction: discord.Reaction, user: Union[discord.Member, discord.User]) -> bool: return all(( str(reaction.emoji) in ("🇸", "🇭"), user == ctx.author, user != self.bot.user, reaction.message == msg )) standing = False while True: player_score = self.calc_hand(player_hand) dealer_score = self.calc_hand(dealer_hand) if player_score == 21: result = ("Blackjack!", 'won') break elif player_score > 21: result = ("Player busts", 'lost') break msg = await out_table( title="Your Turn", description=f"Your hand: {player_score}\nDealer's hand: {dealer_score}" ) await msg.add_reaction("🇭") await msg.add_reaction("🇸") try: reaction, _ = await self.bot.wait_for('reaction_add', timeout=60, check=check) except asyncio.TimeoutError: await msg.delete() return if str(reaction.emoji) == "🇭": player_hand.append(deck.pop()) await msg.delete() continue elif str(reaction.emoji) == "🇸": standing = True break if standing: dealer_hand[1] = dealer_hand[1].flip() player_score = self.calc_hand(player_hand) dealer_score = self.calc_hand(dealer_hand) while dealer_score < 17: dealer_hand.append(deck.pop()) dealer_score = self.calc_hand(dealer_hand) if dealer_score == 21: result = ('Dealer blackjack', 'lost') elif dealer_score > 21: result = ("Dealer busts", 'won') elif dealer_score == player_score: result = ("Tie!", 'kept') elif dealer_score > player_score: result = ("You lose!", 'lost') elif dealer_score < player_score: result = ("You win!", 'won') color = ( discord.Color.red() if result[1] == 'lost' else discord.Color.green() if result[1] == 'won' else discord.Color.blue() ) try: await msg.delete() except: pass msg = await out_table( title=result[0], color=color, description=( f"**You {result[1]}**\nYour hand: {player_score}\n" + f"Dealer's hand: {dealer_score}" ) ) os.remove(f'data/{ctx.author.id}.png') except Exception as e: print(e)