5
\$\begingroup\$

I wrote a piece of blackjack code - for dealer dealing cards to himself (python). My question is how can I make it more efficient, faster and neater. I ran it for 1.000.000 iterations and it took 14.7 second which is quite slow, I believe.

import random
deck = 4 * [2,3,4,5,6,7,8,9,10,10,10,10,11]
random.shuffle(deck)


dealer_hand = []
dealer_hand.append(deck.pop(0))
dealer_hand.append(deck.pop(0))
hit_on_soft_17 = True
exit = False

while not exit:
    if sum(dealer_hand) == 17 and hit_on_soft_17:
        exit = True
        for i, card in enumerate(dealer_hand):
            if card == 11:
                exit = False
                dealer_hand.append(deck.pop(0))
                break
    if sum(dealer_hand) < 17:
        exit = False
        dealer_hand.append(deck.pop(0))
    if sum(dealer_hand) > 21: 
        exit = True
        for i, card in enumerate(dealer_hand):
            if card == 11:
                exit = False
                dealer_hand[i] = 1
                break
    if sum(dealer_hand) < 22 and sum(dealer_hand) > 17:
        exit = True

print(dealer_hand)
\$\endgroup\$
2
  • 5
    \$\begingroup\$ Welcome to Code Review! I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Feel free to edit and give it a different title if there is something more appropriate. \$\endgroup\$ Sep 16 at 15:54
  • 2
    \$\begingroup\$ Why would a dealer need to run this a million times? Things don't need to be faster than necessary for your use case. Popping the back of a list is faster than the front, although the gains may be negligible for such a tiny list. You're also summing the hand unnecessarily many times. You can store that in a variable. Again, sort of a micro optimization. \$\endgroup\$
    – ggorlen
    Sep 16 at 21:27

1 Answer 1

4
\$\begingroup\$

Testing

One million print(dealer_hand) statements executed in 14.7 seconds would be amazing. I'm running it as I write this answer and it has yet to finish. Please be clear on exactly what you are timing.

pop

deck.pop(0) is inefficient, since every element in the list must be copied to a new position. Consider instead using deck.pop() to remove a card from the other end of the deck. Alternately, you could use a deque, which allows O(1) insertion/removal from either end. Finally, you could create an iterator (it = iter(deck)) and use it to deal cards (next(it)); it doesn't actually remove the cards from deck, but iterates through them one-by-one.

sum

If the dealer holds [10, 8], then sum(dealer_hand) is computed 5 times in the loop! Each time, the same value is returned. You could compute this once at the top of the loop, dealer_total = sum(dealer_hand), and use that variable in subsequent tests.

Chained conditionals

if sum(dealer_hand) < 22 and sum(dealer_hand) > 17: could be written more succinctly and more efficiently as if 17 < sum(dealer_hand) < 22: Of course, if 17 < dealer_total < 22: is better still.

Reserved identifiers

exit() is a function ... or it was until you redefined it as a variable. Use a different identifier, like done, instead.

Bug

If hit_on_soft_17 = False, then once the dealer holds [10, 7] (or equivalent), none of the if conditions will be true, exit will remain False, and the loop will never end.

PEP-0008

  • hit_on_soft_17 is a global constant, and should be named in UPPER_CASE like HIT_ON_SOFT_17.
  • Commas should be followed by 1 space

Use functions

You should always organize your code into functions.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.