I began creating blackjack in python; however, I'm looking to simulate it, so I'm thinking I'm going to need to optimize it so that I don't just leave my computer running for three hours waiting for just 3000 games to be simulated. I'm very new to this stuff so feel free to be as hard on me as you want. I'm looking to understand the changes as well so that I can improve my code in the future.
import random
ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
suits = ['Spades', 'Hearts', 'Diamonds', 'Clubs']
global deck
global data
data = []
class Hand(object):
def __init__(self):
# start with two cards in hand.
self.cards = [(deck.deal_card()), (deck.deal_card())]
self.cardValue = 0
for c in self.cards:
if c.rank == 'A':
self.sοft = True
else:
self.soft = False
def value(self):
# setting two temporary variables and resetting them whenever the function is run.
temp = 0
self.aceCount = 0
anothertemp = False
for card in self.cards:
# if the card is an ace. This works because our value for aces is (1, 11), declared in our card class.
if card.value() == (1, 11):
# add an ace to the aceCount, aces needs to be accounted for at the end, because otherwise if you have an ace
# in the first few cards they will count as 11 even if that would cause a bust (hand value greater than 21).
self.aceCount += 1
self.soft = True
else:
# add the card if it's not an ace
temp += card.value()
# Now we add the aces to our hand value, one ace can count as 11 while the other counts as one, so this works just fine
for card in range(self.aceCount):
# checking if the ace would cause a bust
if temp + 11 > 21:
# if it does, its value is only 1
temp += 1
self.soft = False
else:
# if it doesn't, its value is 11
temp += 11
self.soft = True
anothertemp = True
# finally set the cardValue to the temp variable to correctly return our hand value.
# I don't think I actually need the 'temp' variable if I just set self.cardValue to 0 in the beginning.
self.soft = anothertemp
self.cardValue = temp
return str(self.cardValue)
# just makes it easier to draw cards
def draw(self):
c = deck.deal_card()
self.cards.append(c)
# set up a string for display and testing purposes
def __str__(self):
hand = []
for c in self.cards:
hand.append(str(c))
return str(hand)
class Player(object):
def __init__(self, strategy = 'none', name='Player', budget=100):
self.name = name
self.hand = Hand()
self.budget = budget
self.state = 'play'
self.handValue = self.hand.value()
self.handIsSoft = self.hand.soft
self.strategy = strategy
# strategy sheets
# Neither player has a soft hand
self.strategy1NoSoft = [
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'hit', 'stay'],
['hit', 'hit', 'hit', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay', 'stay']
]
# Only dealer has a soft hand
self.strategy1DealerSoft = [
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','stay'],
['hit','hit','stay','stay','stay'],
['hit','stay','stay','stay','stay'],
['stay','stay','stay','stay','stay'],
['stay','stay','stay','stay','stay'],
['stay','stay','stay','stay','stay'],
['stay','stay','stay','stay','stay'],
['stay','stay','stay','stay','stay'],
['stay','stay','stay','stay','stay'],
['stay','stay','stay','stay','stay']
]
# only the player has a soft hand
self.strategy1SelfSoft = [
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'hit', 'stay'],
['stay', 'stay', 'stay', 'stay', 'hit', 'hit', 'hit', 'hit', 'stay', 'hit', 'hit', 'hit', 'hit', 'stay', 'hit', 'hit', 'hit', 'stay'],
['stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','hit','hit','stay'],
['stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','hit','stay'],
['stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay','stay']
]
# both have a soft hand
self.strategy1BothSoft = [
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','hit'],
['hit','hit','hit','hit','stay'],
['stay','stay','stay','stay','stay',],
['stay','stay','stay','stay','stay',],
['stay','stay','stay','stay','stay',]
]
# add a card to hand
def hit(self):
self.hand.draw()
self.handValue = self.hand.value()
self.handIsSoft = self.hand.soft
# change the state to stay to stop all functions revolving around 'play' state
def stay(self):
self.state = 'stay'
# Check if the player busted
def check_bust(self):
# if your hand is over 21 you bust
if int(self.handValue) > 21:
self.state = 'bust'
return True
else:
return False
# Check the budget
def check_broke(self):
if int(self.budget < 1):
self.state = 'broke'
return True
else:
return False
# bid an amount
def bid(self):
self.broke = self.check_broke()
if self.broke:
print('You are out of money and cannot bid anymore')
else:
self.bidAmount = int(input('How much?\n'))
self.budget -= self.bidAmount
# the strategy function, checks for each soft/not soft hand and responds according to the strategy sheet
def strategy_one(self, dealer):
try:
if not self.handIsSoft and not dealer.handIsSoft:
if self.strategy1NoSoft[int(self.handValue)][int(dealer.handValue)] == 'hit':
self.hit()
elif self.strategy1NoSoft[int(self.handValue)][int(dealer.handValue)] == 'stay':
self.stay()
elif not self.handIsSoft and dealer.handIsSoft and dealer.handValue >= 12 and dealer.handValue <= 16:
if self.strategy1DealerSoft[int(self.handValue)][int(dealer.handValue) - 12] == 'hit':
self.hit()
elif self.strategy1DealerSoft[int(self.handValue)][int(dealer.handValue) - 12] == 'stay':
self.stay()
elif self.handIsSoft and not dealer.handISSoft and dealer.handValue >= 4:
if self.strategy1SelfSoft[int(self.handValue)][int(dealer.handValue) - 4] == 'hit':
self.hit()
elif self.strategy1SelfSoft[int(self.handValue)][int(dealer.handValue) - 4] == 'stay':
self.stay()
elif self.handIsSoft and dealer.handIsSoft and self.handValue >= 13 and dealer.handValue >= 12 and dealer.handValue <= 16:
if self.strategy1BothSoft[int(self.handValue)-13][int(dealer.handValue) - 12] == 'hit':
self.hit()
elif self.strategy1BothSoft[int(self.handValue)-13][int(dealer.handValue) - 12] == 'stay':
self.stay()
except:
self.stay()
def __str__(self):
# player string
return str('Name: ' + str(self.name) + '; Hand: ' + str(self.hand) + '; Budget: ' + str(self.budget) +
'; Hand Value: ' + str(self.handValue) + '; Hand is soft: ' + str(self.handIsSoft) + '; Busted: ' + str(self.check_bust()) + '; State: ' + str(self.state))
class Dealer(Player):
# initialize the dealer as a player
def __init__(self, name='Dealer'):
super().__init__('None',name)
# automate the dealer strategy. His strategy is nice and simple, hit below 17 stay at 17 or higher if the hand is 17 and soft, hit.
def dealer_strategy(self):
if int(self.handValue) < 17 or int(self.handValue) == 17 and self.handIsSoft:
self.hit()
else:
self.stay()
class Game(object):
def __init__(self, strategy = 'none', name = 'Player'):
self.dealer = Dealer()
self.deck = Deck()
self.deck.shuffle()
self.player = Player(strategy, name)
# this will be used later for running a simulation
global gameCount
gameCount += 1
# this will later be used to save the data of the game
def log_game(self):
data.append((gameCount, self.player.name, self.player.budget))
# run the game
def run(self):
print(self.player)
print(self.dealer)
if self.dealer.handValue == 21 and self.dealer.hand.aceCount > 0:
self.player.state = 'stay'
# while the player is not staying, busted or out of budget
while not self.player.state in ['stay', 'bust']:
# if a strategy is already selected then automate the strategy
if self.player.strategy == 'strategy1':
self.player.strategy_one(self.dealer)
print(self.player.hand, self.player.handValue)
else:
# get the move that the player wants to do
playerMove = input("what would you like to do? \n1) Hit \n2) Stay \n3) Bid\n")
# execute that move
if playerMove.upper() in ['1', 'HIT']:
self.player.hit()
print(self.player.hand)
print(self.player.handValue)
elif playerMove.upper() in ['2', 'STAY']:
self.player.stay()
elif playerMove.upper() in ['3', 'BID']:
self.player.bid()
else:
print('Not a valid move')
# Check if the player busted
self.player.check_bust()
if self.player.state == 'Bust':
print('Sorry, you busted.')
# Run the dealer simulation
while not self.dealer.state in ['stay', 'bust']:
self.dealer.dealer_strategy()
print(self.dealer.hand, self.dealer.handValue)
# Check bust for both
self.dealer.check_bust()
self.player.check_bust()
# Check win and loss
if ((self.player.handValue > self.dealer.handValue) and self.player.state != 'bust') or self.dealer.state == 'bust':
if self.player.state == 'bust' and self.dealer.state == 'bust':
print('You lost this round.')
else:
print('You won this round!')
else:
print('You lost this round.')
self.log_game()
print(data)
class Deck(object):
def __init__(self):
self.cards = []
# Create a deck, suit first then rank. This makes the deck follow what a brand new pack of cards looks like.
for suit in suits:
for rank in ranks:
c = Card(rank, suit)
self.cards.append(c)
def shuffle(self):
# use the random library to shuffle the cards
random.shuffle(self.cards)
def deal_card(self):
# Check if there are no cards left in the deck
if not self.cards:
raise Exception('No more cards in deck!')
# As long as there are cards left in the deck, pop one from the top of the deck out.
draw = self.cards.pop(0)
# return the drawn card so it can be added to the hand
return draw
# Set up another string so that the deck can be displayed whenever needed
def __str__(self):
deck = []
for c in self.cards:
deck.append(str(c))
return str(deck)
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def value(self):
# if the rank is is a king, queen, or jack, return 10 as the value of the card.
if self.rank in ['J', 'Q', 'K']:
return 10
# if the rank is an ace return 1 and 11. This will later let us choose whether we want the ace to be a one or eleven
if self.rank == 'A':
return 1, 11
else:
# if it's nothing special just return the number.
return int(self.rank)
def suit(self):
return self.suit
def rank(self):
return self.rank
# another string declaration to make it easy to display
def __str__(self):
return self.rank + '-' + self.suit
# <------------- TESTS ------------->
def cardTest():
# manually create 4 cards to make sure all the special things work
card1 = Card('9', 'Spades')
card2 = Card('3', 'Hearts')
card3 = Card('K', 'Clubs')
card4 = Card('A', 'Diamonds')
print(card1, card2, card3, card4)
def deckTest():
# create a deck, display the deck to test the str then shuffle and re-display to test the shuffle method
deck = Deck()
print('<----------- Before Shuffle ----------->')
print(deck)
deck.shuffle()
print('<----------- After Shuffle ----------->')
print(deck)
def handTest():
# It's safe to use global because the deck variable will be global, make a new hand and print it and its value.
global deck
deck = Deck()
deck.shuffle()
hand = Hand()
print(hand)
print(hand.value())
print(hand.soft)
def playerTest():
# make sure the player class and each of there moves are working.
player1 = Player('Andrea', 200)
player2 = Player()
player3 = Player('Rob')
player4 = Player('Tara', 300)
print(player1, '\n' + str(player2), '\n' + str(player3), '\n' + str(player4))
player4.hit()
print(player4)
def dealerTest():
dealer = Dealer()
print(str(dealer))
while dealer.state not in ['stay', 'bust']:
dealer.dealer_strategy()
print(str(dealer))
def gameTest():
print('<--------------------------------------- GAME --------------------------------------->')
global gameCount
gameCount = 0
game = Game('None') # 'None', 'strategy1'
game.run()
cardTest()
deckTest()
handTest()
playerTest()
dealerTest()
gameTest()