Representing Playing Cards
In addition to computational uses such as arrays of counters, lists can also be used to represent real-world โobjectsโ, in particular composite objects that have multiple components. Consider representing a deck of playing cards. A deck of cards is made up of many similar, but not identical, objects. Plainly a deck of cards cannot be easily represented by a single integer, string or floating point value because it has so many constituents, but an ordered list seems like a promising alternative.
Our representation: The simplest thing that could possibly work
A standard deck of playing cards consists of 52 unique cards. Each card combines one of 13 face values (Ace up to King) and one of four suits (Clubs, Diamonds, Hearts and Spades). If we agree on the ordering of face values and suits then we could just refer to each card by a number from 0 to 51, i.e. card 0 is the Ace of Clubs, card 3 is the 4 of Clubs, and card 50 is the Queen of Spades. If we follow standard contract bridge ordering we would number the cards as follows,
0 Ace Clubs 13 Ace Diamonds 26 Ace Hearts 39 Ace Spades
1 Two Clubs 14 Two Diamonds 27 Two Hearts 40 Two Spades
2 Three Clubs 15 Three Diamonds 28 Three Hearts 41 Three Spades
3 Four Clubs 16 Four Diamonds 29 Four Hearts 42 Four Spades
4 Five Clubs 17 Five Diamonds 30 Five Hearts 43 Five Spades
5 Six Clubs 18 Six Diamonds 31 Six Hearts 44 Six Spades
6 Seven Clubs 19 Seven Diamonds 32 Seven Hearts 45 Seven Spades
7 Eight Clubs 20 Eight Diamonds 33 Eight Hearts 46 Eight Spades
8 Nine Clubs 21 Nine Diamonds 34 Nine Hearts 47 Nine Spades
9 Ten Clubs 22 Ten Diamonds 35 Ten Hearts 48 Ten Spades
10 Jack Clubs 23 Jack Diamonds 36 Jack Hearts 49 Jack Spades
11 Queen Clubs 24 Queen Diamonds 37 Queen Hearts 50 Queen Spades
12 King Clubs 25 King Diamonds 38 King Hearts 51 King Spades
And a new ordered full deck of cards would be represented by the list,
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..., 47, 48, 49, 50, 51]
So in Python we can initialize a full deck just by saying:
deck = list(range(52))
since the range
command produces a range
object, which can be cast to the list above.
To work with the card numbers we'll need to be able to figure out the suit and face value of a card given its card number.
Suit from card number
To get the suit a card belongs to from its card number consider how the suits are grouped:
0 Clubs 13 Diamonds 26 Hearts 39 Spades
1 Clubs 14 Diamonds 27 Hearts 40 Spades
2 Clubs 15 Diamonds 28 Hearts 41 Spades
3 Clubs 16 Diamonds 29 Hearts 42 Spades
4 Clubs 17 Diamonds 30 Hearts 43 Spades
5 Clubs 18 Diamonds 31 Hearts 44 Spades
6 Clubs 19 Diamonds 32 Hearts 45 Spades
7 Clubs 20 Diamonds 33 Hearts 46 Spades
8 Clubs 21 Diamonds 34 Hearts 47 Spades
9 Clubs 22 Diamonds 35 Hearts 48 Spades
10 Clubs 23 Diamonds 36 Hearts 49 Spades
11 Clubs 24 Diamonds 37 Hearts 50 Spades
12 Clubs 25 Diamonds 38 Hearts 51 Spades
Solution 1
One way to get the suit from the card number is to test the card number to see which group of 13 (i.e. which suit) it is in.
if cardnum < 13 :
suit = 'Clubs'
elif cardnum < 26 :
suit = 'Diamonds'
elif cardnum < 39 :
suit = 'Hearts'
else :
suit = 'Spades'
Solution 2
An alternative is to get the suit from the card number by dividing the card number by 13. The result tells us which of the groups of 13 our card number is in:
suit = cardnum // 13
if suit == 0 :
suit = 'Clubs'
elif suit == 1 :
suit = 'Diamonds'
elif suit == 2 :
suit = 'Hearts'
else :
suit = 'Spades'
Face value from card number
To get the face value from the card number we need to look at where our card occurs within its group of 13.
0 Ace 13 Ace 26 Ace 39 Ace
1 Two 14 Two 27 Two 40 Two
2 Three 15 Three 28 Three 41 Three
3 Four 16 Four 29 Four 42 Four
4 Five 17 Five 30 Five 43 Five
5 Six 18 Six 31 Six 44 Six
6 Seven 19 Seven 32 Seven 45 Seven
7 Eight 20 Eight 33 Eight 46 Eight
8 Nine 21 Nine 34 Nine 47 Nine
9 Ten 22 Ten 35 Ten 48 Ten
10 Jack 23 Jack 36 Jack 49 Jack
11 Queen 24 Queen 37 Queen 50 Queen
12 King 25 King 38 King 51 King
Solution 1
We could do this as we did above by writing the logic directly:
if cardnum == 0 or cardnum == 13 or cardnum == 26 or cardnum == 39 :
face_value = 'Ace'
elif cardnum == 1 or cardnum == 14 or cardnum == 27 or cardnum == 40 :
face_value = 'Two'
elif cardnum == 2 or cardnum == 15 or cardnum == 28 or cardnum == 41 :
face_value = 'Three'
elif cardnum == 3 or cardnum == 16 or cardnum == 29 or cardnum == 42 :
face_value = 'Four'
elif cardnum == 4 or cardnum == 17 or cardnum == 30 or cardnum == 43 :
face_value = 'Five'
elif cardnum == 5 or cardnum == 18 or cardnum == 31 or cardnum == 44 :
face_value = 'Six'
elif cardnum == 6 or cardnum == 19 or cardnum == 32 or cardnum == 45 :
face_value = 'Seven'
elif cardnum == 7 or cardnum == 20 or cardnum == 33 or cardnum == 46 :
face_value = 'Eight'
elif cardnum == 8 or cardnum == 21 or cardnum == 34 or cardnum == 47 :
face_value = 'Nine'
elif cardnum == 9 or cardnum == 22 or cardnum == 35 or cardnum == 48 :
face_value = 'Ten'
elif cardnum == 10 or cardnum == 23 or cardnum == 36 or cardnum == 49 :
face_value = 'Jack'
elif cardnum == 11 or cardnum == 24 or cardnum == 37 or cardnum == 50 :
face_value = 'Queen'
else:
face_value = 'King'
Solution 2
Or we could do the same thing using list membership tests instead of equality tests,
if cardnum in [0, 13, 26, 39]:
face_value = 'Ace'
elif cardnum in [1, 14, 27, 40]:
face_value = 'Two'
elif cardnum in [2, 15, 28, 41]:
face_value = 'Three'
elif cardnum in [3, 16, 29, 42]:
face_value = 'Four'
elif cardnum in [4, 17, 30, 43]:
face_value = 'Five'
elif cardnum in [5, 18, 31, 44]:
face_value = 'Six'
elif cardnum in [6, 19, 32, 45]:
face_value = 'Seven'
elif cardnum in [7, 20, 33, 46]:
face_value = 'Eight'
elif cardnum in [8, 21, 34, 47]:
face_value = 'Nine'
elif cardnum in [9, 22, 35, 48]:
face_value = 'Ten'
elif cardnum in [10, 23, 36, 49]:
face_value = 'Jack'
elif cardnum in [11, 24, 37, 50]:
face_value = 'Queen'
else:
face_value = 'Kind'
Solution 3
We could also do it by considering the remainder when we divide the card number by 13.
face_value = cardnum % 13
if face_value == 0 :
face_value = 'Ace'
elif face_value == 1 :
face_value = 'Two'
elif face_value == 2 :
face_value = 'Three'
elif face_value == 3 :
face_value = 'Four'
elif face_value == 4 :
face_value = 'Five'
elif face_value == 5 :
face_value = 'Six'
elif face_value == 6 :
face_value = 'Seven'
elif face_value == 7 :
face_value = 'Eight'
elif face_value == 8 :
face_value = 'Nine'
elif face_value == 9 :
face_value = 'Ten'
elif face_value == 10 :
face_value = 'Jack'
elif face_value == 11 :
face_value = 'Queen'
else :
face_value = 'King'
Solution 4
A different and very compact modification to Solution 3 is to use list lookup instead of if
statements. We'll store the face values in a list, and then use the remainder of the division by 13 to access the appropriate entry in the list:
FACE_VALUES = ['Ace', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', \
'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King']
face_value = cardnum % 13
print('The face value of card number', cardnum, 'is', FACE_VALUES[face_value])
Study this code carefully. List lookups like this one are very convenient. They are brief and thus easier to read and understand than the lengthy if
cascades above, and the list is easier to edit than either of the previous code-intensive approaches.
Now that we can work with individual cards let's move on to working with hands, i.e. groups of cards.
Dealing a hand
How can we deal a hand of cards from our deck of cards?
Solution 1
One approach is to shuffle the cards in the deck and then to deal from the top of deck using the list method pop()
.
import random
# Create the deck of cards.
deck = list(range(52))
# Shuffle the deck of cards
for swaps in range(104):
posn1 = random.randint(0, 51)
posn2 = random.randint(0, 51)
# Swap the cards at posn1 and posn2
(deck[posn1], deck[posn2]) = (deck[posn2], deck[posn1])
# Create the empty hand.
hand = []
# Deal 5 cards from the deck into the hand.
for card in range(0, 5):
hand.append( deck.pop() )
(You can read about what is going on in the last line of the first for
loop in Tuples.)
Solution 2
An alternative is to select cards at random from inside the deck to add to the hand.
import random
deck = list(range(52))
hand = []
for card in range(5) :
# Choose the card to deal.
posn = random.randint(0, len(deck) - 1)
# Append the number at that position to the hand.
hand.append(deck[posn])
# Delete that card from the deck.
del(deck[posn])
Putting it together
Pulling selected pieces together we can write code like this,
import random
# Define handy string constants.
FACE_VALUES = ['Ace', 'Two', 'Three', 'Four', 'Five', 'Six',
'Seven', 'Eight', 'Nine', 'Ten', 'Jack',
'Queen', 'King']
SUITS = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
# Create deck of cards.
deck = list(range(52))
# Create empty hand.
hand = []
# Deal 5 cards into hand.
for deal in range(5) :
posn = random.randint(0, len(deck) - 1)
hand.append(deck[posn])
del(deck[posn])
# Display the cards in the hand.
for card in hand:
print(FACE_VALUES[card % 13], 'of', SUITS[card // 13])
Try it out! Then try making some changes to get a feel for how the code works.