Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: dblack/ruby-cards
base: 39bb2a8625
...
head fork: dblack/ruby-cards
compare: 4de3aaa38d
Checking mergeability… Don't worry, you can still create the pull request.
  • 2 commits
  • 8 files changed
  • 0 commit comments
  • 1 contributor
View
2  README
@@ -0,0 +1,2 @@
+Some basic playing card functionality in Ruby, along with logic for evaluating
+and comparing poker hands.
View
20 beat.rb
@@ -0,0 +1,20 @@
+require './playing_cards.rb'
+ def hand_from(*specs)
+ PlayingCards::Hand.new(specs.each_slice(2).map {|r,s| PlayingCards::Card.new(r,s)})
+ end
+ hand = hand_from("2", "spades", "3", "clubs", "4", "diamonds",
+ "5", "hearts", "Q", "clubs")
+
+ beats = loses = 0
+ deck = PlayingCards::Deck.new
+ deck.combination(5).each.with_index do |five, index|
+ puts index if index % 1000 == 0
+ if PlayingCards::Hand.new(five).beats?(hand)
+ beats += 1
+ else
+ loses += 1
+ end
+ end
+ p "Beaten by #{beats}; beat #{loses}"
+
+ # "Beaten by 2134860; beat 464100"
View
33 lib/card.rb
@@ -0,0 +1,33 @@
+module PlayingCards
+ class Card
+ class InvalidCardError < StandardError
+ end
+
+ include Comparable
+
+ SUITS = %w{ hearts diamonds spades clubs }
+ RANKS = %w{ 2 3 4 5 6 7 8 9 10 J Q K A }
+
+ attr_reader :rank, :suit
+
+ def initialize(rank, suit)
+ @rank = rank.to_s.upcase
+ @suit = suit.downcase
+ unless valid?
+ raise InvalidCardError
+ end
+ end
+
+ def to_s
+ "#{rank} of #{suit}"
+ end
+
+ def <=>(other_card)
+ RANKS.index(self.rank) <=> RANKS.index(other_card.rank)
+ end
+
+ def valid?
+ SUITS.include?(suit) && RANKS.include?(rank)
+ end
+ end
+end
View
32 lib/deck.rb
@@ -0,0 +1,32 @@
+module PlayingCards
+ class Deck
+ def initialize
+ @cards = []
+ Card::SUITS.each do |suit|
+ Card::RANKS.each do |rank|
+ card = Card.new(rank, suit)
+ @cards.push(card)
+ end
+ end
+ @cards.shuffle!
+ end
+
+ def combination(n)
+ @cards.combination(n)
+ end
+
+ def size
+ @cards.size
+ end
+
+ def deal(n=1)
+ hand = []
+ n.times do |i|
+ card = @cards.pop
+ yield card if block_given?
+ hand << card
+ end
+ hand
+ end
+ end
+end
View
156 lib/hand.rb
@@ -0,0 +1,156 @@
+module PlayingCards
+ class Hand
+
+ include Comparable
+
+ HANDS_IN_ORDER = [
+ "royal straight flush",
+ "straight flush",
+ "four of a kind",
+ "full house",
+ "flush",
+ "straight",
+ "three of a kind",
+ "two pair",
+ "pair",
+ "high card"
+ ]
+
+ high_card_proc = proc do |hand1, hand2|
+ hand1.high_card <=> hand2.high_card
+ end
+
+ TIE_BREAKERS = {
+ "high card" => high_card_proc,
+ "pair" => proc do |hand1, hand2|
+ comp = hand1.multiple_cards(2).first <=> hand2.multiple_cards(2).first
+ comp.zero? ? hand1.compare_kickers(hand2) : comp
+ end,
+ "three of a kind" => proc do |hand1, hand2|
+ hand1.multiple_cards(3).first <=> hand2.multiple_cards(3).first
+ end,
+ "two pair" => proc do |hand1, hand2|
+ pairs1 = hand1.multiple_cards(2).sort
+ pairs2 = hand2.multiple_cards(2).sort
+ comp = pairs1.last <=> pairs2.last
+ comp.zero? ? hand1.compare_kickers(hand2) : comp
+ end,
+ "four of a kind" => proc do |hand1, hand2|
+ hand1.multiple_cards(4).first <=> hand2.multiple_cards(4).first
+ end,
+ "straight" => high_card_proc,
+ "flush" => high_card_proc,
+ "straight flush" => high_card_proc,
+ "full house" => proc do |hand1, hand2|
+ hand1.multiple_cards(3).first <=> hand2.multiple_cards(3).first
+ end
+ }
+
+ def kickers
+ multiple_cards(1).sort
+ end
+
+ def compare_kickers(other)
+ kickers.each.with_index do |card, i|
+ comp = card <=> other.kickers[i]
+ return comp unless comp.zero?
+ end
+ return 0
+ end
+
+ def initialize(cards)
+ @cards = cards
+ end
+
+ attr_reader :cards
+ protected :cards
+
+ def multiple_cards(n)
+ cards.select do |card|
+ cards.count {|c| c.rank == card.rank } == n
+ end
+ end
+
+ def hand_name
+ HANDS_IN_ORDER.each do |hand|
+ send("is_#{hand.tr(' ', '_')}?") and return hand
+ end
+ end
+
+ def ties?(hand)
+ self == hand and TIE_BREAKERS[self.hand_name][self, hand] == 0
+ end
+
+ def beats?(hand)
+ return false if self.ties?(hand)
+ if self == hand
+ case TIE_BREAKERS[self.hand_name][self, hand]
+ when -1 then false
+ when 1 then true
+ else raise "Can't determine winner"
+ end
+ else
+ self > hand
+ end
+ end
+
+ def <=>(hand)
+ HANDS_IN_ORDER.index(hand.hand_name) <=> HANDS_IN_ORDER.index(self.hand_name)
+ end
+
+ def histogram
+ Hash.new(0).tap do |hist|
+ @cards.each do |card|
+ hist[card.rank] += 1
+ end
+ end
+ end
+
+ def is_royal_straight_flush?
+ is_straight_flush? && @cards.sort.first.rank == "10"
+ end
+
+ def is_high_card?
+ !is_straight? && histogram.values.sort == [1,1,1,1,1]
+ end
+
+ def is_pair?
+ histogram.values.sort == [1,1,1,2]
+ end
+
+ def is_two_pair?
+ histogram.values.sort == [1,2,2]
+ end
+
+ def is_three_of_a_kind?
+ histogram.values.sort == [1,1,3]
+ end
+
+ def is_full_house?
+ histogram.values.sort == [2,3]
+ end
+
+ def is_four_of_a_kind?
+ histogram.values.sort == [1,4]
+ end
+
+ def is_straight?
+ ranks = histogram.keys.sort_by {|c| Card::RANKS.index(c) }
+ !is_pair? && Card::RANKS.index(ranks.last) - Card::RANKS.index(ranks.first) == 4
+ end
+
+ def is_flush?
+ suit = @cards.first.suit
+ @cards.all? {|card| card.suit == suit }
+ end
+
+ def is_straight_flush?
+ is_straight? && is_flush?
+ end
+
+ def high_card
+ @cards.sort.last
+ end
+ end
+end
+
View
3  playing_cards.rb
@@ -0,0 +1,3 @@
+require_relative "lib/card.rb"
+require_relative "lib/deck.rb"
+require_relative "lib/hand.rb"
View
31 playing_cards_spec.rb
@@ -0,0 +1,31 @@
+require 'minitest/autorun'
+require_relative 'playing_card'
+
+describe PlayingCard do
+ describe "when initialized" do
+ it "must set its suit and rank" do
+ two = PlayingCard.new("2", "spades")
+ two.rank.must_equal("2")
+ two.suit.must_equal("spades")
+ end
+
+ it "should convert integer to string rank" do
+ two = PlayingCard.new(2, "spades")
+ two.rank.must_equal("2")
+ end
+ end
+
+ describe "when compared with another card" do
+ it "should evaluate king higher than ten" do
+ king = PlayingCard.new("K", "clubs")
+ ten = PlayingCard.new(10, "diamonds")
+ assert(king > ten)
+ end
+
+ it "should equate two kings" do
+ king1 = PlayingCard.new("K", "clubs")
+ king2 = PlayingCard.new("K", "spades")
+ king1.must_equal(king2)
+ end
+ end
+end
View
280 playing_cards_test.rb
@@ -0,0 +1,280 @@
+require 'minitest/autorun'
+require_relative 'playing_cards'
+
+class CardTest < MiniTest::Unit::TestCase
+ def test_initialization
+ card = PlayingCards::Card.new("2", "spades")
+ assert_equal("2", card.rank)
+ assert_equal("spades", card.suit)
+ end
+
+ def test_initializing_invalid_card_raises_exception
+ assert_raises(PlayingCards::Card::InvalidCardError) do
+ PlayingCards::Card.new("blah", "things")
+ end
+ end
+
+ def test_int_gets_saved_as_string
+ card = PlayingCards::Card.new(2, "spades")
+ assert_equal("2", card.rank)
+ end
+
+ def test_card_comparison
+ king = PlayingCards::Card.new("K", "clubs")
+ ten = PlayingCards::Card.new(10, "diamonds")
+ assert(king > ten)
+ end
+
+ def test_card_equality
+ king1 = PlayingCards::Card.new("K", "clubs")
+ king2 = PlayingCards::Card.new("K", "spades")
+ assert(king1 == king2)
+ end
+
+ def test_invalid_rank
+ assert_raises(PlayingCards::Card::InvalidCardError) {
+ PlayingCards::Card.new("blah", "diamonds")
+ }
+ end
+
+ def test_invalid_suit
+ assert_raises(PlayingCards::Card::InvalidCardError) {
+ PlayingCards::Card.new("J", "things")
+ }
+ end
+end
+
+class HandTest < MiniTest::Unit::TestCase
+ def test_pair
+ hand = hand_from("2", "spades", "2", "clubs", "3", "diamonds",
+ "5", "hearts", "J", "clubs")
+ assert(hand.is_pair?)
+ assert_equal("pair", hand.hand_name)
+ end
+
+ def test_two_pair
+ hand = hand_from("2", "spades", "2", "clubs", "3", "diamonds",
+ "3", "hearts", "J", "clubs")
+ assert(hand.is_two_pair?)
+ assert_equal("two pair", hand.hand_name)
+ end
+
+ def test_high_card
+ hand = hand_from("2", "spades", "K", "clubs", "8", "diamonds",
+ "3", "hearts", "J", "clubs")
+ assert(hand.is_high_card?)
+ assert_equal("high card", hand.hand_name)
+ end
+
+ def test_three_of_a_kind
+ hand = hand_from("2", "spades", "2", "clubs", "3", "diamonds",
+ "5", "hearts", "2", "hearts")
+ assert(hand.is_three_of_a_kind?)
+ assert_equal("three of a kind", hand.hand_name)
+ end
+
+ def test_full_house
+ hand = hand_from("2", "spades", "2", "clubs", "3", "diamonds",
+ "3", "hearts", "2", "hearts")
+ assert(hand.is_full_house?)
+ assert_equal("full house", hand.hand_name)
+ end
+
+ def test_four_of_a_kind
+ hand = hand_from("3", "spades", "3", "clubs", "3", "diamonds",
+ "3", "hearts", "2", "hearts")
+ assert(hand.is_four_of_a_kind?)
+ assert_equal("four of a kind", hand.hand_name)
+ end
+
+ def test_flush
+ hand = hand_from("3", "spades", "4", "spades", "J", "spades",
+ "6", "spades", "K", "spades")
+ assert(hand.is_flush?)
+ assert_equal("flush", hand.hand_name)
+ end
+
+ def test_straight
+ hand = hand_from("2", "spades", "3", "clubs", "4", "diamonds",
+ "5", "hearts", "6", "clubs")
+ assert(hand.is_straight?)
+ assert_equal("straight", hand.hand_name)
+ end
+
+ def test_straight_flush
+ hand = hand_from("2", "spades", "3", "spades", "4", "spades",
+ "5", "spades", "6", "spades")
+ assert(hand.is_straight_flush?)
+ assert_equal("straight flush", hand.hand_name)
+ end
+
+ def test_royal_straight_flush
+ hand = hand_from("10", "clubs", "J", "clubs", "Q", "clubs",
+ "K", "clubs", "A", "clubs")
+ assert(hand.is_royal_straight_flush?)
+ assert_equal("royal straight flush", hand.hand_name)
+ end
+
+ def test_sort
+ hand1 = hand_from("2", "spades", "3", "clubs", "4", "diamonds",
+ "5", "hearts", "6", "clubs")
+ hand2 = hand_from("2", "spades", "3", "clubs", "4", "diamonds",
+ "5", "hearts", "2", "clubs")
+ hand3 = hand_from("2", "spades", "2", "clubs", "3", "diamonds",
+ "3", "hearts", "2", "hearts")
+ assert_equal([hand2, hand1, hand3], [hand1, hand2, hand3].sort)
+ end
+
+ def test_get_high_card
+ hand = hand_from("2", "spades", "K", "clubs", "8", "diamonds",
+ "3", "hearts", "J", "clubs")
+ assert_equal(PlayingCards::Card.new("K", "clubs"), hand.high_card)
+ end
+
+ def test_beats_high_card
+ hand1 = hand_from("2", "spades", "K", "clubs", "8", "diamonds",
+ "3", "hearts", "J", "clubs")
+ hand2 = hand_from("2", "spades", "10", "clubs", "8", "diamonds",
+ "3", "hearts", "J", "clubs")
+ assert(hand1.beats?(hand2))
+ end
+
+ def test_beats_pair
+ hand1 = hand_from("2", "spades", "2", "clubs", "8", "diamonds",
+ "3", "hearts", "J", "clubs")
+ hand2 = hand_from("10", "spades", "10", "clubs", "8", "diamonds",
+ "3", "hearts", "J", "clubs")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_beats_pair_with_kicker
+ hand1 = hand_from("2", "spades", "2", "clubs", "8", "diamonds",
+ "3", "hearts", "J", "clubs")
+ hand2 = hand_from("2", "spades", "2", "clubs", "9", "diamonds",
+ "3", "hearts", "J", "clubs")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_beats_two_pair
+ hand1 = hand_from("2", "spades", "2", "clubs", "8", "diamonds",
+ "8", "hearts", "J", "clubs")
+ hand2 = hand_from("2", "diamonds", "2", "hearts", "9", "diamonds",
+ "9", "hearts", "J", "clubs")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_beats_two_pair_with_kicker
+ hand1 = hand_from("2", "spades", "2", "clubs", "8", "diamonds",
+ "8", "hearts", "3", "clubs")
+ hand2 = hand_from("2", "diamonds", "2", "hearts", "8", "diamonds",
+ "8", "hearts", "5", "clubs")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_beats_three_of_a_kind
+ hand1 = hand_from("2", "spades", "2", "clubs", "2", "diamonds",
+ "8", "hearts", "J", "clubs")
+ hand2 = hand_from("5", "diamonds", "5", "hearts", "5", "diamonds",
+ "9", "hearts", "J", "clubs")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_beats_straight
+ hand1 = hand_from("2", "spades", "3", "clubs", "4", "diamonds",
+ "5", "hearts", "6", "clubs")
+ hand2 = hand_from("5", "diamonds", "6", "hearts", "7", "clubs",
+ "8", "spades", "9", "clubs")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_beats_flush
+ hand1 = hand_from("3", "spades", "4", "spades", "J", "spades",
+ "6", "spades", "K", "spades")
+ hand2 = hand_from("3", "spades", "4", "spades", "J", "spades",
+ "6", "spades", "A", "spades")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_beats_full_house
+ hand1 = hand_from("2", "spades", "2", "clubs", "3", "diamonds",
+ "3", "hearts", "2", "hearts")
+ hand2 = hand_from("4", "spades", "4", "clubs", "3", "diamonds",
+ "3", "hearts", "4", "hearts")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_beats_four_of_a_kind
+ hand1 = hand_from("2", "spades", "2", "clubs", "2", "diamonds",
+ "2", "hearts", "J", "clubs")
+ hand2 = hand_from("5", "diamonds", "5", "hearts", "5", "clubs",
+ "5", "spades", "J", "clubs")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_beats_straight_flush
+ hand1 = hand_from("3", "spades", "4", "spades", "5", "spades",
+ "6", "spades", "7", "spades")
+ hand2 = hand_from("5", "spades", "4", "spades", "7", "spades",
+ "6", "spades", "8", "spades")
+ assert(hand2.beats?(hand1))
+ end
+
+ def test_ties_high_card
+ hand = hand_from("2", "spades", "K", "clubs", "8", "diamonds",
+ "3", "hearts", "J", "clubs")
+ assert(hand.ties?(hand))
+ end
+
+ def test_ties_pair
+ hand = hand_from("2", "spades", "2", "clubs", "8", "diamonds",
+ "3", "hearts", "J", "clubs")
+ assert(hand.ties?(hand))
+ end
+
+ def test_ties_two_pair
+ hand = hand_from("2", "spades", "2", "clubs", "8", "diamonds",
+ "8", "hearts", "J", "clubs")
+ assert(hand.ties?(hand))
+ end
+
+ def test_ties_three_of_a_kind
+ hand = hand_from("5", "diamonds", "5", "hearts", "5", "diamonds",
+ "9", "hearts", "J", "clubs")
+ assert(hand.ties?(hand))
+ end
+
+ def test_ties_straight
+ hand = hand_from("5", "diamonds", "6", "hearts", "7", "clubs",
+ "8", "spades", "9", "clubs")
+ assert(hand.ties?(hand))
+ end
+
+ def test_ties_flush
+ hand = hand_from("3", "spades", "4", "spades", "J", "spades",
+ "6", "spades", "K", "spades")
+ assert(hand.ties?(hand))
+ end
+
+ def test_ties_full_house
+ hand1 = hand_from("2", "spades", "2", "clubs", "3", "diamonds",
+ "3", "hearts", "2", "hearts")
+ assert(hand1.ties?(hand1))
+ end
+
+ def test_beats_four_of_a_kind
+ hand = hand_from("2", "spades", "2", "clubs", "2", "diamonds",
+ "2", "hearts", "J", "clubs")
+ assert(hand.ties?(hand))
+ end
+
+ def test_ties_straight_flush
+ hand1 = hand_from("3", "spades", "4", "spades", "5", "spades",
+ "6", "spades", "7", "spades")
+ assert(hand1.ties?(hand1))
+ end
+
+ def hand_from(*specs)
+ PlayingCards::Hand.new(specs.each_slice(2).map {|r,s| PlayingCards::Card.new(r,s)})
+ end
+
+end

No commit comments for this range

Something went wrong with that request. Please try again.