VII: The Programmatic Side of Mathematica: Dealing Cards

You might recall in our last post, we had a way of declaring a deck of cards:

deck = Flatten[Outer[List, {s, h, c, d},
       Join[Range[2, 10], {J, Q, K, A}]], 1]
{{s,2},{s,3},{s,4},{s,5},{s,6},{s,7},{s,8},{s,9},{s,10},{s,J},
{s,Q},{s,K},{s,A},{h,2},{h,3},{h,4},{h,5},{h,6},{h,7},{h,8},{h,9},
{h,10},{h,J},{h,Q},{h,K},{h,A},{c,2},{c,3},{c,4},{c,5},{c,6},
{c,7},{c,8},{c,9},{c,10},{c,J},{c,Q},{c,K},{c,A},{d,2},{d,3},{d,4},
{d,5},{d,6},{d,7},{d,8},{d,9},{d,10},{d,J},{d,Q},{d,K},{d,A}}

… but alas, no way yet of dealing random cards. And this was because we said there was no way of actually shuffling the deck. Mathematica would likely provide a way, but it is probably better to declare a function that randomly deals out the cards, while removing the cards it deals from the deck. To see this more easily, let’s declare a small set whose results are easier to see:

m= {2,4, 6, 8, 12, 78, 114}
{2,4,6,8,12,78,114}

We need to declare a function that chooses a random element from the list:

Random[Integer, {1, Length[m]}]
3

The number 3 returned is not from the list, but random integer between 1 and the list length. That is, the third element in the list, or the number 6. This can still be useful, however:

removeRand[lis_]:=Delete[lis, Random[Integer,{1, Length[lis]}]]

We can pass this random element number into a function which deletes the number at the random position from the list, as shown above. But don’t we want to deal that card also? There is not really any way I can think of that can both delete and return an element, but there is a way we may use set theory. If I delete an element n from set A, then A’s complement (or “not A”) becomes the deleted element n.

Complement[m, removeRand[m]]
{12}

On the same call, m is the same copy with all the original elements. So, in this call to Complement[], an element is randomly removed from set m. This new set, without the “12”, is now compared against m to find the new set’s complement, the number that was removed, and it is displayed. removeRand[] evidently returned a “5”, which is the number 12, the 5th element in m. A new subset of m {2,4,6,8,78,114} is compared against m to determine the complement, and 12 is returned. Throughout this operation, m never really gets modified.

When we deal cards from a deck, we want to deal several cards in one hand. In rummy, that can consist of 7 cards, depending on how many people are playing (Wikipedia says up to 13 cards for two-handed rummy). So, let’s declare a function which will help us deal cards:

hand[n_]:=Complement[deck, Nest[removeRand, deck,n]]

What Nest[] does is remove n cards recursively from a set called “deck”, the set we originally declared in today’s article. The complement between “deck” and “deck” with 7 rummy cards removed, is the rummy hand.

hand[7]
{{c,6},{c,A},{d,A},{h,2},{h,J},{h,K},{s,2}}

With a pair of aces at the start, it’s not a bad hand, although your opponent might do better.

VI: The Programmatic Side of Mathematica: The perfect shuffle of a deck of cards

Making a deck of cards requires making a the basic thirteen card values:

Join[Range[2,10], {J, Q, K, A}]
{2,3,4,5,6,7,8,9,10,J,Q,K,A}

Then, you need to distribute these values among the four suits:

Outer[List, {c, d, h, s}, %]
{{{c,2},{c,3},{c,4},{c,5},{c,6},{c,7},{c,8},{c,9},{c,10},{c,J},{c,Q},{c,K},{c,
   A}},{{d,2},{d,3},{d,4},{d,5},{d,6},{d,7},{d,8},{d,9},{d,10},{d,J},{d,
   Q},{d,K},{d,A}},{{h,2},{h,3},{h,4},{h,5},{h,6},{h,7},{h,8},{h,9},{h,
   10},{h,J},{h,Q},{h,K},{h,A}},{{s,2},{s,3},{s,4},{s,5},{s,6},{s,7},{s,
   8},{s,9},{s,10},{s,J},{s,Q},{s,K},{s,A}}}

This set gives four subsets representing each suit, then expresses each value-suit combination as a set. You want to keep the value-suit combinations, but if you want to shuffle the deck, you need to “flatten” the set somewhat to remove the set boundaries for the suits. That would be a call to the Flatten[] function with a “1” as a second parameter, indicating that we know that this set is a superset of four subsets, and we would like to remove the boundaries for the four subsets (the suits) and create a superset of the 52 individual cards:

