```#!/usr/bin/ruby -w
#
# Quiz 24: Texas Hold'em
# Solution by Glenn Parker

module Combine
# Generate all combinations of +pick+ elements from +items+ array.
def Combine.pick(pick, items, &block)
combine([], 0, pick, items, &block)
end

private

def Combine.combine(set, index, pick, items, &block)
if pick == 0 or index == items.length
yield set
else
set.push(items[index])
combine(set, index + 1, pick - 1, items, &block)
set.pop
combine(set, index + 1, pick, items, &block) if
pick < items.length - index
end
end
end

# One card, with a face [2-9TJQKA] and a suit [shdc].
class Card

Face_Ranks = {
:A =>   12, :K =>   11, :Q =>   10, :J =>    9,
:T =>    8, :"9" =>  7, :"8" =>  6, :"7" =>  5,
:"6" =>  4, :"5" =>  3, :"4" =>  2, :"3" =>  1,
:"2" =>  0
}

Suit_Ranks = {
:s => 3, :h => 2, :d => 1, :c => 0
}

def initialize(face_suit)
@face = face_suit[0].chr.to_sym
raise "Invalid face \"#{@face}\"" unless Face_Ranks.has_key?(@face)
@suit = face_suit[1].chr.to_sym
raise "Invalid suit \"#{@suit}\"" unless Suit_Ranks.has_key?(@suit)
freeze
end

def rank                      # Overall ranking in the deck.
index * 4 + Suit_Ranks[@suit]
end

def index                     # Ranking, independent of suit.
Face_Ranks[@face]
end

def to_s
@face.to_s + @suit.to_s
end
end

# A typed collection of up to five cards.
class Hand
include Comparable            # Hands can be compared.

Hand_Names = [
"Folded",
"High Card",
"Pair",
"Two Pair",
"Three of a Kind",
"Straight",
"Flush",
"Full House",
"Four of a Kind",
"Straight Flush",
"Royal Flush"
]

# Define constants by converting "High Card" to Hand::High_Card = 0.
Hand_Names.each_with_index do |n, i|
const_set(n.tr(" ", "_"), i)
end

def initialize(hand_type, cards)
@hand_type = hand_type
@cards = cards.dup
freeze
end

def to_s
@cards.join(" ") + " " + Hand_Names[@hand_type]
end

def <=>(other)
if @hand_type != other.hand_type
# Hand ranking dominates.
return @hand_type <=> other.hand_type

elsif @hand_type == Flush
# Compare corresponding cards, highest to lowest.
@cards.reverse.zip(other.cards.reverse) do |a, b|
return a.index <=> b.index if a.index != b.index
end
return 0

elsif @hand_type == Two_Pair
# Compare the two highest pairs, then the remaining pairs
self_indices = [@cards[0].index, @cards[2].index].sort!
other_indices = [other.cards[0].index, other.cards[2].index].sort!
if self_indices[1] != other_indices[1]
return self_indices[1] <=> other_indices[1]
else
return self_indices[0] <=> other_indices[0]
end

else
# All others types of hand are compared using their first card.
return @cards[0].index <=> other.cards[0].index
end
end
end

# A collection of seven cards, from which Hands are extracted.
class Deal

def initialize(card_string)
# Parse and sort the cards.  The sorting order chosen here is
# important when extracting and comparing hands later.
@all_cards = card_string.split(/ /).collect do |face_suit|
Card.new(face_suit)
end.sort_by { |card| card.rank }
@hands = []
if @all_cards.length == 7
# Extract all possible hands if we got 7 cards.
find_high_card
find_groups
find_two_pairs_and_full_house
find_straight_and_flush
else
# Otherwise, make a folded hand.
end
# Pick the best possible hand and determine the kickers.
@best_hand = @hands.max
@kickers = (@all_cards - @best_hand.cards).sort_by do |card|
-card.rank
end
end

private

@hands << Hand.new(hand_type, cards)
end

def find_high_card
end

def find_groups
# Find the longest run of each face in @all_cards.
start = 0
while @all_cards[start]
for stop in ((start + 1).. / all_cards.length)
next if @all_cards[stop] and
(@all_cards[start].face == @all_cards[stop].face)
case (stop - start)
when 4:
when 3:
when 2:
end
break
end
start = stop
end
end

def find_two_pairs_and_full_house
pairs = @hands.find_all do |h|
h.hand_type == Hand::Pair
end
threes = @hands.find_all do |h|
h.hand_type == Hand::Three_of_a_Kind
end
# Find up to three combinations of two pairs.
if (pairs.length > 1)
Combine.pick(2, pairs) do |pair_hands|
pair_hands[0].cards + pair_hands[1].cards)
end
end
# Each combination of a pair and three-of-a-kind is a full house.
pairs.each do |pair|
threes.each do |three|
end
end
# Two three-of-a-kinds yield two possible full-houses.
if (threes.length > 1)
threes[0].cards + threes[1].cards[0..1])
threes[1].cards + threes[0].cards[0..1])
end
# We could combine four-of-a-kind and a pair for a full-house
# but four-of-a-kind already beats a full-house.
end

def find_straight_and_flush
# Examine all combinations of five cards
Combine.pick(5, @all_cards) do |cards|
is_flush = true
is_straight = true
1.upto(4) do |i|
is_straight = false if
(cards[i].index != cards[i - 1].index + 1)
is_flush = false if
(cards[i].suit != cards[0].suit)
end
# Add the best hand found in this iteration.
case
when (is_straight and is_flush and cards[0].face == :"T")
when (is_straight and is_flush)
when (is_flush)
when (is_straight)
end
end
end

end

# A card player that holds a Hand and some kickers.
class Player
attr_accessor :wins

def initialize(hand, kickers)
@hand = hand
@kickers = kickers
@wins = false
end

# Return <=> value comparing kickers from another Player.
def compare_kickers(other)
@kickers.zip(other.kickers) do |a_kicker, b_kicker|
return  1 if a_kicker.index > b_kicker.index
return -1 if a_kicker.index < b_kicker.index
end
return 0
end
end

players = []
while line = gets
line.chomp!
# Take first 20 chars only, making it easy to use previously
# printed results as input for re-testing.
deal = Deal.new(line[0, 20])
players << Player.new(deal.best_hand, deal.kickers)
end

# Find the winner(s).

winners = []
players.each do |player|
if winners.empty?
winners << player
elsif player.hand > winners[0].hand
winners.clear
winners << player
elsif player.hand == winners[0].hand
# Try to resolve ties based on kickers.
comparison = player.compare_kickers(winners[0])
if comparison >= 0
winners.clear if comparison > 0
winners << player
end
end
end
winners.each { |player| player.wins = true }

# Report the results.

players.each do |player|
# Print cards sorted by face with kickers at the end.
print((player.hand.cards + player.kickers).join(" "))
# Print description of hand and (winner) flag
if player.hand.hand_type > 0
print " ", Hand::Hand_Names[player.hand.hand_type]
print " (winner)" if player.wins
end
print "\n"
end

```