Created
July 24, 2011 03:28
-
-
Save msimpson/1102184 to your computer and use it in GitHub Desktop.
A library to rank and compare poker hands.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# encoding: UTF-8 | |
# - Poker - | |
# A library to rank and compare poker hands. | |
module Poker | |
ACE = 14 | |
KING = 13 | |
QUEEN = 12 | |
JACK = 11 | |
SPADE = 1 | |
HEART = 2 | |
DIAMOND = 3 | |
CLUB = 4 | |
STRFLUSH = 9 | |
FOUR = 8 | |
FULL = 7 | |
FLUSH = 6 | |
STR = 5 | |
THREE = 4 | |
TWO = 3 | |
PAIR = 2 | |
HIGH = 1 | |
class Card | |
attr_accessor :value, :suit | |
def initialize(value = nil, suit = nil) | |
@value = value if (0..ACE).include?value | |
@suit = suit if (SPADE..CLUB).include?suit | |
end | |
def > (card); @value > card.value; end | |
def >= (card); @value >= card.value; end | |
def < (card); @value < card.value; end | |
def <= (card); @value < card.value; end | |
def == (card); @value == card.value && | |
@suit == card.suit; end | |
end | |
class Hand < Array | |
def > (hand); value > hand.value; end | |
def >= (hand); value >= hand.value; end | |
def < (hand); value < hand.value; end | |
def <= (hand); value < hand.value; end | |
def == (hand); value == hand.value; end | |
def sort_by_value | |
sort do |card_a, card_b| | |
card_b.value <=> card_a.value | |
end | |
end | |
def cards_by_value(value) | |
Hand[*select{ |card| card.value == value }] | |
end | |
def cards_by_suit(suit) | |
Hand[*select{ |card| card.suit == suit }] | |
end | |
def ranked; rank; @ranked; end | |
def value; rank; @value; end | |
def type; rank; @type; end | |
def valid? | |
not self.empty? | |
end | |
def to_s | |
faces = ["T", "J", "Q", "K", "A"] | |
suits = ["\u2660", "\u2665", "\u2666", "\u2663"] | |
hand = "" | |
self.each do |card| | |
value = card.value > 9 ? faces[card.value - 10] : card.value.to_s | |
suit = suits[card.suit - 1] | |
hand += "#{value}#{suit} " | |
end | |
hand.strip | |
end | |
private | |
def rank | |
pack = Proc.new do |hand, type, value| | |
@ranked, @type, @value = hand, type, value; self | |
end | |
# 221 - 230 | |
if (hand = find_str_flush).valid? | |
return pack.call hand, STRFLUSH, hand.first.value + 216 + kick(hand) | |
end | |
# 208 - 220 | |
if (hand = find_four).valid? | |
return pack.call hand, FOUR, hand.first.value + 206 + kick(hand) | |
end | |
# 130 - 207 | |
if (hand = find_full).valid? | |
three = hand.first.value | |
pair = hand[3].value | |
return pack.call( | |
hand, FULL, | |
((three - 3) ** 2 + (three - 3)) / 2 + (pair - 2) + 130 + kick(hand) | |
) | |
end | |
# 129 | |
if (hand = find_flush).valid? | |
return pack.call hand, FLUSH, 129 + kick(hand) | |
end | |
# 119 - 128 | |
if (hand = find_str).valid? | |
return pack.call hand, STR, hand.first.value + 114 + kick(hand) | |
end | |
# 106 - 118 | |
if (hand = find_three).valid? | |
return pack.call hand, THREE, hand.first.value + 104 + kick(hand) | |
end | |
# 028 - 105 | |
if (hand = find_two).valid? | |
pair_1 = hand.first.value | |
pair_2 = hand[2].value | |
return pack.call( | |
hand, TWO, | |
((pair_1 - 3) ** 2 + (pair_1 - 3)) / 2 + (pair_2 - 2) + 28 + kick(hand) | |
) | |
end | |
# 015 - 027 | |
if (hand = find_pair).valid? | |
return pack.call hand, PAIR, hand.first.value + 13 + kick(hand) | |
end | |
# 002 - 014 | |
card = sort_by_value.first | |
return pack.call hand, HIGH, card.value + kick(Hand[card]) | |
end | |
def unique_cards(cards = self) | |
Hand[*cards].sort_by_value.uniq { |card| card.value } | |
end | |
def values_by_count(limit) | |
counts = Array.new(13, 0) | |
values = [] | |
each { |card| counts[card.value - 2] += 1 } | |
counts.each_with_index do |count, value| | |
values << value + 2 if count == limit | |
end | |
values.reverse | |
end | |
def cards_by_count(limit) | |
values = values_by_count limit | |
Hand[*sort_by_value.select{ |card| values.include? card.value }] | |
end | |
def find_pair | |
cards_by_value values_by_count(2).first | |
end | |
def find_two | |
cards = cards_by_count(2) | |
cards.count > 3 ? cards[0..3] : Hand[] | |
end | |
def find_three | |
cards_by_value values_by_count(3).first | |
end | |
def find_four | |
cards_by_value values_by_count(4).first | |
end | |
def find_full | |
pair = find_pair | |
three = cards_by_count 3 | |
if three.count > 3 | |
three[0..4] | |
elsif not (pair.empty? || three.empty?) | |
Hand[*three + pair] | |
else | |
Hand[] | |
end | |
end | |
def find_str(cards = self, limit = 5) | |
cards = unique_cards Hand[*cards] | |
return Hand[] if cards.count < limit | |
(cards.count - (limit - 1)).times do |i| | |
if cards[i].value - cards[i + (limit - 1)].value == limit - 1 | |
return cards[i..(i + limit - 1)] | |
end | |
end | |
if cards.first.value == ACE && | |
cards[1 - limit].value == limit && | |
cards[1 - limit].value - cards[-1].value == limit - 2 | |
return Hand[*cards[(1 - limit)..-1] + [cards.first]] | |
end | |
return Hand[] | |
end | |
def flush?(limit = 5) | |
counts = Array.new(4, 0) | |
each do |card| | |
suit = card.suit - 1 | |
counts[suit] += 1 | |
return card.suit if counts[suit] == limit | |
end | |
false | |
end | |
def find_raw_flush(limit = 5) | |
suit = flush? | |
return suit ? cards_by_suit(suit) : Hand[] | |
end | |
def find_flush(limit = 5) | |
flush = find_raw_flush | |
flush.sort_by_value[0..(limit - 1)] | |
end | |
def find_str_flush(limit = 5) | |
find_str(find_raw_flush) | |
end | |
def kick(cards = self, hand) | |
diff = Hand[] | |
cards.each do |card_1| | |
same = false | |
hand.each do |card_2| | |
same = true if card_1 == card_2 | |
end | |
diff << card_1 if not same | |
end | |
value = '0.' | |
diff.sort_by_value.each do |card| | |
value += card.value > 9 ? card.value.to_s : "0#{card.value}" | |
end | |
return value.empty? ? 0.0 : value.to_f | |
end | |
end | |
end | |
# Test | |
if __FILE__ == $0 | |
hand_1 = Poker::Hand[ | |
Poker::Card.new(Poker::JACK, Poker::CLUB), | |
Poker::Card.new(Poker::JACK, Poker::DIAMOND), | |
Poker::Card.new(Poker::ACE, Poker::SPADE), | |
Poker::Card.new(Poker::ACE, Poker::CLUB), | |
Poker::Card.new(Poker::ACE, Poker::DIAMOND), | |
Poker::Card.new(5, Poker::CLUB), | |
Poker::Card.new(4, Poker::DIAMOND) | |
] | |
hand_2 = Poker::Hand[ | |
Poker::Card.new(5, Poker::CLUB), | |
Poker::Card.new(4, Poker::DIAMOND), | |
Poker::Card.new(3, Poker::SPADE), | |
Poker::Card.new(2, Poker::CLUB), | |
Poker::Card.new(Poker::ACE, Poker::CLUB), | |
Poker::Card.new(Poker::JACK, Poker::CLUB), | |
Poker::Card.new(Poker::JACK, Poker::DIAMOND) | |
] | |
puts "[#{hand_1.ranked.to_s}] > [#{hand_2.ranked.to_s}] = #{hand_1 > hand_2}" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment