Hello everyone and thanks in advance for taking the time to read my post!!
In the process of learning Ruby, I'm writing a video poker machine that analyzes hands represented by arrays of Cards, like so: [ card, card, card, card, card ]
You may know that in draw poker, one can hold certain cards and toss the rest to the dealer, to be replaced by drawing new cards from the deck in the hopes of improving one's hand (hence the name). It's natural to figure that holds would be represented by an array of boolean values. To wit: [ true, true, false, false, false ]
would mean holding the first two cards in a five-card hand and discarding the rest. What I'm trying to write is a method that will give me an array of all possible holds (an array of arrays) for a given hand size.
I've found that Ruby is terrific (though a bit slow) for permutations and combinations, i.e., all possible unique hands from a 52-card deck. All I have to do for that is call deck.combination( hand_size ).to_a
. But unfortunately neither .permutation
nor .combination
is really giving me what I need for a method to produce possible holds.
High school math (which I'm terrible at, by the way) tells me that when determining possible unique n
-combinations of two possible values, the size of the universe of all possible n
-combinations will be two to the power of n
- meaning there are 32 possible holds for a five-card poker hand.
After slaughtering a snow-white bull for the gods at midnight under a full moon, I've come up with the following solution using binary numbers. This method loops through a range from 0...2 ** hand_size
, turns each element of the range into a binary number string with a hand_size
number of leading zeroes using .rjust
, then .map
s each character in the string to an array of hand_size
elements using a conditional and ternary; if the character is "1"
it maps a true
to the array, otherwise the character is a "0"
and it maps false
to the array.
def possible_holds( hand_size )
result = []
for each_hold in 0...2 ** hand_size do
as_binary = each_hold.to_s( 2 ).rjust( hand_size, "0" )
result << ( 0...hand_size ).to_a.map{ | card | as_binary[ card ] == "1" ? true : false }
end
result
end
This clunky little method actually works very well, but... it just doesn't seem very rubinic. I would love to hear clever suggestions as to how to improve it. I have this nagging feeling that this can all be done with a neat, simple method/enumerable or two... but no matter how many virgins I sacrifice I just can't seem to come up with anything better.
Thanks in advance for your help and happy coding!!
n = 5
,a = n.times.to_a.combination(2).to_a #=> [[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
are the10
(n*(n-1)/2
) ways of holding 2 cards in a 5-card hand. Each element gives the indices of the two cards to be kept. If the hand werehand = ['4D', 'AS', 'KH', '6C', 'QD']
, the possible pairs of cards to keep would bea.map { |b| a.values_at(*b) } #=> [["4D", "AS"], ["4D", "KH"], ["4D", "6C"], ["4D", "QD"], ["AS", "KH"], ["AS", "6C"], ["AS", "QD"], ["KH", "6C"], ["KH", "QD"], ["6C", "QD"]]
. \$\endgroup\$