Flatten[%,1]
{{c,2},{c,3},{c,4},{c,5},{c,6},{c,7},{c,8},{c,9},{c,10},{c,J},{c,Q},{c,K},{c,
  A},{d,2},{d,3},{d,4},{d,5},{d,6},{d,7},{d,8},{d,9},{d,10},{d,J},{d,Q},{d,
  K},{d,A},{h,2},{h,3},{h,4},{h,5},{h,6},{h,7},{h,8},{h,9},{h,10},{h,J},{h,
  Q},{h,K},{h,A},{s,2},{s,3},{s,4},{s,5},{s,6},{s,7},{s,8},{s,9},{s,10},{s,
  J},{s,Q},{s,K},{s,A}}

The cards are in numerical order of clubs, diamonds, hearts and spades. We would now like to shuffle the deck. What would a good dealer do to shuffle a deck? I would guess that he would first split the deck:

Partition[%,26]
{{{c,2},{c,3},{c,4},{c,5},{c,6},{c,7},{c,8},{c,9},{c,10},{c,J},{c,Q},{c,K},{c,
    A},{d,2},{d,3},{d,4},{d,5},{d,6},{d,7},{d,8},{d,9},{d,10},{d,J},{d,
    Q},{d,K},{d,A}},{{h,2},{h,3},{h,4},{h,5},{h,6},{h,7},{h,8},{h,9},{h,
    10},{h,J},{h,Q},{h,K},{h,A},{s,2},{s,3},{s,4},{s,5},{s,6},{s,7},{s,
    8},{s,9},{s,10},{s,J},{s,Q},{s,K},{s,A}}}

That creates that three-layered set of sets of sets again, but this time the middle layer is in two pieces, where the deck was split, rather than being split at each suit. Now, what? Our dealer would likely shuffle the deck. If he is skilled he could do what is known as a perfect shuffle, where the cards from each half-deck are interleaved with cards in the other half:

Transpose[%]
{{{c,2},{h,2}},{{c,3},{h,3}},{{c,4},{h,4}},{{c,5},{h,5}},{{c,6},{h,6}},{{c,
   7},{h,7}},{{c,8},{h,8}},{{c,9},{h,9}},{{c,10},{h,10}},{{c,J},{h,J}},{{c,
   Q},{h,Q}},{{c,K},{h,K}},{{c,A},{h,A}},{{d,2},{s,2}},{{d,3},{s,3}},{{d,
   4},{s,4}},{{d,5},{s,5}},{{d,6},{s,6}},{{d,7},{s,7}},{{d,8},{s,8}},{{d,
   9},{s,9}},{{d,10},{s,10}},{{d,J},{s,J}},{{d,Q},{s,Q}},{{d,K},{s,K}},{{d,
   A},{s,A}}}

All of these commands can be nested inside of one command so that we get:

Transpose[Partition[Flatten[Outer[List, {c, d, h, s},
    Join[Range[2,10], {J, Q, K, A}]], 1], 26]]

Oh yeah, and don’t forget to flatten the set again to remove the split in the deck:

Flatten[Transpose[Partition[Flatten[Outer[List, {c, d, h, s},
    Join[Range[2,10], {J, Q, K, A}]], 1], 26]], 1]
{{c,2},{h,2},{c,3},{h,3},{c,4},{h,4},{c,5},{h,5},{c,6},{h,6},{c,7},{h,7},{c,
    8},{h,8},{c,9},{h,9},{c,10},{h,10},{c,J},{h,J},{c,Q},{h,Q},{c,K},{h,K},{c,
    A},{h,A},{d,2},{s,2},{d,3},{s,3},{d,4},{s,4},{d,5},{s,5},{d,6},{s,6},{d,
    7},{s,7},{d,8},{s,8},{d,9},{s,9},{d,10},{s,10},{d,J},{s,J},{d,Q},{s,Q},{d,
    K},{s,K},{d,A},{s,A}}

I wouldn’t personally use these to play cards at this point, since the cards are still shuffled predictably and reproducibly. In the real world, if it so happened that a dealer shuffled the deck by interleaving the cards, this might still be the result from a new deck. But what you want is for them to be shuffled randomly and not predictably. You may even want to store the array in a variable so you can’t see the deck right away.

So, what is known as the “perfect shuffle” is only “perfect” in the sense that the interleaving of two halves of an ordered deck is done without errors. If all interleavings are perfect beginning with an ordered deck, I would guess that all subsequent deck splittings and interleavings are also repreoducible, and therefore predictable, assuming all shuffles are perfect shuffles. In the real world, random errors would be introduced in the interleavings (two cards slip by on one hand before one slips by on the other for example), which introduce the element of chance in card games.