[QUIZ SOLUTION] Texas Hold'Em (#24)

This was a fun one :slight_smile:

I'm looking forward to seeing other solutions.

Regs,
Derek

holdem.rb (10.5 KB)

I will concur this one was fun, although it took me more like 4 hours
than the one I planned on spending on it. I think this a complete
solution. There is no smarts in the folding logic, but the player
class is the place to make it smarter. It displays each round
(unranked, but with current "score/status" and then when the game is
over it rearranges the hands to display them based upon their best
hand and ranks them from best to worst (folded hands are just randomly
layed out at the bottom.

Thanks as always for the quiz.
Patrick

路路路

----------------------------------------------------------------------------------

#!ruby -w

class Card
聽聽SUITS = "cdhs"
聽聽FACES = "L23456789TJQKA"
聽聽SUIT_LOOKUP = {
聽聽聽聽'c' => 0,
聽聽聽聽'd' => 1,
聽聽聽聽'h' => 2,
聽聽聽聽's' => 3,
聽聽聽聽'C' => 0,
聽聽聽聽'D' => 1,
聽聽聽聽'H' => 2,
聽聽聽聽'S' => 3,
聽聽}
聽聽FACE_VALUES = {
聽聽聽聽'L' => 1, # this is a magic low ace
聽聽聽聽'2' => 2,
聽聽聽聽'3' => 3,
聽聽聽聽'4' => 4,
聽聽聽聽'5' => 5,
聽聽聽聽'6' => 6,
聽聽聽聽'7' => 7,
聽聽聽聽'8' => 8,
聽聽聽聽'9' => 9,
聽聽聽聽'T' => 10,
聽聽聽聽'J' => 11,
聽聽聽聽'Q' => 12,
聽聽聽聽'K' => 13,
聽聽聽聽'A' => 14,
聽聽}

聽聽def Card.face_value(face)
聽聽聽聽if (face)
聽聽聽聽聽聽FACE_VALUES[face] - 1
聽聽聽聽else
聽聽聽聽聽聽nil
聽聽聽聽end
聽聽end

聽聽def build_from_string(card)
聽聽聽聽build_from_face_suit(card[0,1], card[1,1])
聽聽end

聽聽def build_from_value(value)
聽聽聽聽@value = value
聽聽聽聽@suit = value / FACES.size()
聽聽聽聽@face = (value % FACES.size())
聽聽end

聽聽def build_from_face_suit(face, suit)
聽聽聽聽@face = Card::face_value(face)
聽聽聽聽@suit = SUIT_LOOKUP[suit]
聽聽聽聽@value = (@suit * FACES.size()) + (@face - 1)
聽聽end

聽聽def build_from_face_suit_values(face, suit)
聽聽聽聽build_from_value((face - 1) + (suit * FACES.size()))
聽聽end

聽聽# got a little carried away with this constructor :wink:
聽聽def initialize(*value)
聽聽聽聽if (value.size == 1)
聽聽聽聽聽聽if (value[0].respond_to?(:to_str))
聽聽聽聽聽聽聽聽build_from_string(value[0])
聽聽聽聽聽聽elsif (value[0].respond_to?(:to_int))
聽聽聽聽聽聽聽聽build_from_value(value[0])
聽聽聽聽聽聽end
聽聽聽聽elsif (value.size == 2)
聽聽聽聽聽聽if (value[0].respond_to?(:to_str) && value[1].respond_to?(:to_str))
聽聽聽聽聽聽聽聽build_from_face_suit(value[0], value[1])
聽聽聽聽聽聽elsif (value[0].respond_to?(:to_int) && value[1].respond_to?(:to_int))
聽聽聽聽聽聽聽聽build_from_face_suit_values(value[0], value[1])
聽聽聽聽聽聽end
聽聽聽聽end
聽聽end

聽聽attr_reader :suit, :face, :value

聽聽def to_s
聽聽聽聽FACES[@face].chr + SUITS[@suit].chr
聽聽end
end

class Deck
聽聽def shuffle
聽聽聽聽deck_size = @cards.size
聽聽聽聽(deck_size * 2).times do
聽聽聽聽聽聽pos1, pos2 = rand(deck_size), rand(deck_size)
聽聽聽聽聽聽@cards[pos1], @cards[pos2] = @cards[pos2], @cards[pos1]
聽聽聽聽end
聽聽end

聽聽def initialize
聽聽聽聽@cards = []
聽聽聽聽Card::SUITS.each_byte do |suit|
聽聽聽聽聽聽# careful not to double include the aces...
聽聽聽聽聽聽Card::FACES[1..-1].each_byte do |face|
聽聽聽聽聽聽聽聽@cards.push(Card.new(face.chr, suit.chr))
聽聽聽聽聽聽end
聽聽聽聽end
聽聽聽聽shuffle()
聽聽end

聽聽def deal
聽聽聽聽@cards.pop
聽聽end

聽聽def empty?
聽聽聽聽@cards.empty?
聽聽end
end

class Hand
聽聽def initialize(cards = [])
聽聽聽聽if (cards.respond_to?(:to_str))
聽聽聽聽聽聽@hand = cards.scan(/\S\S/).map { |str| Card.new(str) }
聽聽聽聽else
聽聽聽聽聽聽@hand = cards
聽聽聽聽end
聽聽end
聽聽attr_reader :hand

聽聽def face_values
聽聽聽聽@hand.map { |c| c.face }
聽聽end

聽聽def by_suit
聽聽聽聽Hand.new(@hand.sort_by { |c| [c.suit, c.face] }.reverse)
聽聽end

聽聽def by_face
聽聽聽聽Hand.new(@hand.sort_by { |c| [c.face, c.suit] }.reverse)
聽聽end

聽聽def =~ (re)
聽聽聽聽re.match(@hand.join(' '))
聽聽end

聽聽def royal_flush?
聽聽聽聽if (md = (by_suit =~ /A(.) K\1 Q\1 J\1 T\1/))
聽聽聽聽聽聽[[10], (md[0] + ' ' + md.pre_match + ' ' +
md.post_match).gsub(/\s+/, ' ')]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def delta_transform(use_suit = false)
聽聽聽聽aces = @hand.select { |c| c.face == Card::face_value('A') }
聽聽聽聽aces.map! { |c| Card.new(1,c.suit) }

聽聽聽聽base = if (use_suit)
聽聽聽聽聽聽(@hand + aces).sort_by { |c| [c.suit, c.face] }.reverse
聽聽聽聽else
聽聽聽聽聽聽(@hand + aces).sort_by { |c| [c.face, c.suit] }.reverse
聽聽聽聽end

聽聽聽聽result = base.inject(['',nil]) do |(delta_hand, prev_card), card|
聽聽聽聽聽聽if (prev_card)
聽聽聽聽聽聽聽聽delta = prev_card - card.face
聽聽聽聽聽聽else
聽聽聽聽聽聽聽聽delta = 0
聽聽聽聽聽聽end
聽聽聽聽聽聽delta = 'x' if (delta > 9 || delta < 0) # does not really
matter for my needs
聽聽聽聽聽聽delta_hand += delta.to_s + card.to_s + ' '
聽聽聽聽聽聽[delta_hand, card.face]
聽聽聽聽end

聽聽聽聽# we just want the delta transform, not the last cards face too
聽聽聽聽result[0]
聽聽end

聽聽def fix_low_ace_display(arranged_hand)
聽聽聽聽# remove card deltas (this routine is only used for straights)
聽聽聽聽arranged_hand.gsub!(/\S(\S\S)\s+/, "\\1 ")

聽聽聽聽# Fix "low aces"
聽聽聽聽arranged_hand.gsub!(/L(\S)/, "A\\1")

聽聽聽聽# Remove duplicate aces (this will not work if you have multiple
decks or wild cards)
聽聽聽聽arranged_hand.gsub!(/((A\S).*)\2/, "\\1")

聽聽聽聽# cleanup white space
聽聽聽聽arranged_hand.gsub!(/\s+/, ' ')
聽聽聽聽arranged_hand.gsub(/\s+$/, '') # careful to use gsub as gsub!
can return nil here
聽聽end

聽聽def straight_flush?
聽聽聽聽if (md = (/.(.)(.) 1.\2 1.\2 1.\2 1.\2/.match(delta_transform(true))))
聽聽聽聽聽聽high_card = Card::face_value(md[1])
聽聽聽聽聽聽arranged_hand = fix_low_ace_display(md[0] + ' ' + md.pre_match +
' ' + md.post_match)
聽聽聽聽聽聽[[9, high_card], arranged_hand]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def arrange_hand(md)
聽聽聽聽聽聽hand = if (md.respond_to?(:to_str))
聽聽聽聽聽聽聽聽md
聽聽聽聽聽聽else
聽聽聽聽聽聽聽聽md[0] + ' ' + md.pre_match + md.post_match
聽聽聽聽聽聽end
聽聽聽聽聽聽hand.gsub!(/\s+/, ' ')
聽聽聽聽聽聽hand.gsub(/\s+$/,'')
聽聽end

聽聽def four_of_a_kind?
聽聽聽聽if (md = (by_face =~ /(.). \1. \1. \1./))
聽聽聽聽聽聽# get kicker
聽聽聽聽聽聽(md.pre_match + md.post_match).match(/(\S)/)
聽聽聽聽聽聽[[8, Card::face_value(md[1]), Card::face_value($1)], arrange_hand(md)]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def full_house?
聽聽聽聽if (md = (by_face =~ /(.). \1. \1. (.*)(.). \3./))
聽聽聽聽聽聽arranged_hand = arrange_hand(md[0] + ' ' + md.pre_match + ' ' +
md[2] + ' ' + md.post_match)
聽聽聽聽聽聽[[7, Card::face_value(md[1]), Card::face_value(md[3])], arranged_hand]
聽聽聽聽elsif (md = (by_face =~ /((.). \2.) (.*)((.). \5. \5.)/))
聽聽聽聽聽聽arranged_hand = arrange_hand(md[4] + ' ' + md[1] + ' ' +
md.pre_match + ' ' + md[3] + ' ' + md.post_match)
聽聽聽聽聽聽[[7, Card::face_value(md[5]), Card::face_value(md[2])], arranged_hand]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def flush?
聽聽聽聽if (md = (by_suit =~ /(.)(.) (.)\2 (.)\2 (.)\2 (.)\2/))
聽聽聽聽聽聽[[6, Card::face_value(md[1]), *(md[3..6].map { |f|
Card::face_value(f) })], arrange_hand(md)]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def straight?
聽聽聽聽if (md = (/.(.). 1.. 1.. 1.. 1../.match(delta_transform)))
聽聽聽聽聽聽high_card = Card::face_value(md[1])
聽聽聽聽聽聽arranged_hand = fix_low_ace_display(md[0] + ' ' + md.pre_match +
' ' + md.post_match)
聽聽聽聽聽聽[[5, high_card], arranged_hand]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def three_of_a_kind?
聽聽聽聽if (md = (by_face =~ /(.). \1. \1./))
聽聽聽聽聽聽# get kicker
聽聽聽聽聽聽arranged_hand = arrange_hand(md)
聽聽聽聽聽聽arranged_hand.match(/(?:\S\S ){3}(\S)\S (\S)/)
聽聽聽聽聽聽[[4, Card::face_value(md[1]), Card::face_value($1),
Card::face_value($2)], arranged_hand]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def two_pair?
聽聽聽聽if (md = (by_face =~ /(.). \1.(.*) (.). \3./))
聽聽聽聽聽聽# get kicker
聽聽聽聽聽聽arranged_hand = arrange_hand(md[0] + ' ' + md.pre_match + ' ' +
md[2] + ' ' + md.post_match)
聽聽聽聽聽聽arranged_hand.match(/(?:\S\S ){4}(\S)/)
聽聽聽聽聽聽[[3, Card::face_value(md[1]), Card::face_value(md[3]),
Card::face_value($1)], arranged_hand]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def pair?
聽聽聽聽if (md = (by_face =~ /(.). \1./))
聽聽聽聽聽聽# get kicker
聽聽聽聽聽聽arranged_hand = arrange_hand(md)
聽聽聽聽聽聽arranged_hand.match(/(?:\S\S ){2}(\S)\S\s+(\S)\S\s+(\S)/)
聽聽聽聽聽聽# (' ' + md.pre_match + md.post_match).match(/^\s+(\S)\S\s+(\S)\S\s+(\S)/)
聽聽聽聽聽聽[[2, Card::face_value(md[1]), Card::face_value($1),
Card::face_value($2), Card::face_value($3)], arranged_hand]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def highest_card?
聽聽聽聽result = by_face
聽聽聽聽[[1, *result.face_values[0..4]], result.hand.join(' ')]
聽聽end

聽聽OPS = [
聽聽聽聽['Royal Flush', :royal_flush? ],
聽聽聽聽['Straight Flush', :straight_flush? ],
聽聽聽聽['Four of a kind', :four_of_a_kind? ],
聽聽聽聽['Full house', :full_house? ],
聽聽聽聽['Flush', :flush? ],
聽聽聽聽['Straight', :straight? ],
聽聽聽聽['Three of a kind', :three_of_a_kind?],
聽聽聽聽['Two pair', :two_pair? ],
聽聽聽聽['Pair', :pair? ],
聽聽聽聽['Highest Card', :highest_card? ],
聽聽]

聽聽def hand_rating
聽聽聽聽OPS.map { |op| (method(op[1]).call()) ? op[0] : false }. find { |v| v }
聽聽end

聽聽def score
聽聽聽聽OPS.map { |op| method(op[1]).call() }.find([0]) { |score| score }
聽聽end

聽聽def take_card(card)
聽聽聽聽@hand.push(card)
聽聽end

聽聽def arranged_hand
聽聽聽聽score[1] + " (#{hand_rating})"
聽聽end

聽聽def just_cards
聽聽聽聽@hand.join(" ")
聽聽end

聽聽def to_s
聽聽聽聽just_cards + " (" + hand_rating + ")"
聽聽end
end

class Player
聽聽def initialize(name, deck)
聽聽聽聽@name = name
聽聽聽聽@hand = Hand.new
聽聽聽聽2.times { @hand.take_card(deck.deal()) }
聽聽聽聽@folded = false
聽聽end

聽聽def folded?
聽聽聽聽@folded
聽聽end

聽聽def take_card(card)
聽聽聽聽@hand.take_card(card)
聽聽end

聽聽def fold?(players)
聽聽聽聽unless (folded?)
聽聽聽聽聽聽if (players)
聽聽聽聽聽聽聽聽folded_count = players.inject(0) { |count, p| (p.folded?) ?
count + 1 : count }
聽聽聽聽聽聽聽聽@folded = rand(players.size - folded_count) > (folded_count)
聽聽聽聽聽聽else
聽聽聽聽聽聽聽聽@folded = (rand(10) <= 1)
聽聽聽聽聽聽end
聽聽聽聽end
聽聽聽聽folded?

聽聽end

聽聽def score
聽聽聽聽(folded?) ? [[0]] : @hand.score
聽聽end

聽聽def arranged_hand
聽聽聽聽@name + ' ' +
聽聽聽聽if (folded?)
聽聽聽聽聽聽@hand.just_cards + ' (folded)'
聽聽聽聽else
聽聽聽聽聽聽@hand.arranged_hand
聽聽聽聽end
聽聽end

聽聽def to_s
聽聽聽聽@name + ' ' +
聽聽聽聽if (folded?)
聽聽聽聽聽聽@hand.just_cards + ' (folded)'
聽聽聽聽else
聽聽聽聽聽聽@hand.to_s
聽聽聽聽end
聽聽end

聽聽def <=>(other)
聽聽聽聽score <=> other.score
聽聽end
end

class TexasHoldEm
聽聽def initialize(player_count)
聽聽聽聽@deck = Deck.new
聽聽聽聽@common_cards = Array.new(5) { @deck.deal }
聽聽聽聽@players = (1..player_count).inject([]) { |players, num| players
<< Player.new("Player #{num}", @deck) }
聽聽end

聽聽def game_over?
聽聽聽聽@common_cards.empty?
聽聽end

聽聽def play_round
聽聽聽聽unless game_over?
聽聽聽聽聽聽card = @common_cards.pop
聽聽聽聽聽聽@players.each do |p|
聽聽聽聽聽聽聽聽unless p.fold?(@players)
聽聽聽聽聽聽聽聽聽聽p.take_card(card)
聽聽聽聽聽聽聽聽end
聽聽聽聽聽聽end
聽聽聽聽end

聽聽聽聽game_over?
聽聽end

聽聽def rank_players!
聽聽聽聽@players = @players.sort.reverse
聽聽end

聽聽def arranged_players
聽聽聽聽@players.inject('') { |result, player| result +=
player.arranged_hand + "\n" }
聽聽end

聽聽def to_s
聽聽聽聽@players.join("\n")
聽聽end
end

if __FILE__ == $0
聽聽srand

聽聽game = TexasHoldEm.new(5)
聽聽round = 1
聽聽until game.game_over?
聽聽聽聽puts "\nRound #{round}"
聽聽聽聽puts game
聽聽聽聽game.play_round
聽聽聽聽round += 1
聽聽end
聽聽puts "\nRound #{round}"
聽聽puts game

聽聽game.rank_players!
聽聽puts "\nFinal Ranking"
聽聽puts game.arranged_players
end

Find below a very slightly modified version of my quiz submission
where none of the lines exceed 72 charaters in width. I have become
"lazy" in that my editors on my 20" LCDs have no difficulty with lines
exceeding 100 chars. I have also sometime since I got those new
monitors started printing code in landscape.

Alas I have forgotton other people and so to make amends here is the
reformatted quiz submission.

Grinning :slight_smile:
Patrick

路路路

----------------------------------------------------------------------------------------------------------------

#!ruby -w

class Card
聽聽SUITS = "cdhs"
聽聽FACES = "L23456789TJQKA"
聽聽SUIT_LOOKUP = {
聽聽聽聽'c' => 0,
聽聽聽聽'd' => 1,
聽聽聽聽'h' => 2,
聽聽聽聽's' => 3,
聽聽聽聽'C' => 0,
聽聽聽聽'D' => 1,
聽聽聽聽'H' => 2,
聽聽聽聽'S' => 3,
聽聽}
聽聽FACE_VALUES = {
聽聽聽聽'L' => 1, # this is a magic low ace
聽聽聽聽'2' => 2,
聽聽聽聽'3' => 3,
聽聽聽聽'4' => 4,
聽聽聽聽'5' => 5,
聽聽聽聽'6' => 6,
聽聽聽聽'7' => 7,
聽聽聽聽'8' => 8,
聽聽聽聽'9' => 9,
聽聽聽聽'T' => 10,
聽聽聽聽'J' => 11,
聽聽聽聽'Q' => 12,
聽聽聽聽'K' => 13,
聽聽聽聽'A' => 14,
聽聽}

聽聽def Card.face_value(face)
聽聽聽聽if (face)
聽聽聽聽聽聽FACE_VALUES[face] - 1
聽聽聽聽else
聽聽聽聽聽聽nil
聽聽聽聽end
聽聽end

聽聽def build_from_string(card)
聽聽聽聽build_from_face_suit(card[0,1], card[1,1])
聽聽end

聽聽def build_from_value(value)
聽聽聽聽@value = value
聽聽聽聽@suit = value / FACES.size()
聽聽聽聽@face = (value % FACES.size())
聽聽end

聽聽def build_from_face_suit(face, suit)
聽聽聽聽@face = Card::face_value(face)
聽聽聽聽@suit = SUIT_LOOKUP[suit]
聽聽聽聽@value = (@suit * FACES.size()) + (@face - 1)
聽聽end

聽聽def build_from_face_suit_values(face, suit)
聽聽聽聽build_from_value((face - 1) + (suit * FACES.size()))
聽聽end

聽聽# got a little carried away with this constructor :wink:
聽聽def initialize(*value)
聽聽聽聽if (value.size == 1)
聽聽聽聽聽聽if (value[0].respond_to?(:to_str))
聽聽聽聽聽聽聽聽build_from_string(value[0])
聽聽聽聽聽聽elsif (value[0].respond_to?(:to_int))
聽聽聽聽聽聽聽聽build_from_value(value[0])
聽聽聽聽聽聽end
聽聽聽聽elsif (value.size == 2)
聽聽聽聽聽聽if (value[0].respond_to?(:to_str) &&
聽聽聽聽聽聽聽聽聽聽value[1].respond_to?(:to_str))
聽聽聽聽聽聽聽聽build_from_face_suit(value[0], value[1])
聽聽聽聽聽聽elsif (value[0].respond_to?(:to_int) &&
聽聽聽聽聽聽聽聽聽聽聽聽聽value[1].respond_to?(:to_int))
聽聽聽聽聽聽聽聽build_from_face_suit_values(value[0], value[1])
聽聽聽聽聽聽end
聽聽聽聽end
聽聽end

聽聽attr_reader :suit, :face, :value

聽聽def to_s
聽聽聽聽FACES[@face].chr + SUITS[@suit].chr
聽聽end
end

class Deck
聽聽def shuffle
聽聽聽聽deck_size = @cards.size
聽聽聽聽(deck_size * 2).times do
聽聽聽聽聽聽pos1, pos2 = rand(deck_size), rand(deck_size)
聽聽聽聽聽聽@cards[pos1], @cards[pos2] = @cards[pos2], @cards[pos1]
聽聽聽聽end
聽聽end

聽聽def initialize
聽聽聽聽@cards = []
聽聽聽聽Card::SUITS.each_byte do |suit|
聽聽聽聽聽聽# careful not to double include the aces...
聽聽聽聽聽聽Card::FACES[1..-1].each_byte do |face|
聽聽聽聽聽聽聽聽@cards.push(Card.new(face.chr, suit.chr))
聽聽聽聽聽聽end
聽聽聽聽end
聽聽聽聽shuffle()
聽聽end

聽聽def deal
聽聽聽聽@cards.pop
聽聽end

聽聽def empty?
聽聽聽聽@cards.empty?
聽聽end
end

class Hand
聽聽def initialize(cards = [])
聽聽聽聽if (cards.respond_to?(:to_str))
聽聽聽聽聽聽@hand = cards.scan(/\S\S/).map { |str| Card.new(str) }
聽聽聽聽else
聽聽聽聽聽聽@hand = cards
聽聽聽聽end
聽聽end
聽聽attr_reader :hand

聽聽def face_values
聽聽聽聽@hand.map { |c| c.face }
聽聽end

聽聽def by_suit
聽聽聽聽Hand.new(@hand.sort_by { |c| [c.suit, c.face] }.reverse)
聽聽end

聽聽def by_face
聽聽聽聽Hand.new(@hand.sort_by { |c| [c.face, c.suit] }.reverse)
聽聽end

聽聽def =~ (re)
聽聽聽聽re.match(@hand.join(' '))
聽聽end

聽聽def arrange_hand(md)
聽聽聽聽聽聽hand = if (md.respond_to?(:to_str))
聽聽聽聽聽聽聽聽md
聽聽聽聽聽聽else
聽聽聽聽聽聽聽聽md[0] + ' ' + md.pre_match + md.post_match
聽聽聽聽聽聽end
聽聽聽聽聽聽hand.gsub!(/\s+/, ' ')
聽聽聽聽聽聽hand.gsub(/\s+$/,'')
聽聽end

聽聽def royal_flush?
聽聽聽聽if (md = (by_suit =~ /A(.) K\1 Q\1 J\1 T\1/))
聽聽聽聽聽聽[[10], arrange_hand(md)]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def delta_transform(use_suit = false)
聽聽聽聽aces = @hand.select { |c| c.face == Card::face_value('A') }
聽聽聽聽aces.map! { |c| Card.new(1,c.suit) }

聽聽聽聽base = if (use_suit)
聽聽聽聽聽聽(@hand + aces).sort_by { |c| [c.suit, c.face] }.reverse
聽聽聽聽else
聽聽聽聽聽聽(@hand + aces).sort_by { |c| [c.face, c.suit] }.reverse
聽聽聽聽end

聽聽聽聽result = base.inject(['',nil]) do |(delta_hand, prev_card), card|
聽聽聽聽聽聽if (prev_card)
聽聽聽聽聽聽聽聽delta = prev_card - card.face
聽聽聽聽聽聽else
聽聽聽聽聽聽聽聽delta = 0
聽聽聽聽聽聽end
聽聽聽聽聽聽# does not really matter for my needs
聽聽聽聽聽聽delta = 'x' if (delta > 9 || delta < 0)
聽聽聽聽聽聽delta_hand += delta.to_s + card.to_s + ' '
聽聽聽聽聽聽[delta_hand, card.face]
聽聽聽聽end

聽聽聽聽# we just want the delta transform, not the last cards face too
聽聽聽聽result[0]
聽聽end

聽聽def fix_low_ace_display(arranged_hand)
聽聽聽聽# remove card deltas (this routine is only used for straights)
聽聽聽聽arranged_hand.gsub!(/\S(\S\S)\s+/, "\\1 ")

聽聽聽聽# Fix "low aces"
聽聽聽聽arranged_hand.gsub!(/L(\S)/, "A\\1")

聽聽聽聽# Remove duplicate aces (this will not work if you have
聽聽聽聽# multiple decks or wild cards)
聽聽聽聽arranged_hand.gsub!(/((A\S).*)\2/, "\\1")

聽聽聽聽# cleanup white space
聽聽聽聽arranged_hand.gsub!(/\s+/, ' ')
聽聽聽聽# careful to use gsub as gsub! can return nil here
聽聽聽聽arranged_hand.gsub(/\s+$/, '')
聽聽end

聽聽def straight_flush?
聽聽聽聽if (md = (/.(.)(.)(?: 1.\2){4}/.match(delta_transform(true))))
聽聽聽聽聽聽high_card = Card::face_value(md[1])
聽聽聽聽聽聽arranged_hand = fix_low_ace_display(md[0] + ' ' +
聽聽聽聽聽聽聽聽聽聽md.pre_match + ' ' + md.post_match)
聽聽聽聽聽聽[[9, high_card], arranged_hand]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def four_of_a_kind?
聽聽聽聽if (md = (by_face =~ /(.). \1. \1. \1./))
聽聽聽聽聽聽# get kicker
聽聽聽聽聽聽(md.pre_match + md.post_match).match(/(\S)/)
聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽[8, Card::face_value(md[1]), Card::face_value($1)],
聽聽聽聽聽聽聽聽arrange_hand(md)
聽聽聽聽聽聽]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def full_house?
聽聽聽聽if (md = (by_face =~ /(.). \1. \1. (.*)(.). \3./))
聽聽聽聽聽聽arranged_hand = arrange_hand(md[0] + ' ' +
聽聽聽聽聽聽聽聽聽聽md.pre_match + ' ' + md[2] + ' ' + md.post_match)
聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽[7, Card::face_value(md[1]), Card::face_value(md[3])],
聽聽聽聽聽聽聽聽arranged_hand
聽聽聽聽聽聽]
聽聽聽聽elsif (md = (by_face =~ /((.). \2.) (.*)((.). \5. \5.)/))
聽聽聽聽聽聽arranged_hand = arrange_hand(md[4] + ' ' + md[1] + ' ' +
聽聽聽聽聽聽聽聽聽聽md.pre_match + ' ' + md[3] + ' ' + md.post_match)
聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽[7, Card::face_value(md[5]), Card::face_value(md[2])],
聽聽聽聽聽聽聽聽arranged_hand
聽聽聽聽聽聽]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def flush?
聽聽聽聽if (md = (by_suit =~ /(.)(.) (.)\2 (.)\2 (.)\2 (.)\2/))
聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽聽聽6,
聽聽聽聽聽聽聽聽聽聽Card::face_value(md[1]),
聽聽聽聽聽聽聽聽聽聽*(md[3..6].map { |f| Card::face_value(f) })
聽聽聽聽聽聽聽聽],
聽聽聽聽聽聽聽聽arrange_hand(md)
聽聽聽聽聽聽]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def straight?
聽聽聽聽if (md = (/.(.). 1.. 1.. 1.. 1../.match(delta_transform)))
聽聽聽聽聽聽high_card = Card::face_value(md[1])
聽聽聽聽聽聽arranged_hand = fix_low_ace_display(md[0] + ' ' +
聽聽聽聽聽聽聽聽聽聽md.pre_match + ' ' + md.post_match)
聽聽聽聽聽聽[[5, high_card], arranged_hand]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def three_of_a_kind?
聽聽聽聽if (md = (by_face =~ /(.). \1. \1./))
聽聽聽聽聽聽# get kicker
聽聽聽聽聽聽arranged_hand = arrange_hand(md)
聽聽聽聽聽聽arranged_hand.match(/(?:\S\S ){3}(\S)\S (\S)/)
聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽聽聽4,
聽聽聽聽聽聽聽聽聽聽Card::face_value(md[1]),
聽聽聽聽聽聽聽聽聽聽Card::face_value($1),
聽聽聽聽聽聽聽聽聽聽Card::face_value($2)
聽聽聽聽聽聽聽聽],
聽聽聽聽聽聽聽聽arranged_hand
聽聽聽聽聽聽]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def two_pair?
聽聽聽聽if (md = (by_face =~ /(.). \1.(.*) (.). \3./))
聽聽聽聽聽聽# get kicker
聽聽聽聽聽聽arranged_hand = arrange_hand(md[0] + ' ' +
聽聽聽聽聽聽聽聽聽聽md.pre_match + ' ' + md[2] + ' ' + md.post_match)
聽聽聽聽聽聽arranged_hand.match(/(?:\S\S ){4}(\S)/)
聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽聽聽3,
聽聽聽聽聽聽聽聽聽聽Card::face_value(md[1]),
聽聽聽聽聽聽聽聽聽聽Card::face_value(md[3]),
聽聽聽聽聽聽聽聽聽聽Card::face_value($1)
聽聽聽聽聽聽聽聽],
聽聽聽聽聽聽聽聽arranged_hand
聽聽聽聽聽聽]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def pair?
聽聽聽聽if (md = (by_face =~ /(.). \1./))
聽聽聽聽聽聽# get kicker
聽聽聽聽聽聽arranged_hand = arrange_hand(md)
聽聽聽聽聽聽arranged_hand.match(/(?:\S\S ){2}(\S)\S\s+(\S)\S\s+(\S)/)
聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽[
聽聽聽聽聽聽聽聽聽聽2,
聽聽聽聽聽聽聽聽聽聽Card::face_value(md[1]),
聽聽聽聽聽聽聽聽聽聽Card::face_value($1),
聽聽聽聽聽聽聽聽聽聽Card::face_value($2),
聽聽聽聽聽聽聽聽聽聽Card::face_value($3)
聽聽聽聽聽聽聽聽],
聽聽聽聽聽聽聽聽arranged_hand
聽聽聽聽聽聽]
聽聽聽聽else
聽聽聽聽聽聽false
聽聽聽聽end
聽聽end

