In Progress
Unit 1, Lesson 21
In Progress

Combination

Quick, how do you discover every possible combination of 5 cards drawn from a 7-card set? In this episode you’ll see a demonstration of Ruby’s shortcuts for this kind of data-shuffling busywork. In the process, you’ll learn why Ruby has both Array#combination and Array#repeated_combination methods.

Video transcript & code

Lately we've been working through some programming problems using playing card examples. While we've still got our playing card classes fresh in mind, I thought we could demonstrate a feature of the Array class that we haven't yet touched on.

Let's instantiate a deck of cards, grab a reference to the draw pile, and draw a hand of seven cards.

# coding: utf-8
require "./cards"

srand 123

deck = Deck.new

draw_pile = deck.stack(:draw_pile)

hand = draw_pile.first(7)

hand
# => [♠J, ♠6, ♦K, ♣7, ♣8, ♠Q, ♠7]

I'm no poker player, but I understand this much about the game: most versions of it involve picking a subset of five cards from either your hand or the table which taken together form a high-valued combination.

Quickly now: how would you go about discovering every possible combination of 5 cards to be found in this seven-card set?

We could fiddle around with nested loops or other solutions, but as is so often the case, Ruby has a shortcut for us.

We can simply send the combination message. Passing the number 5 tells the set of 7 that we want to see possible combinations of 5 cards drawn from it.

hand.combination(5).to_a
# => [[♠J, ♠6, ♦K, ♣7, ♣8],
#     [♠J, ♠6, ♦K, ♣7, ♠Q],
#     [♠J, ♠6, ♦K, ♣7, ♠7],
#     [♠J, ♠6, ♦K, ♣8, ♠Q],
#     [♠J, ♠6, ♦K, ♣8, ♠7]
# ...

Scrolling through the results, we can see that Ruby has produced every possible unique combination of five cards.

One thing we can see from these results is that Ruby has assumed that each object in the original set can only appear once in the output sets. Which is just as well, since I'm not aware of any poker variation where a single card can be counted more than once.

However, if we wanted to, we could tell Ruby to go ahead and show possibilities where source objects are used more than once. We do that with the repeated_combination method.

hand.repeated_combination(5).take(5).to_a
# => [[♠J, ♠J, ♠J, ♠J, ♠J],
#     [♠J, ♠J, ♠J, ♠J, ♠6],
#     [♠J, ♠J, ♠J, ♠J, ♦K],
#     [♠J, ♠J, ♠J, ♠J, ♣7],
#     [♠J, ♠J, ♠J, ♠J, ♣8]
# ...

This isn't very useful for analyzing card games, but we might think of other uses for it.

For instance, let's say we wanted a secret code that would let us communicate messages to a compatriot just by leaving what appear to be random groupings of playing cards somewhere they'll be able to see them.

We might start with a list of common English words.

Then we might take our deck of cards, convert it to an array, and ask it for all possible combinations of 2 cards, with duplicates allowed.

For the purposes of our code, we'll consider a single three of clubs (or whatever) lying by itself to be equivalent to a pile with doubled threes of clubs.

We reflect that in the code by uniq-ing doubles down to single-element arrays.

Then we zip our card combinations with our word list.

We turn the resulting list of lists into a hash.

And invert the hash so that we have an english-to-cards translation key.

wordlist = IO.readlines("1000.txt").map(&:chomp)
draw_pile
  .to_a
  .repeated_combination(2)
  .map(&:uniq)
  .zip(wordlist)
  .to_h
  .invert
# => {"the"=>[♠J],
#     "of"=>[♠J, ♠6],
#     "to"=>[♠J, ♦K],
#     "and"=>[♠J, ♣7],
#     "a"=>[♠J, ♣8],
#     "in"=>[♠J, ♠Q],
#     "is"=>[♠J, ♠7],
#     "it"=>[♠J, ♣4],
#     "you"=>[♠J, ♠2],
# ...

Now we can easily look up the words for the message we want to encode with cards.

One thing you'll notice with the combination methods is that they consider order to be irrelevent to uniqueness. In other words, if one combination produced is the five of clubs followed by the jack of diamonds, we won't then see a later set with jack of diamonds followed by the five of clubs. The combination methods consider these two lists, with the same cards in a different order, to be identical.

In certain cases, we might not want this behavior. We might want to see all possible combinations, including different orderings of the same cards. Array also has tools to produce these types of permutations.

But that's a topic for another day. Happy hacking!

Responses