Shuffling the deck of cards
Breaking down the problem
Before we start writing the code, it’s always a good idea to step back and think about the problem.
I find it useful to have a bird’s view of the problem and have a look at a starting point and the goal, and then try to build a bridge over the gap.
So I’ll ask a few questions of myself and if no one responds (no one will), I’ll try to answer them.
What is the problem about?
I need to shuffle a deck of 52 cards, so I will first of all need collection of data that represents a deck of cards, and then I need to change the order of the elements (i.e. shuffle the deck ) in a way that it should not be calculated ahead of time.
What would represent the deck the best?
I need 52 cards. It’s best to achieve with an array, which contains 52 elements. The elements can be anything: the actual cards, names or numbers. I personally don’t feel like typing the cards one by one in the array, so I will go with 52 different positive whole numbers.
How can I shuffle the deck?
Let’s think about it. If I incorporate some rule or consistency in the shuffling process, the system can easily be played. If I always take e.g. the first third of the deck and put it at the bottom of the leftover two-thirds, my shuffling system is easy to break and players can take advantage of it. So I need to figure out a system to shuffle the deck, which has something random in it.
What is the process of shuffling?
I need to split the deck at a random place, and divide the deck into two parts. I then take the first part and insert it somewhere inside the second. But again, the point where I insert the part of the deck at shouldn’t be based on a rule. So I will need another random point in the second part of the deck, and this will be the place where I insert the first part. I will need to repeat the process over and over again then, and it hopefully gives me a well shuffled deck.
Code it!
Now that we have the blueprint, our job is to translate it into code!
Create a shuffle.js file in your project folder. I like to write down the process either in the file itself in form of comments or on a piece of paper. As it would be fairly cumbersome to present the algorithm on a piece of paper, let’s write the steps in the file as comments:
// 1. create deck of cards
// 2. shuffle it!
// a. get a random number between 0 and 51 where we split the deck
// b. split the deck at that point
// c. paste the split part into a random place
Creating the deck
We said earlier that the deck could be represented with an array of 52 numbers. Here’s how I would do it:
const deck = [...Array(52)].map((value, index) => index);
We use the spread operator to create an array of 52 elements. If we just leave it as […Array(52)]
, the length of the array will be 52, but these elements will all have a value of undefined
.
So we iterate over these elements using the map
array method. The map method takes each element in an array and does with them whatever we specify in its callback function. The first argument of the callback function is the value of the current element (which is now undefined) and the second is the index of these elements in the array, which will be a number starting with 0.
An easy way to create 52 numbers is to replace the undefined values with the index numbers. This is what we are doing here: for each element (referred as value) in the array we replace them with their respective index. This way we will get an array of 52 numbers, ranging from 0 to 51.
Great, we have the deck!
Shuffling the deck
Let’s create a function for this task and surprisingly call it shuffle
:
// 2. shuffle it!
const shuffle = () => {
// a. get a random number between 0 and 51 where we split the deck
// b. split the deck at that point
// c. paste the split part into a random place
};
We will write the logic inside this function.
Let’s recall the thinking process. We need to split the deck at a random place (point a). What does this mean for our array-deck? We need to get a random number between 0 and 51 (that’s 52 elements):
// a. get a random number between 0 and 51 where we split the deck
const randomSplitNumber = Math.floor(Math.random() * 52);
Math.random
returns a random number between 0 and 1 (0 is included but 1 is not) but we need the random number between 0 and 51, so we need to multiply the number by 52. Hence we make sure that the number is definitely less than 52, which is good because the maximum value in our array is 51.
But our numbers are integers, so we will need to round them, and because the greatest number in the array is 51, we need to round down. This is what Math.floor
does.
Awesome, we have the random number which falls between 0 and 51, both inclusive. Now we need to raise the deck at that point.
So we need to split the array at the place our random number indicates. The splice
array method does just that:
// b. split the deck at that point
const firstPart = deck.splice(0, randomSplitNumber); // returns an array of numbers going from 0 to 40
The splice
method returns a new array we separate from the deck (this is important).
The first number inside splice
refers to the starting point. In our case this is going to be 0, the first element of the array.
The second argument is the number which indicates how many elements should be removed from deck and should be in the newly returned array. This will be our random number we have just generated.
As a result of this step, we got two arrays: one with the number of elements determined by the random number and one with the rest of the elements (splice
actually modifies the deck array and removes the elements from them as opposed to the slice
method, which keeps the original array).
Cool, we have the deck raised! Now we need to insert the raised first part into the second at a random spot. Let’s create this random number as well:
// c. paste the split part into a random place
const randomPasteNumber = Math.floor(Math.random() * deck.length); // 10
The process is the same, the only difference that we multiply by the length of the deck, which is now 52 minus the number of removed elements (remember, splice
removes the elements from the original array).
All we have left is to insert the first part into the second where randomPasteNumber
indicates:
deck.splice(randomPasteNumber, 0, ...firstPart);
Here we start with the random number (first argument) but this time we don’t remove any elements (so the second argument, the number of elements to remove is zero). On the contrary, we insert new elements into the array, and we are doing exactly that: we spread the elements of the firstPart
onto the second, so we will get the length of our deck
array back with the total number of 52 elements.
The difference is that the numbers are not in order anymore as the raised firstPart
is (ideally) inserted somewhere inside the leftover second part (deck
).
It can happen, of course, that the randomSplitNumber
is 0, which means that no elements will be removed from the deck, so no parts will be shifted and pasted somewhere else.
So we need to repeat this process over and over again at a large number of times. Only this way we can be sure that our deck is thoroughly shuffled.
Repeat it
How can we repeat it the process? There are a few ways to do it, one of them is to simply wrap everything we just wrote in a for
loop:
const shuffle = (repetitions) => {
for (let i = 0; i < repetitions; i++) {
// a. get a random number between 0 and 51 where we split the deck
const randomSplitNumber = Math.floor(Math.random() * 52);
// b. split the deck at that point
const firstPart = deck.splice(0, randomSplitNumber);
// c. paste the split part into a random place
const randomPasteNumber = Math.floor(Math.random() * deck.length);
deck.splice(randomPasteNumber, 0, ...firstPart);
}
};
We can finally call the function, say 15000 times:
shuffle(15000);
The for
loop will repeat the process 15000 times or whatever number you put in.
Obviously, the greater the number is the lower the probability is that a card remains at its original place.
Feel free to tweak the code and play around with it. Try entering 1, and you will occasionally see that the deck won’t change.
We can now tap ourselves on the shoulder because we have a simple card shuffling algorithm!
Conclusion and further considerations
You have seen one way of shuffling a deck of cards. Random is an important element of the algorithm. However, it’s worth taking some time to think about other ways. Can we shuffle the deck with having more split? Say we split the deck into three or even more parts and put them together at random? Or can we take every second, third etc. card, form a new pack of cards and insert it into somewhere in the leftover?
How does this solution compares to well-known shuffling algorithms?
Enjoy coding!