聽聽def highest_card?
聽聽聽聽result = by_face
聽聽聽聽[[1, *result.face_values[0..4]], result.hand.join(' ')]
聽聽end

聽聽OPS = [
聽聽聽聽['Royal Flush', :royal_flush? ],
聽聽聽聽['Straight Flush', :straight_flush? ],
聽聽聽聽['Four of a kind', :four_of_a_kind? ],
聽聽聽聽['Full house', :full_house? ],
聽聽聽聽['Flush', :flush? ],
聽聽聽聽['Straight', :straight? ],
聽聽聽聽['Three of a kind', :three_of_a_kind?],
聽聽聽聽['Two pair', :two_pair? ],
聽聽聽聽['Pair', :pair? ],
聽聽聽聽['Highest Card', :highest_card? ],
聽聽]

聽聽def hand_rating
聽聽聽聽OPS.map { |op|
聽聽聽聽聽聽(method(op[1]).call()) ? op[0] : false
聽聽聽聽}.find { |v| v }
聽聽end

聽聽def score
聽聽聽聽OPS.map { |op|
聽聽聽聽聽聽method(op[1]).call()
聽聽聽聽}.find([0]) { |score| score }
聽聽end

聽聽def take_card(card)
聽聽聽聽@hand.push(card)
聽聽end

