JavaScript Playing Cards Part 1: Ranks and Values

Juha Lindstedt
4 min readOct 26, 2018

--

I’m currently rewriting my Deck of Cards project and decided to write about the logic behind virtual playing cards. This is the first post of the series.

How to represent cards in a deck

Deck of cards have 52 cards, 4 suits and values from 1–13. So let’s start with a loop from 0 to 51:

const cards = new Array(52);for (let i = 0; i < cards.length; i++) {
cards[i] = i;
}

Here we have a deck of cards. You probably think I’m crazy – just an array of numbers? But wait, we’ll just need some number magic. Before moving on, make sure you know couple of operators: Remainder (%) and Bitwise OR (|):

const getRank = (i) => i % 13;
const getValue = (i) => getRank(i) + 1;
const getSuit = (i) => i / 13 | 0; // floor

Let’s see what will these do:

cards.map(getRank) // [0...51]
cards.map(getValue) // [1...52]
cards.map(getSuit) // [0, 0, ..., 1, 1, ..., 2, 2, ..., 3, 3, ...]

Multiple decks

What if we want multiple decks?

const cards = new Array(52 * 3);for (let i = 0; i < cards.length; i++) {
cards[i] = i % 52;
}

That creates an array [0…51, 0…51, 0…51]

Jokers

What about jokers?

Ok, let’s add two more indexes:

const cards = new Array(54);for (let i = 0; i < cards.length; i++) {
cards[i] = i % 54;
}

Combine

Now we’ll create a general purpose function for all properties:

const getProperties = (i) => {
const joker = i > 51;
const rank = joker ? -1 : i % 13;
const value = rank + 1;
const suit = joker ? -1 : i / 13 | 0;
return { rank, value, suit, joker };
};

Try out:

cards.map(getProperties);

Here’s the results:

[
{ rank: 0, value: 1, suit: 0, joker: false }, // 0
{ rank: 1, value: 2, suit: 0, joker: false }, // 1
...,
{ rank: 11, value: 12, suit: 0, joker: false }, // 11
{ rank: 12, value: 13, suit: 0, joker: false }, // 12
{ rank: 0, value: 1, suit: 1, joker: false }, // 13
{ rank: 1, value: 2, suit: 1, joker: false }, // 14
...,
{ rank: 11, value: 12, suit: 1, joker: false }, // 24
{ rank: 12, value: 13, suit: 1, joker: false }, // 25
{ rank: 0, value: 1, suit: 2, joker: false }, // 26
{ rank: 1, value: 2, suit: 2, joker: false }, // 27
...,
{ rank: 11, value: 12, suit: 2, joker: false }, // 37
{ rank: 12, value: 13, suit: 2, joker: false }, // 38
{ rank: 0, value: 1, suit: 3, joker: false }, // 39
{ rank: 1, value: 2, suit: 3, joker: false }, // 40
...,
{ rank: 11, value: 12, suit: 3, joker: false }, // 50
{ rank: 12, value: 13, suit: 3, joker: false }, // 51
...,
{ rank: -1, value: 0, suit: -1, joker: true }, // 52
{ rank: -1, value: 0, suit: -1, joker: true } // 53
];

Beautiful!

You can use whatever ranks and values you like with jokers, I just like -1 + 1 = 0 for the value.

Human-readable ranks and suits

How can we get ranks and suits more human-readable? Just create an array of names and use ranks/suits as indexes for those:

const ranks = 'A 2 3 4 5 6 7 8 9 10 J Q K'.split(' ');
const suits = '♠︎ ♥︎ ♣︎ ♦︎'.split(' ');

Ok, so now we can just call ranks[rank] and suits[suit] and we’ll get human-readable strings out. Nice!

Color

This is easy, just take suit’s remainder of two:

const color = suit % 2 ? 'red' : 'black';

Shuffling the deck

One more trick for this post: how to shuffle the deck? You could use sort:

cards.sort(() => {
return Math.random() < 0.5;
};

Although because of how sort function works, you won’t get good randomness. Let’s use Fisher–Yates shuffle instead:

const shuffle = (cards) => {
for (let i = 0; i < cards.length; i++) {
const rnd = Math.random() * i | 0;
const tmp = cards[i];
cards[i] = cards[rnd];
cards[rnd] = tmp;
}
return cards;
};

Fisher–Yates starts from the end of an array. For every card, it’ll choose itself or another card before the iterating card to switch places with. So you’ll get equal amount of shuffles per card. That’s the key for a perfect randomness.

Try yourself

Here’s a JSFiddle, which you can use to try things out: http://jsfiddle.net/Lz3fhkyb/

Next post: drawing cards

In the next post, I’ll show you how to render cards. If you have anything to comment, you’ll find me on Twitter.

--

--

Juha Lindstedt

Web architect at iDiD digital signage, creator of RE:DOM