dstromberg
Tutorial - Create and Shuffle a Deck of Cards in Javascript
Since it has been a long time since I actually sat down to write a blog post here, I though it best to get back in to it with something relatively on the basic side. We will be discussing constructing and shuffling a deck of cards in Javascript.So first things first, we need to define all of the cards.
var cards = [ 'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', '10H', 'JH', 'QH', 'KH', 'AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', '10D', 'JD', 'QD', 'KD', 'AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', '10S', 'JS', 'QS', 'KS', 'AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', '10C', 'JC', 'QC', 'KC' ]That should do it! Alright, I'm being facetious here. While that approach may work in the short term for some cases, there are a few problems right off the bat. For one, we have stored the definition as just a string with two characters. That means whenever we need to perform logic on determining what card we have, we will need to split that string and look at each character separately. While Javascript does allow us to access characters in a string with the same operators as an array, knowing that card[0] is the rank and card[1] is the suit will quickly get confusing further along in your code. So an object with distinct properties will usually be easier to deal with.
The more obvious problem here is that working with such verbose lines of code will get unruly very quickly. Especially if you change your mind on the representation of the properties later. Manually retyping the whole thing is kind of a pain, as it was already a pain for me to type it out here just the once. A more robust solution is to define all the available properties of the cards separately, then loop through those properties and create a unique card for each combination.
One other factor to consider is that in many cases it may be easier to represent all of the properties as integers instead of strings. Then it is only the display layer that needs to be concerned with corresponding an integer to a display value. There are pros and cons to this approach depending on your particular needs; but as an example, it is much easier to check if 13 is greater than 11 than it is to check if K is greater than J. You will likely still need a certain amount of conversion of values for your logic (especially when dealing with Aces in most card games) but integer representations will usually save you many lines of code overall.
So with the above points in mind:
var cards = []; for (suit = 4; suit > 0; suit--) { for (rank = 13; rank > 0; rank--) { cards.push({ suit: suit, rank: rank }); } }To keep things clean here, and to offer a little bit of crossover to other programming languages, it will probably be best to create an object to represent our deck of cards. It lets us store all of the properties related to that deck in one easily identifiable place, and contain any logic related to that deck internal to our object, where it logically exists. So we'll create a deck object that initializes our list of cards whenever it is created.
function Deck() { this.cards = []; for (suit = 4; suit > 0; suit--) { for (rank = 13; rank > 0; rank--) { this.cards.push({ suit: suit, rank: rank }); } } }Now we can create a deck of cards whenever we'd like, using var deck = new Deck() and access the cards by referencing deck.cards. Since we are already delving in to the world of objects for our code, we could also consider using constructors to build our cards as well. Note that it is already created as an object in the previous examples, simply using the most basic version of the native Javascript object. Sometimes it may be useful to have a Card object that is defined and can inherit methods and properties, but there are plenty of other times where this will be complete overkill for your needs. So ultimately you will need to decide your best course. For the sake of inclusion, here is how we would go about it in our simple example:
function Card(suit, rank) { this.suit = suit; this.rank = rank; } function Deck() { this.cards = []; for (suit = 4; suit > 0; suit--) { for (rank = 13; rank > 0; rank--) { this.cards.push(new Card(suit, rank)); } } } var deck = new Deck();Now that we have established our deck of cards, we move on to shuffling. Obviously keeping the cards in the defined order won't do us much good, and would make for some pretty boring games. Fortunately for this operation, the details are pretty well sorted out by great mathematicians, statisticians, and computer scientists before us. The very short version of an answer is to use the Fisher-Yates algorithm. The slightly longer answer is to use the Durstenfeld version of the algorithm, which was optimized for the specific flow of programming instructions. Here is the basic form of this, applied to Javascript:
function shuffle(a) { var j, x, i; for (i = a.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); x = a[i]; a[i] = a[j]; a[j] = x; } }Some of the details may get altered around a bit if you look at various examples, but the principles will always be the same. Loop through the array so that we visit every card in the deck, pick a random position elsewhere in the array, then swap those two cards. A very quick note here, which you will likely see if you read the wikipedia article on the algorithm, this function is only as random as our internal random number generator. This means it will never be truly random here, and ultimately your likelihood of shuffling a deck of cards in real life produces far more potential combinations than this function ever will. But in practicality, this level of randomness should be plenty sufficient, and produce enough psuedorandom combinations that it would take many lifetimes to get through them all.
Note that in this example, we are modifying the passed array directly, so no return value is necessary, and no reassignment is needed when the function is called. For our uses here, that's probably a good thing, as we don't really have a need for the unshuffled deck to continue living on separately. But you can find examples of functions to create a new returned array without altering the original, should you so desire.
The implementation of this function can be done in a whole variety of ways. You can just include it as a global function somewhere in your code, which can then be called anywhere. You can add it to some sort of helper library in your codebase if one already exists for common functions. You could extend the native Array object in Javascript by using Array.prototype (though be aware of the consequences of such modifications to native objects). Or even more simply, you can take a look at any third party libraries which you are already using that may include a shuffle function. Underscore and Lodash already have .shuffle() built in, and certainly many other utility libraries do as well. Nearly all of them should be more or less this same algorithm, though probably with a bit more type checking. If this function is all you need the utility library for, it may be better to write it yourself than to add additional bloat with a full utility library. A quick note that there are a number of npm packages which provide such a shuffle function, but given certain histories of utility libraries in npm, I generally discourage the reliance on small packages like this when we have the resources to create and maintain it ourselves with little overhead.
Since we are building our deck of cards in the context of the Deck object, the simplest solution will probably be to include this shuffle function as a method for the object, which modifies our internal property containing the array of cards. This can be done by adding the method to the prototype for the Deck object:
Deck.prototype.shuffle = function() {...}or by defining it in the class constructor:
function Deck() { this.cards = []; ... this.shuffle = function() { ... } }Then we update our example shuffle function above so that all references to variable 'a' are instead a reference to 'this.cards'. Thus we will not need to pass the deck object back and forth between any functions, and can easily shuffle the deck at any time after it is instantiated by calling deck.shuffle(). If you included the method in the class declaration, you can even choose to add a call to shuffle as part of the constructor, to ensure that a deck is always shuffled from the start whenever it is created.
Now we have our Deck object, with our list of all of the cards, which have then been thoroughly shuffled. The last point to consider is how to access those cards. A deck of cards that just exists on its own isn't very useful. We need to interact with it, hand out cards to our players, and move them around. Again, we will consider a couple of options.
The first option is that when any card is requested, we remove that card from our deck and hand it off to whomever requests the card. The simple answer to this is using the .pop() method that is available to all Javascript arrays. It does exactly what we need here, remove the last element of the array, and return that element on its own. You can also use the .shift() method, which does the same thing, except removing the first element of the array instead of the last. Since we are relying upon our deck being randomly shuffled (well, pseudorandomly), it doesn't make much of a difference which direction you choose to go. The only important piece to keep in mind there is that if you are adding cards back in to the deck at any point, you should insert them at the opposite end of where you are drawing cards. So shift should be complemented with push, and pop should be complemented with unshift.
Since it is usually better to interact with a method of an object, rather than calling the properties directly, we can wrap this up in a method on Deck to retrieve a card.
function Deck() { this.cards = []; ... this.getCard = function() { return this.cards.pop(); } } var deck = new Deck(); var card = deck.getCard();An alternative option may be useful in some scenarios, which is to not remove drawn cards from our Deck object when they are requested. I'll be honest, I thought of this as an alternate way to approach the need, but I couldn't really think of an example of when it would be useful. If you come up with a good use for it, let me know in the comments. In any case, in order to select a card from the deck without removing it from the deck, we can create an internal pointer, and simply return the card at the current position. Once the current card has been identified, we do need to increment the pointer so that subsequent calls retrieve the next card in the list. For simplicity of code, and to avoid intermediary variable storing, we can initialize the pointer at -1, and increment it before we retrieve the card, thus getting the card at position 0 the first time it is called, and continuing to move down the list with later calls to the method.
function Deck() { this.cards = []; ... this.currentCard = -1; this.getCard = function() { this.currentCard++; return this.cards[this.currentCard]; } }One important thing to note in this pattern is that Javascript will return the card object as passed by reference, not by value, since it is an object and not a primitive. What this means is that after you retrieve a card from the deck in this manner, any changes you make to that card outside of the Deck object will also update that card as it exists inside of the Deck. Sometimes this can be a positive, sometimes it is an unintended consequence. There are a variety of solutions out there for returning a copy of an object rather than a reference to it, so I encourage you to explore those if you have that need. Though if you do have that need, you are probably better suited to using the pop() method for retrieving your cards, so consider which pattern is truly the one you need.
Once we are retrieving cards from our deck, we will quickly run in to the need to retrieve multiple cards at once. Since the internal pop and shift methods only execute on one element at a time, we will need to modify our method to create a temporary array which holds our returned cards, and execute the pop function as many times as requested before we can send back all of the cards. Since ES6 is not yet fully browser supported, which would allow default input parameter values, we can instead check if the parameter is undefined and assign a default value then. This allows us to call the method with no parameters when we only want one card. Should you want your Deck object to be extra clear about its intentions, you could add a separate method called getCard, which internally calls getCards with an input of 1.
function Deck(){ this.cards = []; ... this.getCards = function(number) { if (typeof number === 'undefined') number = 1; var returnCards = []; for (var i = number; i > 0; i--) { returnCards.push(this.cards.pop()); } return returnCards; } } var deck = new Deck(); var threeCards = deck.getCards(3); var oneCard = deck.getCards();That should pretty much do it for this example. If you want to see our final Deck object, you can find it all put together here on github. As always, feel free to clone and fork for any modifications you would like to use it for.