聽聽def arranged_hand
聽聽聽聽score[1] + " (#{hand_rating})"
聽聽end

聽聽def just_cards
聽聽聽聽@hand.join(" ")
聽聽end

聽聽def to_s
聽聽聽聽just_cards + " (" + hand_rating + ")"
聽聽end
end

class Player
聽聽def initialize(name, deck)
聽聽聽聽@name = name
聽聽聽聽@hand = Hand.new
聽聽聽聽2.times { @hand.take_card(deck.deal()) }
聽聽聽聽@folded = false
聽聽end

聽聽def folded?
聽聽聽聽@folded
聽聽end

聽聽def take_card(card)
聽聽聽聽@hand.take_card(card)
聽聽end

聽聽def fold?(players)
聽聽聽聽unless (folded?)
聽聽聽聽聽聽if (players)
聽聽聽聽聽聽聽聽folded_count = players.inject(0) { |count, p|
聽聽聽聽聽聽聽聽聽聽(p.folded?) ? count + 1 : count
聽聽聽聽聽聽聽聽}
聽聽聽聽聽聽聽聽@folded = rand(players.size - folded_count) > (folded_count)
聽聽聽聽聽聽else
聽聽聽聽聽聽聽聽@folded = (rand(10) <= 1)
聽聽聽聽聽聽end
聽聽聽聽end
聽聽聽聽folded?

聽聽end

聽聽def score
聽聽聽聽(folded?) ? [[0]] : @hand.score
聽聽end

聽聽def arranged_hand
聽聽聽聽@name + ' ' +
聽聽聽聽if (folded?)
聽聽聽聽聽聽@hand.just_cards + ' (folded)'
聽聽聽聽else
聽聽聽聽聽聽@hand.arranged_hand
聽聽聽聽end
聽聽end

聽聽def to_s
聽聽聽聽@name + ' ' +
聽聽聽聽if (folded?)
聽聽聽聽聽聽@hand.just_cards + ' (folded)'
聽聽聽聽else
聽聽聽聽聽聽@hand.to_s
聽聽聽聽end
聽聽end

聽聽def <=>(other)
聽聽聽聽score <=> other.score
聽聽end
end

class TexasHoldEm
聽聽def initialize(player_count)
聽聽聽聽@deck = Deck.new
聽聽聽聽@common_cards = Array.new(5) { @deck.deal }
聽聽聽聽@players = (1..player_count).inject([]) { |players, num|
聽聽聽聽聽聽players << Player.new("Player #{num}", @deck)
聽聽聽聽}
聽聽end

聽聽def game_over?
聽聽聽聽@common_cards.empty?
聽聽end

聽聽def play_round
聽聽聽聽unless game_over?
聽聽聽聽聽聽card = @common_cards.pop
聽聽聽聽聽聽@players.each do |p|
聽聽聽聽聽聽聽聽unless p.fold?(@players)
聽聽聽聽聽聽聽聽聽聽p.take_card(card)
聽聽聽聽聽聽聽聽end
聽聽聽聽聽聽end
聽聽聽聽end

