Skip to content

Commit

Permalink
Initial commit: state of code at end of filming
Browse files Browse the repository at this point in the history
  • Loading branch information
camillebaldock committed Sep 2, 2014
0 parents commit c74f073
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/card.rb
@@ -0,0 +1 @@
Card = Struct.new(:pips, :suit)
104 changes: 104 additions & 0 deletions lib/hand.rb
@@ -0,0 +1,104 @@
class Hand

attr_reader :cards

def initialize(cards)
@cards = cards
end

def better_than?(other_hand)
POKER_RANKS.index(rank.fetch(:type)) > POKER_RANKS.index(other_hand.rank.fetch(:type))
end

POKER_RANKS = [
:full_house,
:four_of_a_kind,
]

def rank
if has_four
{ :type => :four_of_a_kind }
elsif full_house
{ :type => :full_house }
elsif straight
{ :type => :straight }
elsif flush
{ :type => :flush }
elsif has_three
{ :type => :three_of_a_kind }
elsif has_two
{ :type => :pair }
else
{ :type => :highest }
end
end

##bob
def pip_count
grouped_cards = cards.map(&:pips).group_by { |i| i }
grouped_cards.each do |key, value|
grouped_cards[key] = value.count
end
end

##DUPE! pip_count
def suit_count
grouped_cards = cards.map(&:suit).group_by { |i| i }
grouped_cards.each do |key, value|
grouped_cards[key] = value.count
end
end

private

def has_four
pip_count.values.include?(4)
end

def has_three
pip_count.values.include?(3)
end

def has_two
pip_count.values.include?(2)
end

def full_house
has_three && has_two
end

def flush
suit_count.values.include?(5)
end

def straight
card_values = cards.map(&:pips)
number_aces = card_values.select{|i| i == 14}.count
if number_aces >= 1
aces_as_ones = card_values.clone
aces_as_ones.delete(14)
number_aces.times do
aces_as_ones << 1
end
consecutive_cards?(card_values) ||
consecutive_cards?(aces_as_ones)
else
consecutive_cards?(card_values)
end
end

#TODO: live in a helper, utility
def consecutive_cards?(card_values)
card_values.sort!
#Magic number 4 => always 5 cards per hand
difference_always_1 = true
i = 0
while (difference_always_1 && i < 4) do
difference_between_pips = card_values[i+1] - card_values[i]
difference_always_1 = difference_between_pips == 1
i += 1
end
difference_always_1
end

end
28 changes: 28 additions & 0 deletions lib/hand_parser.rb
@@ -0,0 +1,28 @@
require 'hand'
require 'card'

class HandParser
#TODO: fail nicely when not 5 cards
def parse(array_of_string_cards)
cards = array_of_string_cards.map do |card_string|
make_card(card_string)
end
Hand.new(cards)
end

private

SUITS = { "h" => :heart, "d" => :diamonds,
"s" => :spades, "c" => :clubs}
PIPS_FOR_HEADS = { "j" => 11, "q" => 12, "k" => 13, "a" => 14}

#TODO: fail loudly and with better errors with unexpected inputs
#assumes correct capitalisation, correct letters...
def make_card(card_string)
end_of_card_number_index = (card_string.length)-2
pips_string = card_string[0..end_of_card_number_index]
number_of_pips = PIPS_FOR_HEADS.fetch(pips_string, pips_string.to_i)
Card.new(number_of_pips, SUITS.fetch(card_string[end_of_card_number_index+1]))
end

end
33 changes: 33 additions & 0 deletions spec/hand_parser_spec.rb
@@ -0,0 +1,33 @@
require "spec_helper"
require "hand_parser"

describe HandParser do
describe "#parse"
it "returns a hand of cards" do
array_of_cards = ["5h", "6d", "ks", "qc", "1h"]
hand = described_class.new.parse(array_of_cards)
expect(hand.cards.count).to eq 5
end

it "returns the right cards" do
array_of_cards = ["6d"]
hand = described_class.new.parse(array_of_cards)
expect(hand.cards.first.suit).to eq :diamonds
expect(hand.cards.first.pips).to eq 6
end

it "returns the right cards" do
array_of_cards = ["10d"]
hand = described_class.new.parse(array_of_cards)
expect(hand.cards.first.suit).to eq :diamonds
expect(hand.cards.first.pips).to eq 10
end

it "returns the right cards" do
array_of_cards = ["qd"]
hand = described_class.new.parse(array_of_cards)
expect(hand.cards.first.suit).to eq :diamonds
expect(hand.cards.first.pips).to eq 12
end

