Reputation: 13
In Python is this:
def blackjack_check(hand): # hand is a tuple
winning_cards = [10,'Jack','Queen','King']
if hand[0] in winning_cards and hand[1] == 'Ace':
return True
elif hand[0] == 'Ace' and hand[1] in winning_cards:
return True
else:
return False
the same as this...?
def blackjack_check(hand): # hand is a tuple
winning_cards = [10,'Jack','Queen','King']
if (hand[0] in winning_cards and hand[1]=='Ace' or
hand[0] == 'Ace' and hand[1] in winning_cards):
return True
else:
return False
Can I use the second code block instead of the first? It would eliminate an extra elif statement and it just seems cleaner. My concern is how the 'and' and 'or' operators work. Are the two 'and' comparisons separate and the 'or' compares them? Is there an order of operations for 'and' and 'or'? I ran the code and it works both ways but I want to make sure I understand exactly how the operators work.
Upvotes: 1
Views: 364
Reputation: 30210
vaultah's answer addresses your actual question perfectly -- they deserve the upvote and checkmark.
But I think programming blackjack in every language you're learning is an excellent way to better understand it, so I threw together code to show a different way to implement the blackjack test. More for educational purposes than to actually answer your question:
# Note: sometimes an Ace should be 1, but when checking for blackjack you can always
# consider it 11
def card_value(c):
if isinstance(c, int):
return c
elif c in ['Jack', 'Queen', 'King']:
return 10
elif c == 'Ace':
return 11
def blackjack_check(hand):
hand_value = sum(card_value(c) for c in hand)
return hand_value == 21
print(blackjack_check((2, 10))) # False
print(blackjack_check((10, 10))) # False
print(blackjack_check((2, 'Ace'))) # False
print(blackjack_check(('King', 'Jack'))) # False
print(blackjack_check(('King', 'Ace'))) # True
print(blackjack_check(('Ace', 'Queen'))) # True
If I were to implement it today, Cards and Hands would be classes, and there'd be a Hand.is_blackjack()
method, like:
import random
class Card:
NAMES = {1: 'Ace', 11:'Jack', 12:'Queen', 13:'King'}
def __init__(self, pips, suit):
self.pips = pips
self.suit = suit
def __repr__(self):
name = Card.NAMES.get(self.pips, "%d" % self.pips)
return "%s of %s" % (name, self.suit)
def value(self, ace_hi=True):
# Handle Ace
if self.pips == 1:
return 11 if ace_hi else 1
return min(self.pips, 10)
class Hand(list):
def is_blackjack(self):
hand_value = sum(c.value() for c in self)
return hand_value == 21
CARDS = [Card(p,s) for p in range(1,14) for s in ['Spades', 'Hearts', 'Clubs', 'Diamonds']]
h = Hand(random.sample(CARDS, 2))
print("Hand:")
for c in h:
print(" %s" % c)
print("Blackjack? %s" % h.is_blackjack())
Examples:
Hand: Jack of Spades Ace of Spades Blackjack? True Hand: Queen of Spades 9 of Diamonds Blackjack? False
Sorry for the silly non-answer, but these are just different ideas to think about. Don't worry if they're over your head, you'll get there.
Upvotes: 2
Reputation: 46533
Yes, the second code block is equivalent to the first one. According to the documentation, or
has lower precedence than and
. It means that the if statement is evaluated as
if ((hand[0] in winning_cards and hand[1] == 'Ace') or
(hand[0] == 'Ace' and hand[1] in winning_cards)):
which is what you want.
You could return the result of that boolean expression to shorten the code:
def blackjack_check(hand):
winning_cards = [10, 'Jack', 'Queen', 'King']
return (hand[0] in winning_cards and hand[1] == 'Ace' or
hand[0] == 'Ace' and hand[1] in winning_cards)
Upvotes: 4
Reputation:
The usual advice is if you don't know, then your readers might not know either, and it would be better for everybody if you use parentheses.
if ((hand[0] in winning_cards and hand[1]=='Ace') or
(hand[0] == 'Ace' and hand[1] in winning_cards)):
although you might try other formulations, such as
if (any(card == 'Ace' for card in hand) and
any(card in winning_cards for card in hand)):
or writing a helper function and using
if hascard(hand, ('Ace',)) and hascard(hand, winning_cards):
Upvotes: 0