Card table key and equality methods, Blackjack basic pair and ace table logic

This commit is contained in:
Xevion
2021-01-24 18:55:37 -06:00
parent f2768df33f
commit 9bebdafd7c
+73 -8
View File
@@ -1,8 +1,7 @@
import logging import logging
import os import os
import re import re
from pprint import pprint from typing import Tuple, List, Dict
from typing import Tuple, Optional, List, Dict
import discord import discord
@@ -68,6 +67,14 @@ class Card(object):
match = re.match(r'^(\d{1,2}|[aqkj])([cdhs])$', self.raw_card, flags=re.IGNORECASE) match = re.match(r'^(\d{1,2}|[aqkj])([cdhs])$', self.raw_card, flags=re.IGNORECASE)
return match.group(1), match.group(2) return match.group(1), match.group(2)
@property
def table(self) -> str:
"""Gets the table key representation of this card. Dealer column, or (partial) Player Soft/Pair rows only."""
if self.isAce(): return 'A'
if self.isFace(): return 'T'
if self.isNumerical(): return self.symbol
return '?'
@classmethod @classmethod
def parse_cards(cls, card_str: discord.embeds.EmbedProxy) -> Tuple[int, List['Card']]: 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.""" """Given a EmbedProxy relating to a Blackjack Embed, finds a returns a list of Card objects and the value."""
@@ -82,18 +89,35 @@ class Card(object):
return int(value.group(1)), cards return int(value.group(1)), cards
def __eq__(self, other) -> bool:
if isinstance(other, Card):
return self.symbol == other.symbol
return False
def __repr__(self) -> str: 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]: def generate_table_structure(filename: str, column_keys: List[str], row_keys: List[str]) -> Dict[Tuple[str, str], str]:
data = {} """
Using a column and row header variable, create a dictionary representing the table.
:param filename: The file in the static directory to read data from.
:param column_keys: Keys in the column (y)
:param row_keys: Keys in the row (x)
:return: A dictionary with all keys as a tuple of the column and row key directing to the table's suggested play.
"""
logger.debug(f'Generating table structure with {filename}') logger.debug(f'Generating table structure with {filename}')
with open(os.path.join(constants.STATIC_DIR, filename)) as hard_file: with open(os.path.join(constants.STATIC_DIR, filename)) as hard_file:
raw_data = [list(line) for line in hard_file.read().split('\n') if len(line) > 0] raw_data = [list(line) for line in hard_file.read().split('\n') if len(line) > 0]
data = {}
# Iterate along the column keys and build the dictionary
for x, col_key in enumerate(column_keys): for x, col_key in enumerate(column_keys):
for y, row_key in enumerate(row_keys): for y, row_key in enumerate(row_keys):
data[(col_key, row_key)] = raw_data[y][x] data[(col_key, row_key)] = raw_data[y][x]
return data return data
@@ -115,17 +139,58 @@ class Blackjack(object):
PAIR = 2 PAIR = 2
@staticmethod @staticmethod
def choose(options: Tuple[bool, bool, bool, bool], cards: Tuple[str, Optional[str], Tuple[int, bool]], def choose(options: constants.PlayOptions, cards: List[Card], dealer: Card) -> str:
dealer: Tuple[str, Optional[str], Tuple[int, bool]]) -> str:
"""With all information presented, calculates the final decision.""" """With all information presented, calculates the final decision."""
def get_ideal_choice(self, cards: List[Card], choice: str = 'S' # Default is to stand
dealer: Tuple[str, Optional[str], Tuple[int, bool]]) -> str:
"""Gets the ideal baseline strategy choice based on my cards and the bot's cards.""" # Pair checking first
if len(cards) == 2:
if cards[0] == cards[1]:
symbol = {cards[0].table}
logger.debug(f'Pair of {cards[0]} found.')
choice = Blackjack.access(Blackjack.PAIR, (f'{symbol}-{symbol}', dealer.table))
if any(card.isAce() for card in cards):
sum_value = sum(card.value for card in cards if not card.isAce())
if 2 < sum_value < 9:
choice = Blackjack.access(Blackjack.SOFT, (f'A-{sum_value}', dealer.table))
else:
logger.error(
f'Sum of cards was a Soft {sum_value} ({", ".join(card.symbol.upper() for card in cards)})')
return Blackjack.convert_letter(choice)
def options_convert(self, choice: str, options: Tuple[bool, bool, bool, bool]): def options_convert(self, choice: str, options: Tuple[bool, bool, bool, bool]):
"""Converts the choice to the best possible choice based on the options given by the bot.""" """Converts the choice to the best possible choice based on the options given by the bot."""
new_choice = None
if choice == 'P':
if options.split: pass
else:
logger.warning(f'Poor options available for splitting. ({options})')
new_choice = 'S'
elif choice == 'D':
if options.double: pass
else:
logger.warning(f'Poor options available for doubling. ({options})')
new_choice = 'H'
elif choice == 'H':
if options.hit: pass
else:
logger.error(f'Hit option preferred but not possible? ({options})')
new_choice = 'S'
elif choice == 'S':
if options.stand: pass
else:
logger.error(f'Stand option preferred but not possible? ({options})')
new_choice = 'H'
if new_choice is not None:
logger.info(f'Option verification yielded a different method than originally selected: {choice} -> {new_choice}')
return new_choice
return choice
@classmethod @classmethod
def access(cls, table: int, key: Tuple[str, str]) -> str: def access(cls, table: int, key: Tuple[str, str]) -> str:
""" """