end
91 changes: 91 additions & 0 deletions spec/hand_spec.rb
@@ -0,0 +1,91 @@
require 'spec_helper'
require 'hand'
require 'hand_parser'

describe Hand do

#TODO: this is not a nice interface
let(:pair_hand) {
HandParser.new.parse(["5h", "5d", "6d", "7d", "8d"])
}
let(:highest_hand) {
HandParser.new.parse(["4h", "5d", "6d", "7d", "9d"])
}
let(:three_of_a_kind_hand) {
HandParser.new.parse(["5h", "5d", "5s", "7d", "8d"])
}
let(:four_of_a_kind_hand) {
HandParser.new.parse(["5h", "5d", "5s", "5c", "8d"])
}
let(:full_house_hand) {
HandParser.new.parse(["5h", "5d", "5s", "6c", "6h"])
}
let(:flush_hand) {
HandParser.new.parse(["5h", "6h", "7h", "8h", "10h"])
}
let(:straight_hand) {
HandParser.new.parse(["5h", "6h", "7h", "8h", "9d"])
}
let(:straight_hand_low_ace) {
HandParser.new.parse(["2h", "3h", "4h", "5h", "ad"])
}
let(:straight_hand_high_ace) {
HandParser.new.parse(["10h", "jh", "qh", "kh", "ad"])
}

describe "#better_than" do
it "tells me if the hand is better" do
expect(four_of_a_kind_hand.better_than?(full_house_hand)).to be true
end
end

describe "#pip_count" do
it "returns information about the winning potential of the hand" do
expect(pair_hand.pip_count).to eq ({ 5 => 2, 6 => 1, 7 => 1, 8 => 1 })
end
end

describe "#suit_count" do
it "returns information about the winning potential of the hand" do
expect(pair_hand.suit_count).to eq ({ :heart => 1, :diamonds => 4 })
end
end

describe "#rank" do
it "returns information about the winning potential of the hand" do
expect(pair_hand.rank).to eq ({:type => :pair})
end

it "returns information about the winning potential of the hand" do
expect(highest_hand.rank).to eq ({:type => :highest})
end

it "returns information about the winning potential of the hand" do
expect(three_of_a_kind_hand.rank).to eq ({:type => :three_of_a_kind})
end

it "returns information about the winning potential of the hand" do
expect(four_of_a_kind_hand.rank).to eq ({:type => :four_of_a_kind})
end

it "returns information about the winning potential of the hand" do
expect(full_house_hand.rank).to eq ({:type => :full_house})
end

it "returns information about the winning potential of the hand" do
expect(flush_hand.rank).to eq ({:type => :flush})
end

it "returns information about the winning potential of the hand" do
expect(straight_hand.rank).to eq ({:type => :straight})
end

it "works with a straight with low ace" do
expect(straight_hand_low_ace.rank).to eq ({:type => :straight})
end

it "works with a straight with high ace" do
expect(straight_hand_high_ace.rank).to eq ({:type => :straight})
end
end
end
78 changes: 78 additions & 0 deletions spec/spec_helper.rb
@@ -0,0 +1,78 @@
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause this
# file to always be loaded, without a need to explicitly require it in any files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, make a
# separate helper file that requires this one and then use it only in the specs
# that actually need it.
#
# The `.rspec` file also contains a few flags that are not defaults but that
# users commonly want.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
=begin
# These two settings work together to allow you to limit a spec run
# to individual examples or groups you care about by tagging them with
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
# get run.
config.filter_run :focus
config.run_all_when_everything_filtered = true
# Many RSpec users commonly either run the entire suite or an individual
# file, and it's useful to allow more verbose output when running an
# individual spec file.
if config.files_to_run.one?
# Use the documentation formatter for detailed output,
# unless a formatter has already been configured
# (e.g. via a command-line flag).
config.default_formatter = 'doc'
end
# Print the 10 slowest examples and example groups at the
# end of the spec run, to help surface which specs are running
# particularly slow.
config.profile_examples = 10
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = :random
# Seed global randomization in this process using the `--seed` CLI option.
# Setting this allows you to use `--seed` to deterministically reproduce
# test failures related to randomization by passing the same `--seed` value
# as the one that triggered the failure.
Kernel.srand config.seed
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
# Enable only the newer, non-monkey-patching expect syntax.
# For more details, see:
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
expectations.syntax = :expect
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Enable only the newer, non-monkey-patching expect syntax.
# For more details, see:
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
mocks.syntax = :expect
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended.
mocks.verify_partial_doubles = true
end
=end
end

0 comments on commit c74f073

Please sign in to comment.