聽聽聽聽game_over?
聽聽end

聽聽def rank_players!
聽聽聽聽@players = @players.sort.reverse
聽聽end

聽聽def arranged_players
聽聽聽聽@players.inject('') { |result, player|
聽聽聽聽聽聽result += player.arranged_hand + "\n"
聽聽聽聽}
聽聽end

聽聽def to_s
聽聽聽聽@players.join("\n")
聽聽end
end

if __FILE__ == $0
聽聽srand

聽聽game = TexasHoldEm.new(5)
聽聽round = 1
聽聽until game.game_over?
聽聽聽聽puts "\nRound #{round}"
聽聽聽聽puts game
聽聽聽聽game.play_round
聽聽聽聽round += 1
聽聽end
聽聽puts "\nRound #{round}"
聽聽puts game

聽聽game.rank_players!
聽聽puts "\nFinal Ranking"
聽聽puts game.arranged_players
end

[Patrick Hurley <phurley@gmail.com>, 2005-03-23 03.39 CET]

Find below a very slightly modified version of my quiz submission

[...]

  def fix_low_ace_display(arranged_hand)
    # remove card deltas (this routine is only used for straights)
    arranged_hand.gsub!(/\S(\S\S)\s+/, "\\1 ")

    # Fix "low aces"
    arranged_hand.gsub!(/L(\S)/, "A\\1")

    # Remove duplicate aces (this will not work if you have
    # multiple decks or wild cards)
    arranged_hand.gsub!(/((A\S).*)\2/, "\\1")

