What is the best way to create a deck of cards?

Question:

I was thinking about making a deck of cards for a card game. I could make a list of all of the cards (I don’t really care about the suits), but I was wondering if there was a much easier way to do this.

cards = ['1','1','1','1'....]

I’m positive you could make a for loop to create 4 cards of the same value and add it to a list, but I was wondering if that was the best solution. I am not advanced enough to know about or create a Class which I have seen to be offered as other solutions, but I am open to explanations.

I have already made a dictionary defining the card values.

Asked By: Mushy0364

||

Answers:

I think you’re right, a for loop would get the job done but might not be the most elegant solution. I’ve never programmed in Python so I don’t know the exact syntax but I could give you a psuedo code rundown of a class that would get the job done.

Answered By: fatalError

I propose you a solution with a basic class usage.

First, let’s make a Card class:

class Card:
    def __init__(self, value, color):
        self.value = value
        self.color = color

Then, let’s make a list of colors:

colors = ['heart', 'diamonds', 'spades', 'clubs']

Finally, let’s build your deck with a list comprehension:

deck = [Card(value, color) for value in range(1, 14) for color in colors]

The Card class is only a wrapper, just to manipulate cards instead of tuples, which feels more natural.

In this current state, it’s almost equivalent to renaming the tuple type… Basically, it only consists in a constructor, __init__, that sets the attributes of the instance.

So when I call Card(value, color) in the list comprehension, so for example Card(11, 'spades'), a new instance of the Card class is created, which has its value attribute set to 11, and its color attribute set to 'spades'.

I recommend you read some tutorial about OOP for an in-depth understanding of the concepts.


Now, you can try and improve this idea, for instance by using a more detailed values list instead of the range(1, 14):

values = ['ace', '2', ..., 'king']
Answered By: Right leg
values = ['2','3','4','5','6','7','8','9','10','Jack','Queen','King','Ace']
suites = ['Hearts', 'Clubs', 'Diamonds', 'Spades']
deck = [[v + ' of ' + s,v] for s in suites for v in values]
Answered By: sgk525

Could also do this:

card_deck = []

for i in range(3,11):
    card_deck.extend([i]*4)

for c in ['Jack', 'Queen', 'King', 'Ace']:
    card_deck.extend([c]*4)
Answered By: Gene Burinsky

You can represent your deck as a list of tuples. Which is a lighter weight alternative to classes. In dynamic languages like python, you will often do this to avoid the boilerplate code incurred by defining your own classes.

import itertools
import random

vals = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'jack', 'queen', 'king', 'ace']
suits = ['spades', 'clubs', 'hearts', 'diamonds']

deck = list(itertools.product(vals, suits))

random.shuffle(deck)

for val, suit in deck:
    print('The %s of %s' % (val, suit))

You may wish to represent the card values by an integer, this could easily be achieved by altering the input list.

Answered By: Paul Rooney

This solution uses enum class (package enum34).

Two enum classes represent the Suit and the Number with a custom str function. The Card class takes a Suit + a Number

from enum import Enum          
from enum import unique        

@unique
class Suit(Enum):              
    Spade = 1
    Heart = 2
    Dimond = 3 
    Club = 4

    def __str__(self):
        return self.name

@unique
class Number(Enum):
    N1 = 1
    N2 = 2
    N3 = 3
    N4 = 4
    N5 = 5
    N6 = 6
    N7 = 7
    N8 = 8
    N9 = 9
    N10 = 10
    J = 11
    Q = 12
    K = 13 

    def __str__(self):
        if self.value <= 10:
            return str(self.value)          
        return self.name

class Card(object):
    def __init__(self, suit, number):
        self.suit = suit
        self.number = number

    def __str__(self):
        return '{} {}'.format(self.suit, self.number)

cards = [ Card(suit, number) for suit in Suit for number in Number ]
for card in cards:
    print card
Answered By: greedy52

