first commit
This commit is contained in:
229
bot/cogs/commands/blackjack.py
Normal file
229
bot/cogs/commands/blackjack.py
Normal file
@@ -0,0 +1,229 @@
|
||||
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.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)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user