Why not just delete the low aces (gsub!(/L./, ""))? What am I missing?

BTW, I liked how you detected straights using the delta transform. Clever
idea (for me at least :).

路路路

    # cleanup white space
    arranged_hand.gsub!(/\s+/, ' ')
    # careful to use gsub as gsub! can return nil here
    arranged_hand.gsub(/\s+$/, '')
  end

Thanks - I need to keep the low aces so that they appear in the
correct position in the rearranged hand - if I had just deleted them,
straights with the aces low would have been displayed with the ace out
of order.

I liked the delta transform as well, of course it was the cause of the
bug (delta 0 when pairs were encountered) - if fixed it by shuffling
the delta zeros (excluding the first card) to the back of the hand.

Patrick

路路路

On Wed, 23 Mar 2005 23:02:00 +0900, Carlos <angus@quovadis.com.ar> wrote:

[Patrick Hurley <phurley@gmail.com>, 2005-03-23 03.39 CET]
> Find below a very slightly modified version of my quiz submission
[...]
> def fix_low_ace_display(arranged_hand)
> # remove card deltas (this routine is only used for straights)
> arranged_hand.gsub!(/\S(\S\S)\s+/, "\\1 ")
>
> # Fix "low aces"
> arranged_hand.gsub!(/L(\S)/, "A\\1")
>
> # Remove duplicate aces (this will not work if you have
> # multiple decks or wild cards)
> arranged_hand.gsub!(/((A\S).*)\2/, "\\1")

