A Poker program written in Ruby.
Rank (1..14, 14 is Ace)
Suit (spade, heart, diamond, club)
hand (cards)
NoHandError = Class.new(StandardError)
# Return the best hand: poker([hand, ...]) => "hand"
def poker hands
raise NoHandError if hands.empty?
return hands.max_by { |hand| hand_rank hand }
end
# return a value indicating the ranking of a hand
# 9 diffrent kinds of hand
# straight flush: 8
# 4 of a kind: 7
# full house: 6
# flush: 5
# straight: 4
# 3 of a kind: 3
# 2 pair: 2
# kind: 1
# high card: 0
def hand_rank(hand)
ranks = card_ranks(hand) # card_ranks return the ranks in sorted order
if straight(ranks) && flush(hand)
[8, ranks.max] # 2 3 4 5 6 => [8, 6], 6 7 8 9 T => [8, T]
elsif kind(4, ranks)
[7, kind(4, ranks), kind(1, ranks)] # 9 9 9 9 3 => [7, 9, 3]
elsif kind(3, ranks) && kind(2, ranks) # 8 8 8 K K => [6, 8, 13]
[6, kind(3, ranks), kind(2, ranks)]
elsif flush(hand)
[5, ranks]
elsif straight(ranks)
[4, ranks.max]
elsif kind(3, ranks)
[3, kind(3, ranks), ranks]
elsif two_pair(ranks)
[2, kind(2, ranks), ranks]
elsif kind(2, ranks)
[1, kind(2, ranks), ranks]
else
[0, ranks]
end
end
# Return a array of ranks, sorted with higher first.
# Example usage:
# ["6C", "7C", "8C", "9C", "TC"] => [10, 9, 8, 7, 6]
def card_ranks(cards)
ranks = cards.map { |card| '--23456789TJQKA'.index(card[0]) }
ranks.sort { |a, b| b <=> a }
end
# Return True if the ordered ranks form a 5-card straight.
def straight(ranks)
(ranks.max - ranks.min) == 4 && ranks.uniq.size == 5
end
# Return True if all the cards have the same suit.
def flush(hand)
suits = hand.map { |card| card[1] }
suits.uniq.one?
end
# Return the first rank that this hand has exactly n of.
# Return None if there is no n-of-a-kind in the hand.
def kind(n, ranks)
ranks.each { |r| return r if ranks.count(r) == n }
return nil
end
# If there are two pair, return the two ranks as a
# array: [highest, lowest]; otherwise return nil.
def two_pair(ranks)
pair = kind(2, ranks)
lowpair = kind(2, ranks.reverse)
if pair && lowpair != pair
[pair, lowpair]
end
end
Tests
require "minitest/autorun"
require_relative 'poker'
class TestPoker < Minitest::Test
def setup
@sf = "6C 7C 8C 9C TC".split # straight flush
@fk = "9D 9H 9S 9C 7D".split # four of a kind
@fh = "TD TC TG 7C 7D".split # full house
@tp = "5S 5D 9H 9C 6S".split # two_pairs
@fk_ranks = card_ranks(@fk)
@tp_ranks = card_ranks(@tp)
end
def test_kind1
assert kind(4, @fk_ranks) == 9
end
def test_kind2
assert kind(3, @fk_ranks) == nil
end
def test_kind3
assert kind(2, @fk_ranks) == nil
end
def test_kind1
assert kind(1, @fk_ranks) == 7
end
def test_two_pair1
assert two_pair(@fk_ranks) == nil
end
def test_two_pair2
assert two_pair(@tp_ranks) == [9, 5]
end
def test_straight1
assert straight([9, 8, 7, 6, 5]) == true
end
def test_straight2
assert straight([9, 8, 8, 6, 5]) == false
end
def test_flush1
assert flush(@sf) == true
end
def test_flush2
assert flush(@fk) == false
end
def test_card_ranks1
assert card_ranks(@sf) == [10, 9, 8, 7, 6]
end
def test_card_ranks2
assert card_ranks(@fk) == [9, 9, 9, 9, 7]
end
def test_card_ranks3
assert card_ranks(@fh) == [10, 10, 10, 7, 7]
end
# Test cases for the functions in poker program
def test_hands
assert poker([@sf, @fk, @fh]) == @sf
end
def test_hands2
assert poker([@fk, @fh]) == @fk
end
def test_identical_hands
assert poker([@fh, @fh]) == @fh
end
def test_one_player
assert poker([@sf]) == @sf
end
def test_hundred_players
assert poker(Array.new(1, @sf) + Array.new(99, @fh)) == @sf
end
def test_no_players
assert_raises(NoHandError) { poker([]) }
end
def test_hand_rank1
assert hand_rank(@sf) == [8, 10]
end
def test_hand_rank2
assert hand_rank(@fk) == [7, 9, 7]
end
def test_hand_rank3
assert hand_rank(@fh) == [6, 10, 7]
end
end