[Carlos <angus / quovadis.com.ar>, 2005-03-23 18.21 CET]
> [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.

Bahh... I forgot about the straight flush. The change should've been more
above. Please disregard the version in my last message and use this one.

Sorry about the spam (at least it is the sortest solution :). If I find more
bugs I should send the new versions to JGE2 directly.

Here it is:

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)
	
	#   take out pairs inside a possible straight!
	#   -- thanks Patrick Hurley
	pairs = ""
	line_wo_pairs = 
		line.gsub(/((\w). )((\2. ?)+)/) { pairs << $3; $1 }

	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_wo_pairs+" "+pairs)
				hands << finish(line_wo_pairs+" "+pairs,
					(m[0][0]==?A ?
					"Royal Flush" :
					"Straight Flush"),
					m, 1)
				throw :found
			end
		end
		# try to find straight flush with low ace
		line_wo_pairs.do!(:sort, true)
		if m = /(5(.) 4\2 3\2 2\2 A\2)/.match(line_wo_pairs)
			hands << finish(line_wo_pairs+" "+pairs,
					"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)
		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