Why not just delete the low aces (gsub!(/L./, ""))? What am I missing?

BTW, I liked how you detected straights using the delta transform. Clever
idea (for me at least :).

>
> # cleanup white space
> arranged_hand.gsub!(/\s+/, ' ')
> # careful to use gsub as gsub! can return nil here
> arranged_hand.gsub(/\s+$/, '')
> end

And since I never saw that version of the program hit the list, here it is:

#!ruby -w

class Card
   SUITS = "cdhs"
   FACES = "L23456789TJQKA"
   SUIT_LOOKUP = {
     'c' => 0,
     'd' => 1,
     'h' => 2,
     's' => 3,
     'C' => 0,
     'D' => 1,
     'H' => 2,
     'S' => 3,
   }
   FACE_VALUES = {
     'L' => 1, # this is a magic low ace
     '2' => 2,
     '3' => 3,
     '4' => 4,
     '5' => 5,
     '6' => 6,
     '7' => 7,
     '8' => 8,
     '9' => 9,
     'T' => 10,
     'J' => 11,
     'Q' => 12,
     'K' => 13,
     'A' => 14,
   }

   def Card.face_value(face)
     if (face)
       FACE_VALUES[face] - 1
     else
       nil
     end
   end

   def build_from_string(card)
     build_from_face_suit(card[0,1], card[1,1])
   end

   def build_from_value(value)
     @value = value
     @suit = value / FACES.size()
     @face = (value % FACES.size())
   end

   def build_from_face_suit(face, suit)
     @face = Card::face_value(face)
     @suit = SUIT_LOOKUP[suit]
     @value = (@suit * FACES.size()) + (@face - 1)
   end

   def build_from_face_suit_values(face, suit)
     build_from_value((face - 1) + (suit * FACES.size()))
   end

   # got a little carried away with this constructor :wink:
   def initialize(*value)
     if (value.size == 1)
       if (value[0].respond_to?(:to_str))
         build_from_string(value[0])
       elsif (value[0].respond_to?(:to_int))
         build_from_value(value[0])
       end
     elsif (value.size == 2)
       if (value[0].respond_to?(:to_str) &&
           value[1].respond_to?(:to_str))
         build_from_face_suit(value[0], value[1])
       elsif (value[0].respond_to?(:to_int) &&
              value[1].respond_to?(:to_int))
         build_from_face_suit_values(value[0], value[1])
       end
     end
   end

   attr_reader :suit, :face, :value

   def to_s
     FACES[@face].chr + SUITS[@suit].chr
   end
end

class Deck
   def shuffle
     deck_size = @cards.size
     (deck_size * 2).times do
       pos1, pos2 = rand(deck_size), rand(deck_size)
       @cards[pos1], @cards[pos2] = @cards[pos2], @cards[pos1]
     end
   end

   def initialize
     @cards =
     Card::SUITS.each_byte do |suit|
       # careful not to double include the aces...
       Card::FACES[1..-1].each_byte do |face|
         @cards.push(Card.new(face.chr, suit.chr))
       end
     end
     shuffle()
   end

   def deal
     @cards.pop
   end

   def empty?
     @cards.empty?
   end
end

