[Derek Wyatt <tone_hole / yahoo.ca>, 2005-03-20 15.55 CET]
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> This was a fun one :)
> 
> I'm looking forward to seeing other solutions.

Well, here is mine. I agree it was a fun one, except in the very long part
where I try to match different plays (what is the correct word?). Poker has
too many!

The strategy was nothing OO, or elegant. I just ordered the hand by rank (or
by suit when checking for flush) and tried to match some regexes. Probably
has bugs, the supplied card generator program never yielded a Royal Flush...

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 indices of 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 ? # if it starts with ace
					"Royal Flush" : # it's royal
					"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)
		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)
				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)
			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