Shuffling a deck of cards with Elm

James Carlson
3 min readApr 4, 2019

--

Here’s the problem: given a list of things — cards, numbers, whatever — how can we put them in a random order? This is a nice little computer science problem that we will solve using Elm, a relatively new and, in my opinion, totally awesome programming language.

Elm is a language of pure functions, which means that that the inputs to a function completely determine its outputs, just like in Math. It’s main use is building web apps.

Cutting the deck

Our solution is going to be built by snapping together some handy-dandy parts, the functional programming equivalent of Legos. The first part we shall use is the function List.Extra.splitAt. It will be used to cut the card deck:

> List.Extra.splitAt 3 [10, 11, 12, 13, 14, 15]
([10,11,12],[13,14,15])

The result is a tuple consisting of the two halves of the list. If we glue these two parts back together in the opposite order, we have cut the deck. Let’s package this idea as a function definition:

cut : Int -> List a -> List a
cut j list =
let
k = modBy (List.length list) j
(a, b) = List.Extra.splitAt k list
in
b ++ a

And let’s test the function to make sure it works as expected:

> cut 3 [1,2,3,4,5,6,7,8]
[4,5,6,7,8,1,2,3]
> cut 3 ["Red", "Green", "Blue", "Black", "White"]
["Black","White","Red","Green","Blue"]

So far, so good!

Shuffling the deck

To shuffle the deck, we use the interleave function:

> List.Extra.interweave [1,2,3] [4,5,6]
[1,4,2,5,3,6]

To perform a real shuffle, we make up a function that splits the deck into two parts, then interweaves the parts:

shuffle : List a -> List a
shuffle list =
let
(a, b) = List.Extra.splitAt ((List.length list)//2) list
in
List.Extra.interweave a b

Again, we test:

> shuffle [1,2,3,4,5,6]
[1,4,2,5,3,6]

Happily, shuffle works with an odd number of elements as well:

> shuffle [1,2,3,4,5]
[1,3,2,4,5]

Dealer’s move

In real card play, the dealer cuts the cards, then shuffles. We’ll do the same:

dealersMove : Int -> List a -> List a
dealersMove k list =
list
|> cut k
|> shuffle

Here is the Dealer’s Move in action:

> dealersMove 2 [1,2,3,4,5,6]
[3,6,4,1,5,2]

Repeating the Dealer’s Move

You may want to repeat the Dealer’s Move several times to really mix things up. To do this, we use the List.foldl function to apply dealersMove again and again:

randomizeList : (List Int) -> List a -> List a
randomizeList integerList list =
List.foldl dealersMove list integerList

The integerList parameter holds a list of numbers that will be used in applying the Dealer’s Move. The code in the example below has the same effect as applying dealersMove first with parameter 2, then to the result with parameter 5

randomizeList [2, 5] [1, 2, 3, 4, 5, 6, 7, 9, 10]
[10,3,6,7,1,4,2,9,5]

If you use a random number generator to produce integerList, then the result of applying randomize integerList to a list will be a random permutation of the given list. We have reached our destination! Notice how little work we did —mostly combining powerful functions like splitAt and interweave. Our Lego pieces.

NOTE. As @jgrenat on the Elm Slack pointed out to me, there is a library function which does what this article describes. Please see elm-community/random.

Appendix

Here is the full code:

module Cards exposing (..)

import List.Extra

cut : Int -> List a -> List a
cut j list =
let
k = modBy (List.length list) j
(a, b) = List.Extra.splitAt j list
in
b ++ a

shuffle : List a -> List a
shuffle list =
let
(a, b) = List.Extra.splitAt ((List.length list)//2) list
in
List.Extra.interweave a b

dealersMove : Int -> List a -> List a
dealersMove k list =
list
|> cut k
|> shuffle

{-|
> randomizeList [2, 4, 3] [1,2,3,4,5,6]
[4,5,3,6,1,2]
-}
randomizeList : (List Int) -> List a -> List a
randomizeList integerList list =
List.foldl dealersMove list integerList

--

--

No responses yet