Another approach can be done using namedtuple from collections module, like this example:

from collections import namedtuple

Card = namedtuple('Card', ['value', 'suit'])
suits = ['hearts', 'diamonds', 'spades', 'clubs']
cards = [Card(value, suit) for value in range(1, 14) for suit in suits]

And you can access to the values like this:

print(cards[0])
>>> Card(value=1, suit='hearts')
print(cards[0].value, cards[0].suit)
>>> 1 hearts
Answered By: Chiheb Nexus

First some utility functions:

import random
from random import shuffle

def RANKS(): return [ "Ace", "2", "3", "4", "5", "6", "7","8", "9", "10", "Jack", "Queen", "King" ]
def SUITS(): return [ "Clubs", "Diamonds", "Hearts", "Spades" ]

Then a Card class:

class Card:

    def __init__( self, rank, suit ):
        self.rank = rank
        self.suit = suit

    def __str__( self ):
        return self.rank + " of " + self.suit

Then a Deck class:

class Deck:

    def __init__( self ):
        self.contents = []
        self.contents = [ Card( rank, suit ) for rank in RANKS() for suit in SUITS() ]
        random.shuffle( self.contents )
Answered By: wasabi

This code makes a deck of 40 card with two for loops. The deck is made of 40 strings in witch the last caracter [-1] is the color among c b s d (coppe, bastoni, spade and denari in an italian type of card deck). As the 10th card got 2 digit for the value number it is a good practise to get the value of the card using [:-1] so that it will take 1,2,3… until 9 and 10 when there is this card with 2 digit.

class Cards:
    def __init__(self):
        self.deck = []                  # the deck is empty
        for n in range(1,11):           # numbers
            for c in "cbsd":            # colors
                self.deck.append(str(n) + c)  # a string for each card


deck = Cards()
deck.deck

output:

[‘1c’, ‘1b’, ‘1s’, ‘1d’, ‘2c’, ‘2b’, ‘2s’, ‘2d’, ‘3c’, ‘3b’, ‘3s’, ‘3d’, ‘4c’, ‘4b’, ‘4s’, ‘4d’, ‘5c’, ‘5b’, ‘5s’, ‘5d’, ‘6c’, ‘6b’, ‘6s’, ‘6d’, ‘7c’, ‘7b’, ‘7s’, ‘7d’, ‘8c’, ‘8b’, ‘8s’, ‘8d’, ‘9c’, ‘9b’, ‘9s’, ‘9d’, ’10c’, ’10b’, ’10s’, ’10d’]

Answered By: PythonProgrammi

I found a very useful tutorial for this:

https://projects.raspberrypi.org/en/projects/deck-of-cards

It may look childish but it contains some really good-quality code

The code it contains looks something like this:

class Card:

    """
    The Card class represents a single playing card and is initialised by passing a suit and number.
    """

    def __init__(self, suit, number):
        self._suit = suit
        self._number = number

    def __repr__(self):
        return self._number + " of " + self._suit

    @property
    def suit(self):

        """
        Gets or sets the suit of a card
        """

        return self._suit

    @suit.setter
    def suit(self, suit):
        if suit in ["hearts", "clubs", "diamonds", "spades"]:
            self._suit = suit
        else:
            print("That's not a suit!")

    @property
    def number(self):
        """
        Gets or sets the number of a card
        """
        return self._number

    @number.setter
    def number(self, number):
        valid = [str(n) for n in range(2,11)] + ["J", "Q", "K", "A"]
        if number in valid:
            self._number = number
        else:
            print("That's not a valid number")
Answered By: bps

"simple code, deck list with 52 card combinations":

card_type=['Diamond', 'Heart', 'Spade', 'Clover']
card_number=['1','2','3','4', '5', '6', '7', '8', '9', 'J', 'Q', 'K']
for c in card_type:
    ...:     for n in card_number:
    ...:         deck.append([c,n])