class Hand
   def initialize(cards = )
     if (cards.respond_to?(:to_str))
       @hand = cards.scan(/\S\S/).map { |str| Card.new(str) }
     else
       @hand = cards
     end
   end
   attr_reader :hand

   def face_values
     @hand.map { |c| c.face }
   end

   def by_suit
     Hand.new(@hand.sort_by { |c| [c.suit, c.face] }.reverse)
   end

   def by_face
     Hand.new(@hand.sort_by { |c| [c.face, c.suit] }.reverse)
   end

   def =~ (re)
     re.match(@hand.join(' '))
   end

   def arrange_hand(md)
       hand = if (md.respond_to?(:to_str))
         md
       else
         md[0] + ' ' + md.pre_match + md.post_match
       end
       hand.gsub!(/\s+/, ' ')
       hand.gsub(/\s+$/,'')
   end

   def royal_flush?
     if (md = (by_suit =~ /A(.) K\1 Q\1 J\1 T\1/))
       [[10], arrange_hand(md)]
     else
       false
     end
   end

   def delta_transform(use_suit = false)
     aces = @hand.select { |c| c.face == Card::face_value('A') }
     aces.map! { |c| Card.new(1,c.suit) }

     base = if (use_suit)
       (@hand + aces).sort_by { |c| [c.suit, c.face] }.reverse
     else
       (@hand + aces).sort_by { |c| [c.face, c.suit] }.reverse
     end

     result = base.inject(['',nil]) do |(delta_hand, prev_card), card|
       if (prev_card)
         delta = prev_card - card.face
       else
         delta = 0
       end
       # does not really matter for my needs
       delta = 'x' if (delta > 9 || delta < 0)
       delta_hand += delta.to_s + card.to_s + ' '
       [delta_hand, card.face]
     end

     # we just want the delta transform, not the last cards face too
     result[0].chop
   end

   def fix_low_ace_display(arranged_hand)
     # remove card deltas (this routine is only used for straights)
     arranged_hand.gsub!(/\S(\S\S)\s*/, "\\1 ")

     # Fix "low aces"
     arranged_hand.gsub!(/L(\S)/, "A\\1")

     # Remove duplicate aces (this will not work if you have
     # multiple decks or wild cards)
     arranged_hand.gsub!(/((A\S).*)\2/, "\\1")

     # cleanup white space
     arranged_hand.gsub!(/\s+/, ' ')
     # careful to use gsub as gsub! can return nil here
     arranged_hand.gsub(/\s+$/, '')
   end

   def straight_flush?
     if (md = (/.(.)(.)(?: 1.\2){4}/.match(delta_transform(true))))
       high_card = Card::face_value(md[1])
       arranged_hand = fix_low_ace_display(md[0] + ' ' +
           md.pre_match + ' ' + md.post_match)
       [[9, high_card], arranged_hand]
     else
       false
     end
   end

   def four_of_a_kind?
     if (md = (by_face =~ /(.). \1. \1. \1./))
       # get kicker
       (md.pre_match + md.post_match).match(/(\S)/)
       [
         [8, Card::face_value(md[1]), Card::face_value($1)],
         arrange_hand(md)
       ]
     else
       false
     end
   end

   def full_house?
     if (md = (by_face =~ /(.). \1. \1. (.*)(.). \3./))
       arranged_hand = arrange_hand(md[0] + ' ' +
           md.pre_match + ' ' + md[2] + ' ' + md.post_match)
       [
         [7, Card::face_value(md[1]), Card::face_value(md[3])],
         arranged_hand
       ]
     elsif (md = (by_face =~ /((.). \2.) (.*)((.). \5. \5.)/))
       arranged_hand = arrange_hand(md[4] + ' ' + md[1] + ' ' +
           md.pre_match + ' ' + md[3] + ' ' + md.post_match)
       [
         [7, Card::face_value(md[5]), Card::face_value(md[2])],
         arranged_hand
       ]
     else
       false
     end
   end

   def flush?
     if (md = (by_suit =~ /(.)(.) (.)\2 (.)\2 (.)\2 (.)\2/))
       [
         [
           6,
           Card::face_value(md[1]),
           *(md[3..6].map { |f| Card::face_value(f) })
         ],
         arrange_hand(md)
       ]
     else
       false
     end
   end

   def straight?
     result = false
     if hand.size > 5
       transform = delta_transform
       # note we can have more than one delta 0 that we
       # need to shuffle to the back of the hand
       until transform.match(/^\S{3}( [1-9x]\S\S)+( 0\S\S)*$/) do
         transform.gsub!(/(\s0\S\S)(.*)/, "\\2\\1")
       end
       if (md = (/.(.). 1.. 1.. 1.. 1../.match(transform)))
         high_card = Card::face_value(md[1])
         arranged_hand = fix_low_ace_display(md[0] + ' ' +
             md.pre_match + ' ' + md.post_match)
         result = [[5, high_card], arranged_hand]
       end
     end
   end

   def three_of_a_kind?
     if (md = (by_face =~ /(.). \1. \1./))
       # get kicker
       arranged_hand = arrange_hand(md)
       arranged_hand.match(/(?:\S\S ){3}(\S)\S (\S)/)
       [
         [
           4,
           Card::face_value(md[1]),
           Card::face_value($1),
           Card::face_value($2)
         ],
         arranged_hand
       ]
     else
       false
     end
   end

   def two_pair?
     if (md = (by_face =~ /(.). \1.(.*) (.). \3./))
       # get kicker
       arranged_hand = arrange_hand(md[0] + ' ' +
           md.pre_match + ' ' + md[2] + ' ' + md.post_match)
       arranged_hand.match(/(?:\S\S ){4}(\S)/)
       [
         [
           3,
           Card::face_value(md[1]),
           Card::face_value(md[3]),
           Card::face_value($1)
         ],
         arranged_hand
       ]
     else
       false
     end
   end

   def pair?
     if (md = (by_face =~ /(.). \1./))
       # get kicker
       arranged_hand = arrange_hand(md)
       arranged_hand.match(/(?:\S\S ){2}(\S)\S\s+(\S)\S\s+(\S)/)
       [
         [
           2,
           Card::face_value(md[1]),
           Card::face_value($1),
           Card::face_value($2),
           Card::face_value($3)
         ],
         arranged_hand
       ]
     else
       false
     end
   end

   def highest_card?
     result = by_face
     [[1, *result.face_values[0..4]], result.hand.join(' ')]
   end

   OPS = [
     ['Royal Flush', :royal_flush? ],
     ['Straight Flush', :straight_flush? ],
     ['Four of a kind', :four_of_a_kind? ],
     ['Full house', :full_house? ],
     ['Flush', :flush? ],
     ['Straight', :straight? ],
     ['Three of a kind', :three_of_a_kind?],
     ['Two pair', :two_pair? ],
     ['Pair', :pair? ],
     ['Highest Card', :highest_card? ],
   ]

   def hand_rating
     OPS.map { |op|
       (method(op[1]).call()) ? op[0] : false
     }.find { |v| v }
   end

   def score
     OPS.map { |op|
       method(op[1]).call()
     }.find([0]) { |score| score }
   end

   def take_card(card)
     @hand.push(card)
   end

   def arranged_hand
     score[1] + " (#{hand_rating})"
   end

   def just_cards
     @hand.join(" ")
   end

   def to_s
     just_cards + " (" + hand_rating + ")"
   end
end

class Player
   def initialize(name, deck)
     @name = name
     @hand = Hand.new
     2.times { @hand.take_card(deck.deal()) }
     @folded = false
   end

   def folded?
     @folded
   end

   def take_card(card)
     @hand.take_card(card)
   end

   def fold?(players)
     unless (folded?)
       if (players)
         folded_count = players.inject(0) { |count, p|
           (p.folded?) ? count + 1 : count
         }
         @folded = rand(players.size - folded_count) > (folded_count)
       else
         @folded = (rand(10) <= 1)
       end
     end
     folded?

   end

   def score
     (folded?) ? [[0]] : @hand.score
   end

   def arranged_hand
     @name + ' ' +
     if (folded?)
       @hand.just_cards + ' (folded)'
     else
       @hand.arranged_hand
     end
   end

   def to_s
     @name + ' ' +
     if (folded?)
       @hand.just_cards + ' (folded)'
     else
       @hand.to_s
     end
   end

   def <=>(other)
     score <=> other.score
   end
end

class TexasHoldEm
   def initialize(player_count)
     @deck = Deck.new
     @common_cards = Array.new(5) { @deck.deal }
     @players = (1..player_count).inject() { |players, num|
       players << Player.new("Player #{num}", @deck)
     }
   end

   def game_over?
     @common_cards.empty?
   end

   def play_round
     unless game_over?
       card = @common_cards.pop
       @players.each do |p|
         unless p.fold?(@players)
           p.take_card(card)
         end
       end
     end

     game_over?
   end

   def rank_players!
     @players = @players.sort.reverse
   end

   def arranged_players
     @players.inject('') { |result, player|
       result += player.arranged_hand + "\n"
     }
   end

   def to_s
     @players.join("\n")
   end
end

if __FILE__ == $0
   srand

   game = TexasHoldEm.new(5)
   round = 1
   until game.game_over?
     puts "\nRound #{round}"
     puts game
     game.play_round
     round += 1
   end
   puts "\nRound #{round}"
   puts game

   game.rank_players!
   puts "\nFinal Ranking"
   puts game.arranged_players
end

__END__

James Edward Gray II

路路路

On Mar 23, 2005, at 8:20 AM, Patrick Hurley wrote:

I liked the delta transform as well, of course it was the cause of the
bug (delta 0 when pairs were encountered) - if fixed it by shuffling
the delta zeros (excluding the first card) to the back of the hand.