Reputation: 167
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.
Upvotes: 9
Views: 82604
Reputation: 116
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.
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: š” š¢ š£ š¤ š„ š¦ š§ šØ š© šŖ š« š š® š± š² š³ š“ šµ š¶ š· šø š¹ šŗ š» š½ š¾ š š š š š
š š š š š š š š š š š š š š š š š š
š š š
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: š” š¢ š£ š¤ š„ š¦ š§ šØ š© šŖ š« š š® š± š² š³ š“ šµ š¶ š· šø š¹ šŗ š» š½ š¾ š š š š š
š š š š š š š š š š š š š š š š š š
š š š
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)Upvotes: 3
Reputation: 1
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]))
Upvotes: 0
Reputation: 1
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()
Upvotes: 0
Reputation: 1
"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])
Upvotes: 0
Reputation: 31
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")
Upvotes: 3
Reputation: 23463
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']
Upvotes: 0
Reputation: 9267
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
Upvotes: 9
Reputation: 39
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 )
Upvotes: 3
Reputation: 16740
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']
Upvotes: 17
Reputation: 21619
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.
Upvotes: 7
Reputation: 1405
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
Upvotes: 2
Reputation: 10223
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)
Upvotes: 0
Reputation: 132
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]
Upvotes: 4
Reputation: 345
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.
Upvotes: 0