An option is to create one card for each rank and suit using "for loops" and then attributing values, and having all the data on a tuple for each card, and retaining all cards in a list.

I used the following code to create a deck of cards without using class: (please not that the values I had attributed were for a blackjack game, but you can change it as you please)

ranks = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
suits = ["Clubs", "Diamonds", "Hearts", "Spades"]
cards = []
def deck_of_cards():
    for rank in ranks:
        for suit in suits:
            a = rank
            b = suit
            if ranks.index(a) == 0:
                c = 11
            elif ranks.index(a) > 9:
                c = 10
            else:
                c = ranks.index(a) + 1
            new_card = (a, b, c)
            cards.append(new_card)

deck_of_cards()
Answered By: Luciana Marini
cards = ["Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"]

deck = []

suits = ["Spades", "Diamonds", "Clubs", "Hearts"]
suit = 0
for card in range(52):
    if card >= 13 * (suit + 1):
        suit += 1
    deck.append(str(cards[card % 13] + "|" + suits[suit]))
Answered By: user16007890

I had to simulate cards myself today. The other answers are (in my opinion) missing the simplicity and performance gained by using a good numeric representation.


Summary

Depending on your familiarity with bitwise operations and unicode this summary code may be self-explanatory — Go to the next section for a more detailed explanation.

A        = 1
J        = 11
Q        = 12
K        = 13
SPADES   = 0
HEARTS   = 1<<4
DIAMONDS = 2<<4
CLUBS    = 3<<4
RANK     = 0b00_1111  # Rank bitmask
SUIT     = 0b11_0000  # Suit bitmask

# "bitwise or" to compose numeric card representations
ace_of_spades = A | SPADES
ace_of_hearts = A | HEARTS
two_of_hearts = 2 | HEARTS

# "bitwise and" to decompose numeric card representations
assert ace_of_spades & RANK == A
assert ace_of_spades & SUIT == SPADES

# Rank and suit comparisons using bitwise operations
assert      ace_of_hearts & RANK == ace_of_spades & RANK
assert      two_of_hearts & SUIT == ace_of_hearts & SUIT
assert not (ace_of_hearts & SUIT == ace_of_spades & SUIT)

deck = [rank | suit
        for suit in range(SPADES, CLUBS+1, 16)
        for rank in range(A, K+1)]

Some utilities:

# Conversion to unicode
def card_to_unicode(card):
    # Unicode cards start at U+1F0A1. Suit order and the
    # gap of 16 between suits is the same as in this code
    code_point = 0x1F0A0 + card
    after_knight_code_point = card & RANK > J  # Skip Knight cards   ,   ,   ,  
    if after_knight_code_point:
        code_point += 1

    return chr(code_point)

assert card_to_unicode(ace_of_spades) == " "
assert card_to_unicode(ace_of_hearts) == " "
assert card_to_unicode(two_of_hearts) == " "
assert card_to_unicode(K | CLUBS    ) == " "

print(" ".join(card_to_unicode(card) for card in deck))
# Output:                                                                                                  
     

Detailed Walk-through

We’ll use a 6-bit number to represent each card. The most significant, left-most 2 bits will store the suit and the least-significant, right-most 4 bits will store the rank. I.e. card = 0bAA_BBB where AA are the suit bits and BBBB are the rank bits:

#          Binary           Decimal       Type  Note
#          0bAA_BBBB
A        = 0b00_0001 # =    1             Rank  Since the rank bits
#        = 0b00_0010   =    2             Rank  are the least-significant,
#        = 0b00_0011   =    3             Rank  each rank's numeric
#        = 0b00_0100   =    4             Rank  representation is simply
#        = 0b00_0101   =    5             Rank  a number between
#        = 0b00_0110   =    6             Rank  1 and 13.
#        = 0b00_0111   =    7             Rank  We use A, J, Q, K as
#        = 0b00_1000   =    8             Rank  aliases for ace, jack
#        = 0b00_1001   =    9             Rank  queen and king ranks'
#        = 0b00_1010   =   10             Rank  representations.
J        = 0b00_1011 # =   11             Rank
Q        = 0b00_1100 # =   12             Rank
K        = 0b00_1101 # =   13             Rank
SPADES   = 0b00_0000 # = 16*0 = 0<<4 = 0  Suit  Suit's numeric
HEARTS   = 0b01_0000 # = 16*1 = 1<<4      Suit  representations are
DIAMONDS = 0b10_0000 # = 16*2 = 2<<4      Suit  0 to 3 shifted left by
CLUBS    = 0b11_0000 # = 16*3 = 3<<4      Suit  4 bits

We can now use the "bitwise or" operation to compose cards’ numeric
representations:

ace_of_spades = A | SPADES
ace_of_hearts = A | HEARTS
two_of_hearts = 2 | HEARTS
# The binary representation is what we'd expect
king_of_clubs = ( K      #   0b00_1101
                | CLUBS) # | 0b11_0000
                         # -----------
assert      king_of_clubs == 0b11_1101

Inversely, we can decompose a card’s representation into rank and suit
using "bitwise and" operations with bitmasks:

RANK    = 0b00_1111  # Rank bits bitmask
SUIT    = 0b11_0000  # Suit bits bitmask
assert ace_of_spades & RANK == A
assert ace_of_spades & SUIT == SPADES

Using these bitwise operations we can now compare cards by both rank
and suit (as required by most card game programs):

assert      ace_of_hearts & RANK == ace_of_spades & RANK
assert      two_of_hearts & SUIT == ace_of_hearts & SUIT
assert not (ace_of_hearts & SUIT == ace_of_spades & SUIT)

A nice property of this representation is that it’s easy to convert
to the unicode characters for cards:

def card_to_unicode(card):
    # Playing cards are represented in unicode, starting at the code
    # point U+1F0A0. U+1F0A1 is the code point for the Ace of Spades   .
    # The next 14 code points including the ace represent all the
    # cards in spades. 14, because the Knight of Spades   -- included
    # in some Itailian, Spanish and Tarot packs -- has the code point
    # U+1F0A0 + 12. The Hearts', Diamands' and Clubs' cards are also
    # represented as sequences of 14 code points starting at
    # U+1F0A1 + 16, U+1F0A1 + 2*16 and U+1F0A1 + 3*16.
    #
    # Consequentially, we can find the unicode code point of a card by
    # a) using our numeric representation as an offset from the start
    # code point U+1F0A0 ...
    code_point = 0x1F0A0 + card
    # ... and b) skipping the Knight cards.
    after_knight_code_point = card & RANK > J
    if after_knight_code_point:
        code_point += 1

    return chr(code_point)

assert card_to_unicode(ace_of_spades) == " "
assert card_to_unicode(ace_of_hearts) == " "
assert card_to_unicode(two_of_hearts) == " "
assert card_to_unicode(king_of_clubs) == " "

In closing, we can create a full, sorted deck of cards using 2 nested range loops (condensing this to a single list comprehension is shown in the summary):

deck = []
for suit in range(SPADES, CLUBS+1, 16):  # Note the step size of 16
    for rank in range(A, K+1):
        card = rank | suit
        deck.append(card)

And print it to the standard output:

for card in deck:
    print(card_to_unicode(card), end=" ")
# Output:                                                                                                  
     

Additional Notes

  1. Jokers and other special cards can be represented by making use of the additional unused rank numbers 0, 14, 15. I suggest 2*16 + 14 and 3*16 + 14 for Jokers so card_to_unicode continues to work (see https://en.wikipedia.org/wiki/Playing_cards_in_Unicode#Playing_Cards_block_chart)
  2. Using representations other than numbers will reduce your performance by a significant constant factor
Answered By: Oscar
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.