Carlos
3/23/2005 5:22:00 PM
[Carlos <angus@quovadis.com.ar>, 2005-03-21 19.44 CET]
[...]
> Probably has bugs, the supplied card generator program never yielded a
> Royal Flush...
...or a straight with a pair inside it!
Thanks to the last message from Patrick Hurley, I've found the same bug in
my program. Here is the diff, and after that the full new version.
Thanks Patrick.
The diff:
--- told.rb 2005-03-23 18:15:54.062826872 +0100
+++ t.rb 2005-03-23 18:10:32.546704752 +0100
@@ -134,17 +134,24 @@
# ...straight
line.do!(:sort)
+ # take out pairs inside a possible straight!
+ # -- thanks Patrick Hurley
+ pairs = ""
+ line_wo_pairs =
+ line.gsub(/((\w). )((\2. ?)+)/) { pairs << $3; $1 }
RANKS.split(//).each_n(5) do |a,b,c,d,e|
r = /(#{a}. #{b}. #{c}. #{d}. #{e}.)/
- if m = r.match(line)
- hands << finish(line, "Straight", m, 1)
+ if m = r.match(line_wo_pairs)
+ hands << finish(line_wo_pairs+" "+pairs,
+ "Straight", m, 1)
throw :found
end
end
# ...straight, low ace
- line.do!(:sort, true)
- if m = /(5. 4. 3. 2. A.)/.match(line)
- hands << finish(line, "Straight", m, 1)
+ line_wo_pairs.do!(:sort, true)
+ if m = /(5. 4. 3. 2. A.)/.match(line_wo_pairs)
+ hands << finish(line_wo_pairs+" "+pairs,
+ "Straight", m, 1)
throw :found
end
The full program:
RANKS = "AKQJT98765432"
INTERNAL = "ABCDEFGHIJKLM"
RANKS_REVERSED = RANKS.reverse
ACE = "A"
LOW_ACE = "N"
# "plays"? maybe "figures"? (?)
PLAYS = {
"Royal Flush" => 10,
"Straight Flush" => 9,
"Four of a Kind" => 8,
"Full House" => 7,
"Flush" => 6,
"Straight" => 5,
"Three of a Kind" => 4,
"Double Pair" => 3,
"Pair" => 2,
"High Card" => 1,
"" => 0
}
class String
# split, do something with the array except finding, join, replace
# I never find the right method name...
def do! (method, low_ace=false, &block)
s = self.tr RANKS, INTERNAL
s.tr!(ACE, LOW_ACE) if low_ace
arr = s.split.send(method, &block)
s = arr.join(" ")
s.tr!(LOW_ACE, ACE) if low_ace
replace s.tr(INTERNAL, RANKS)
self
end
end
module Enumerable
# yields n items each time (but advances by one)
def each_n (n)
a = []
each do |cur|
a << cur
next if a.size < n
yield *a
a.shift
end
end
end
# moves the used cards to the left, calculates hand score,
# creates hash to insert in hands array
# hand is the hand, name is name of the play (game?)
# m is the matched play (game? hand?)
# groups are the groups in m that form the hand
# I repeat, I'm very bad choosing method names
def finish (hand, name, m, *groups)
# extract the matched play (?) from hand,
# sort its parts from biggest to smallest (for the full house)
duphand = hand.dup
groups = groups.map {|g|
b = m.begin(g); e = m.end(g)
hand[b...e] = "*" * (e-b)
duphand.slice(b...e) }.
sort_by {|g| -g.size }
hand.delete!("*")
# if there are any remaining cards (kickers), sort them
if hand.size > 2
hand.do!(:sort)
end
# reinsert hand at the beginning
hand = groups.join(" ") + " " + hand
hand.squeeze!
# calculate score
# the score is a 5-digit hex number, each digit with
# the rank of the card at that position
# ups... can't use String#do! here :(
score = hand.split[0,5].inject(1) { |sc, card|
(sc << 4) + RANKS_REVERSED.index(card[0].chr) }
# build the hash and return it
{ :hand => hand, :name => name, :score => score }
end
hands = []
while line = gets
line.chomp!
if line.split.size != 7
hands << {:hand => line, :name => "", :score => 0}
next
end
line.do!(:sort)
catch :found do
# try to find...
# ... straight (and royal) flush
RANKS.split(//).each_n(5) do |a,b,c,d,e|
r = /(#{a}(.) #{b}\2 #{c}\2 #{d}\2 #{e}\2)/
if m = r.match(line)
hands << finish(line,
(m[0][0]==?A ?
"Royal Flush" :
"Straight Flush"),
m, 1)
throw :found
end
end
# try to find straight flush with low ace
line.do!(:sort, true)
if m = /(5(.) 4\2 3\2 2\2 A\2)/.match(line)
hands << finish(line, "Straight Flush", m, 1)
throw :found
end
# ... four of a kind
line.do!(:sort)
if m = /((\w). \2. \2. \2.)/.match(line)
hands << finish(line, "Four of a Kind", m, 1)
throw :found
end
# ... full house
if m = /((\w)\w \2\w \2\w).*((\w)\w \4\w)/.match(line) or
m = /((\w)\w \2\w).*((\w)\w \4\w \4\w)/.match(line)
hands << finish(line, "Full House", m, 1, 3)
throw :found
end
# ...flush
# sort by color
line.do!(:sort_by){|card| [card[1],card[0]]}
if m = /(\w(\w) \w\2 \w\2 \w\2 \w\2)/.match(line)
hands << finish(line, "Flush", m, 1)
throw :found
end
# ...straight
line.do!(:sort)
# take out pairs inside a possible straight!
# -- thanks Patrick Hurley
pairs = ""
line_wo_pairs =
line.gsub(/((\w). )((\2. ?)+)/) { pairs << $3; $1 }
RANKS.split(//).each_n(5) do |a,b,c,d,e|
r = /(#{a}. #{b}. #{c}. #{d}. #{e}.)/
if m = r.match(line_wo_pairs)
hands << finish(line_wo_pairs+" "+pairs,
"Straight", m, 1)
throw :found
end
end
# ...straight, low ace
line_wo_pairs.do!(:sort, true)
if m = /(5. 4. 3. 2. A.)/.match(line_wo_pairs)
hands << finish(line_wo_pairs+" "+pairs,
"Straight", m, 1)
throw :found
end
# ... three of a kind
line.do!(:sort)
if m = /((\w)\w \2\w \2\w)/.match(line)
hands << finish(line, "Three of a Kind", m, 1)
throw :found
end
# ... double pair
if m = /((\w)\w \2\w).*((\w)\w \4\w)/.match(line)
hands << finish(line, "Double Pair", m, 1, 3)
throw :found
end
# ...pair
if m = /((\w)\w \2\w)/.match(line)
hands << finish(line, "Pair", m, 1)
throw :found
end
# ... high card.. FINISH AT LAST!!!
if m = /^(\w\w)/.match(line)
hands << finish(line, "High Card", m, 1)
throw :found
end
raise "This program is buggy. Terminating."
end
end
# get the winner hand
winner = hands.sort_by {|h| [-PLAYS[h[:name]], -h[:score]] }.first
# print the lines
hands.each do |h|
print h[:hand], " ", h[:name]
if winner[:name] != "" &&
h[:name] == winner[:name] &&
h[:score] == winner[:score]
print " (winner)"
end
puts
end