Skip to content

Instantly share code, notes, and snippets.

@aquacash5
Last active July 24, 2016 19:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aquacash5/96c0389d44130d8785b2f70725847e2f to your computer and use it in GitHub Desktop.
Save aquacash5/96c0389d44130d8785b2f70725847e2f to your computer and use it in GitHub Desktop.
Blackjack Advisor for the Coder Radio Coding Challenge

Blackjack Advisor

Blackjack Advisor for Dealer Hits on Soft 17

Usage

usage: blackjack.py [-h] [-D] [-X] dealer player player [player ...]

Enter any card values for the dealer's card and player's hand

positional arguments:
  dealer           A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K
  player           A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K

optional arguments:
  -h, --help       show this help message and exit
  -D, --double     allow doubling
  -X, --surrender  allow surrendering

Arguments

Positional

Argument Description Required Acceptable Values Type
dealer The card the dealer has face up Yes A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K string
player The your first card Yes A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K list

Optional

Flag Argument Description Required Default
-h, --help None show this help message and exit No False
-D, --double None allow doubling No False
-X, --surrender None allow surrendering No False

Examples

blackjack.py A 8 8
Split
blackjack.py -X A 8 8
Surrender
blackjack.py 8 8 2
Hit
blackjack.py -D 8 8 2
Double
blackjack.py -XD 6 A 7
Double
blackjack.py -XD A 8 8 
Surrender
blackjack.py -XD 6 6 6
Split
blackjack.py -XD A A A A
Hit
import argparse
"""
Blackjack Advisor for Dealer Hits on Soft 17
This will always respond with Hit, Stand, or Split.
It may emit Surrender if it is most advisable and the move is legal.
It may emit Double if it is most advisable and the move is legal
Author: Kyle Bloom
"""
"""
strategy_chart
All values must have either 1 or 2 values.
If the first value is H, S, or P, it is ok if it is alone, for they are always legal moves.
If the first value is X or D, they must be accompanied with either H, S, or P, for X or D may not be legal moves.
"""
strategy_chart = {
'9': {'2': 'H', '3': 'DH', '4': 'DH', '5': 'DH', '6': 'DH', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'10': {'2': 'DH', '3': 'DH', '4': 'DH', '5': 'DH', '6': 'DH', '7': 'DH', '8': 'DH', '9': 'DH', '10': 'H', 'A': 'H'},
'11': {'2': 'DH', '3': 'DH', '4': 'DH', '5': 'DH', '6': 'DH', '7': 'DH', '8': 'DH', '9': 'DH', '10': 'DH', 'A': 'DH'},
'12': {'2': 'H', '3': 'H', '4': 'S', '5': 'S', '6': 'S', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'13': {'2': 'S', '3': 'S', '4': 'S', '5': 'S', '6': 'S', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'14': {'2': 'S', '3': 'S', '4': 'S', '5': 'S', '6': 'S', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'15': {'2': 'S', '3': 'S', '4': 'S', '5': 'S', '6': 'S', '7': 'H', '8': 'H', '9': 'H', '10': 'XH', 'A': 'XH'},
'16': {'2': 'S', '3': 'S', '4': 'S', '5': 'S', '6': 'S', '7': 'H', '8': 'H', '9': 'XH', '10': 'XH', 'A': 'XH'},
'17': {'2': 'S', '3': 'S', '4': 'S', '5': 'S', '6': 'S', '7': 'S', '8': 'S', '9': 'S', '10': 'S', 'A': 'XS'},
'2,A': {'2': 'H', '3': 'H', '4': 'H', '5': 'DH', '6': 'DH', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'3,A': {'2': 'H', '3': 'H', '4': 'H', '5': 'DH', '6': 'DH', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'4,A': {'2': 'H', '3': 'H', '4': 'DH', '5': 'DH', '6': 'DH', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'5,A': {'2': 'H', '3': 'H', '4': 'DH', '5': 'DH', '6': 'DH', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'6,A': {'2': 'H', '3': 'DH', '4': 'DH', '5': 'DH', '6': 'DH', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'7,A': {'2': 'S', '3': 'DS', '4': 'DS', '5': 'DS', '6': 'DS', '7': 'S', '8': 'S', '9': 'H', '10': 'H', 'A': 'H'},
'8,A': {'2': 'S', '3': 'S', '4': 'S', '5': 'S', '6': 'S', '7': 'S', '8': 'S', '9': 'S', '10': 'S', 'A': 'S'},
'9,A': {'2': 'S', '3': 'S', '4': 'S', '5': 'S', '6': 'S', '7': 'S', '8': 'S', '9': 'S', '10': 'S', 'A': 'S'},
'10,A': {'2': 'S', '3': 'S', '4': 'S', '5': 'S', '6': 'S', '7': 'S', '8': 'S', '9': 'S', '10': 'S', 'A': 'S'},
'2,2': {'2': 'P', '3': 'P', '4': 'P', '5': 'P', '6': 'P', '7': 'P', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'3,3': {'2': 'P', '3': 'P', '4': 'P', '5': 'P', '6': 'P', '7': 'P', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'4,4': {'2': 'H', '3': 'H', '4': 'H', '5': 'P', '6': 'P', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'5,5': {'2': 'DH', '3': 'DH', '4': 'DH', '5': 'DH', '6': 'DH', '7': 'DH', '8': 'DH', '9': 'DH', '10': 'H', 'A': 'H'},
'6,6': {'2': 'P', '3': 'P', '4': 'P', '5': 'P', '6': 'P', '7': 'H', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'7,7': {'2': 'P', '3': 'P', '4': 'P', '5': 'P', '6': 'P', '7': 'P', '8': 'H', '9': 'H', '10': 'H', 'A': 'H'},
'8,8': {'2': 'P', '3': 'P', '4': 'P', '5': 'P', '6': 'P', '7': 'P', '8': 'P', '9': 'P', '10': 'P', 'A': 'XP'},
'9,9': {'2': 'P', '3': 'P', '4': 'P', '5': 'P', '6': 'P', '7': 'S', '8': 'P', '9': 'P', '10': 'S', 'A': 'S'},
'10,10': {'2': 'S', '3': 'S', '4': 'S', '5': 'S', '6': 'S', '7': 'S', '8': 'S', '9': 'S', '10': 'S', 'A': 'S'},
'A,A': {'2': 'P', '3': 'P', '4': 'P', '5': 'P', '6': 'P', '7': 'P', '8': 'P', '9': 'P', '10': 'P', 'A': 'P'}
}
legend = {
'H': 'Hit',
'S': 'Stand',
'P': 'Split',
'D': 'Double',
'X': 'Surrender'
}
def card(string):
"""
Validates whether the entry is a valid card
Arguments:
string: the value to be validated
Returns:
str equal to the value of the card
Raises:
argparse.ArgumentError if the card is not valid
"""
string = string.upper() # sets the string to upper case
if string in ['K', 'Q', 'J']: # if the card is a [K]ing, [Q]ueen, or [J]ack
string = '10' # sets string to 10
if string in ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10']: # checks if the card is a legal card
return string # returns best representation of the card
raise argparse.ArgumentError('Invalid Card') # raises error if the card is invalid
def optimize_hand(hand):
"""
Optimizes hand for use with strategy_chart
Example:
['2', '5', 'A'] = ['7', 'A']
left hand side would normally advice Stay
right hand side advices Hit
Arguments:
hand: list of strings representing the cards in a hand
Return:
list of strings optimized for strategy_chart
"""
total_aces = len([card for card in hand if card == 'A']) # total number of aces
if total_aces == 0 or len(hand) == 2 and total_aces == 2: # return if there are no aces or if hand is a pair of aces
return hand
hand = sorted(hand, reverse=True) # sort hand in reverse alphabetical order so aces are first
alt_total = sum([int(card) for card in hand[1:] if card != 'A']) + total_aces - 1 # calculate alternate sum assuming other aces are 1
if alt_total <= 9: # if alt_total is 9 or less then it will be in strategy_chart
return [str(alt_total), 'A']
return hand # return hand otherwise
def hand_sum(hand):
"""
Sums all of the cards in the hand
Arguments:
hand: list of strings representing the cards in a hand
Returns:
integer with the sum of the hands
"""
non_ace_total = sum([int(card) for card in hand if card != 'A']) # total of all non ace cards
total_aces = len([card for card in hand if card == 'A']) # number of aces in hand
sum_options = [non_ace_total + (i * 11) + (total_aces - i) for i in range(total_aces + 1)] # list of possible sums
return sorted([option for option in sum_options if option <= 21], reverse=True)[0] # returns the greatest sum 21 or less
def digest_strategy(recommendation, surrender=False, double=False):
"""
Figures out what recommendation to show based on the rules of the game
Arguments:
recommendation: a string with the recommendation
surrender: a boolean indicating whether surrender is a legal move
double: a boolean indicating whether doubling is a legal move
Return:
str containing the recommended move
"""
if recommendation[0] == 'X' and surrender or recommendation[0] == 'D' and double: # checks if the first move is not legal
return legend[recommendation[0]] # sends initial move if option is legal
else:
return legend[recommendation[1]] # sends second move if option is illegal
def action(dealer, player_hand):
"""
Determines the action the player should take based on the cards dealt to them
Arguments:
dealer: a string representing the dealers card
player_hand: a list containing the players two cards
Return:
str containing the recommended move
"""
total_cards = len(player_hand) # Stores the original number of cards in players hand
player_hand = sorted(optimize_hand(player_hand))
player_sum = hand_sum(player_hand)
if ','.join(player_hand) in strategy_chart: # checks if the cards are in the strategy_chart
return strategy_chart[','.join(player_hand)][dealer]
elif str(player_sum) in strategy_chart:
if total_cards > 2:
# if the player has more than two cards then you only return the always legal move
# if this code is reached it will be impossible to get a Split response so we do not have to worry about that scenario
return strategy_chart[str(player_sum)][dealer][-1]
else:
return strategy_chart[str(player_sum)][dealer]
elif player_sum <= 8:
return 'H' # hand is not in strategy_chart and player_sum 8 or less
else:
return 'S' # hand is not in the strategy_chart player_sum 18 or more
if __name__ == '__main__':
parser = argparse.ArgumentParser(usage='%(prog)s [-h] [-D] [-X] dealer player player [player ...]',
description='Enter any card values for the dealer\'s card and player\'s hand')
parser.add_argument('-D', '--double',
action='store_true',
help='allow doubling')
parser.add_argument('-X', '--surrender',
action='store_true',
help='allow surrendering')
parser.add_argument('dealer',
type=card,
help='A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K')
parser.add_argument('first',
metavar='player',
type=card,
help='A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K')
parser.add_argument('others',
metavar='player',
nargs='+',
type=card,
help=argparse.SUPPRESS)
args = parser.parse_args()
player = list(args.others) # player is mutable list of players hand
player.append(args.first)
print(digest_strategy(action(args.dealer, player), surrender=args.surrender, double=args.double))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment