mirror of
https://github.com/Xevion/unbelievaselfbot.git
synced 2025-12-10 18:09:05 -06:00
move client card parsing into Card.parse_cards method, fix access Union typing mistake, fix missing line in baseline_soft.dat, Card class documentation, remove unneeded Client vars/func
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from typing import Tuple, Optional, List, Union, Dict
|
||||
from pprint import pprint
|
||||
from typing import Tuple, Optional, List, Dict
|
||||
|
||||
import discord
|
||||
|
||||
from bot import exceptions, constants
|
||||
|
||||
@@ -10,10 +13,14 @@ logger.setLevel(constants.LOGGING_LEVEL)
|
||||
|
||||
|
||||
class Card(object):
|
||||
suits = {'h': 'Hearts', 's': 'Spades', 'c': 'Clubs', 'd': 'Diamonds'}
|
||||
symbols = {'10': 'Ten', '9': 'Nine', '8': 'Eight', '7': 'Seven', '6': 'Six', '5': 'Five', '4': 'Four', '3': 'Three',
|
||||
_suits = {'H': 'Hearts', 'S': 'Spades', 'C': 'Clubs', 'D': 'Diamonds'}
|
||||
_symbols = {'10': 'Ten', '9': 'Nine', '8': 'Eight', '7': 'Seven', '6': 'Six', '5': 'Five', '4': 'Four',
|
||||
'3': 'Three',
|
||||
'2': 'Two', 'k': 'King', 'q': 'Queen', 'j': 'Jack', 'a': 'Ace'}
|
||||
|
||||
EMOTE_REGEX = re.compile(r'<:([A-z0-9]+):\d+>')
|
||||
VALUE_PATTERN = re.compile(r'Value: (?:Soft )?(\d+)')
|
||||
|
||||
def __init__(self, card: str) -> None:
|
||||
if 4 <= len(card) <= 1:
|
||||
raise exceptions.InvalidCard(f'By length, {card} is invalid. Identifiers are 2 to 3 characters long.')
|
||||
@@ -22,7 +29,7 @@ class Card(object):
|
||||
self.symbol, self.suit = self.parts()
|
||||
|
||||
@property
|
||||
def value(self, safe: bool = True) -> int:
|
||||
def value(self, safe: bool = True, unsafe_default: int = 0) -> int:
|
||||
"""
|
||||
Attempts to determine the numerical value of the card.
|
||||
|
||||
@@ -33,25 +40,24 @@ class Card(object):
|
||||
raise exceptions.NoAceValue(
|
||||
'The Ace has multiple values (1 and 11) in Blackjack. Special handling is required.')
|
||||
return 0
|
||||
|
||||
if self.isFace():
|
||||
elif self.isFace():
|
||||
return 10
|
||||
|
||||
numeric_match = re.match(r'^(\d{1,2})', self.raw_card)
|
||||
if numeric_match is not None:
|
||||
return int(numeric_match.group(1))
|
||||
|
||||
if safe:
|
||||
elif self.isNumerical():
|
||||
return int(self.symbol)
|
||||
elif safe:
|
||||
raise exceptions.IndetermineValue('Could not determine the numeric value of this card.')
|
||||
return 0
|
||||
return unsafe_default
|
||||
|
||||
def isAce(self) -> bool:
|
||||
"""Returns whether or not the card is a Ace card."""
|
||||
return self.symbol == 'a'
|
||||
|
||||
def isNumerical(self) -> bool:
|
||||
"""Returns whether or not the card is numerical (not face or ace)."""
|
||||
return self.symbol.isnumeric()
|
||||
|
||||
def isFace(self) -> bool:
|
||||
"""Returns whether or not the card is a face card (Queen, King, or Jack)"""
|
||||
return self.symbol in ['q', 'k', 'j']
|
||||
|
||||
def parts(self) -> Tuple[str, str]:
|
||||
@@ -62,15 +68,29 @@ class Card(object):
|
||||
match = re.match(r'^(\d{1,2}|[aqkj])([cdhs])$', self.raw_card, flags=re.IGNORECASE)
|
||||
return match.group(1), match.group(2)
|
||||
|
||||
@classmethod
|
||||
def parse_cards(cls, card_str: discord.embeds.EmbedProxy) -> Tuple[int, List['Card']]:
|
||||
"""Given a EmbedProxy relating to a Blackjack Embed, finds a returns a list of Card objects and the value."""
|
||||
card_matches = re.finditer(cls.EMOTE_REGEX, card_str.value)
|
||||
value = re.search(cls.VALUE_PATTERN, card_str.value)
|
||||
|
||||
cards = []
|
||||
for card in card_matches:
|
||||
identifier = card.group(1)
|
||||
if identifier != 'cardBack':
|
||||
cards.append(Card(identifier))
|
||||
|
||||
return int(value.group(1)), cards
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Card({self.symbols[self.symbol]} of {self.suits[self.suit]})'
|
||||
return f'Card({self._symbols[self.symbol]} of {self._suits[self.suit]})'
|
||||
|
||||
|
||||
def generate_table_structure(filename: str, column_keys: List[str], row_keys: List[str]) -> Dict[Tuple[str, str], str]:
|
||||
data = {}
|
||||
logger.debug(f'Generating table structure with {filename}')
|
||||
with open(os.path.join(constants.STATIC_DIR, filename)) as hard_file:
|
||||
raw_data = [list(line) for line in hard_file.read().split('\n')]
|
||||
raw_data = [list(line) for line in hard_file.read().split('\n') if len(line) > 0]
|
||||
for x, col_key in enumerate(column_keys):
|
||||
for y, row_key in enumerate(row_keys):
|
||||
data[(col_key, row_key)] = raw_data[y][x]
|
||||
@@ -107,7 +127,7 @@ class Blackjack(object):
|
||||
"""Converts the choice to the best possible choice based on the options given by the bot."""
|
||||
|
||||
@classmethod
|
||||
def access(cls, table: Union[HARD, SOFT, PAIR], key: Tuple[str, str]) -> str:
|
||||
def access(cls, table: int, key: Tuple[str, str]) -> str:
|
||||
"""
|
||||
Access table data given a column and row key.
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ from typing import Optional, Tuple
|
||||
import discord
|
||||
from discord.ext.tasks import loop
|
||||
|
||||
from bot import constants, parsers, timings
|
||||
from bot import constants, parsers, timings, helpers
|
||||
from bot.blackjack import Card
|
||||
from bot.constants import PlayOptions
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
@@ -20,10 +21,10 @@ class UnbelievaClient(discord.Client):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.last_message = -1
|
||||
|
||||
# References
|
||||
self.bot_id, self.channel_id = bot_id, channel_id
|
||||
|
||||
self.channel: Optional[discord.TextChannel] = None
|
||||
self.bot: Optional[discord.User] = None
|
||||
|
||||
self.tasks = {
|
||||
'$work': timings.Cooldown(5 * 60 + 2),
|
||||
'$slut': timings.Cooldown(13 * 60 + 2),
|
||||
@@ -42,7 +43,6 @@ class UnbelievaClient(discord.Client):
|
||||
await self.wait_until_ready()
|
||||
|
||||
self.channel: discord.TextChannel = self.get_channel(self.channel_id)
|
||||
self.bot = self.bot_id
|
||||
self.check_task_available.start()
|
||||
ctypes.windll.kernel32.SetConsoleTitleW(f"#{self.channel.name}/{self.channel.guild.name}")
|
||||
logger.info(f'Connected to #{self.channel.name} in {self.channel.guild.name}')
|
||||
@@ -52,7 +52,7 @@ class UnbelievaClient(discord.Client):
|
||||
if message.channel != self.channel or message.author == self.user:
|
||||
return
|
||||
|
||||
if message.author.id == self.bot and len(message.embeds) > 0:
|
||||
if message.author.id == self.bot_id and len(message.embeds) > 0:
|
||||
embed = message.embeds[0]
|
||||
is_self = embed.author.name == f'{self.user.name}#{self.user.discriminator}'
|
||||
|
||||
@@ -73,8 +73,8 @@ class UnbelievaClient(discord.Client):
|
||||
# Handling for blackjack
|
||||
if embed.description.startswith('Type `hit` to draw another card'):
|
||||
options = self.parse_options(embed.description)
|
||||
my_cards = self.parse_cards(embed.fields[0])
|
||||
dealer_cards = self.parse_cards(embed.fields[1])
|
||||
my_cards = Card.parse_cards(embed.fields[0])
|
||||
dealer_cards = Card.parse_cards(embed.fields[1])
|
||||
print(options, my_cards, dealer_cards)
|
||||
|
||||
def parse_options(self, options_str: str) -> PlayOptions:
|
||||
@@ -86,24 +86,6 @@ class UnbelievaClient(discord.Client):
|
||||
# noinspection PyProtectedMember
|
||||
return PlayOptions._make(options)
|
||||
|
||||
def parse_cards(self, card_str: discord.embeds.EmbedProxy) -> Tuple[str, Optional[str], Tuple[int, bool]]:
|
||||
"""
|
||||
Parses a Embed Proxy
|
||||
|
||||
:param card_str:
|
||||
:return: [Card1, Card2?, [Value, isSoft]]
|
||||
"""
|
||||
emote_pattern = r'<:([A-z0-9]+):\d+>'
|
||||
value_pattern = r'Value: (Soft )?(\d+)'
|
||||
cards = list(re.finditer(emote_pattern, card_str.value))
|
||||
value = re.search(value_pattern, card_str.value)
|
||||
|
||||
c2: Optional[str]
|
||||
c1, c2 = cards[0].group(1), cards[1].group(1)
|
||||
c2 = c2 if c2 != 'cardBack' else None
|
||||
|
||||
return c1, c2, (int(value.group(2)), value.groups()[1] is None)
|
||||
|
||||
def handle_blackjack(self):
|
||||
embed = self.current_blackjack.embeds[0]
|
||||
options = self.parse_options(embed.description)
|
||||
@@ -114,9 +96,7 @@ class UnbelievaClient(discord.Client):
|
||||
|
||||
@loop(seconds=1)
|
||||
async def check_task_available(self):
|
||||
"""
|
||||
Loop to run tasks as soon as they are available.
|
||||
"""
|
||||
"""Loop to run tasks as soon as they are available."""
|
||||
await self.wait_until_ready()
|
||||
|
||||
for task, task_cooldown in self.tasks.items():
|
||||
@@ -132,13 +112,3 @@ class UnbelievaClient(discord.Client):
|
||||
# Activate the cooldowns
|
||||
task_cooldown.hit()
|
||||
self.command_cooldown.hit()
|
||||
|
||||
async def command_sleep(self):
|
||||
"""Sleep right before sending a command."""
|
||||
now = datetime.utcnow().timestamp()
|
||||
time_between = now - self.last_message
|
||||
wait_time = 6 - time_between
|
||||
|
||||
if wait_time > 0:
|
||||
logger.debug(f'Sleeping for {round(wait_time, 2)}s before sending a command...')
|
||||
await asyncio.sleep(wait_time)
|
||||
|
||||
@@ -3,5 +3,6 @@ SSSSDSSSSS
|
||||
DDDDDSSHHH
|
||||
HDDDDHHHHH
|
||||
HHDDDHHHHH
|
||||
HHDDDHHHHH
|
||||
HHHDDHHHHH
|
||||
HHHDDHHHHH
|
||||
|
||||
Reference in New Issue
Block a user