the_gambler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rspec"
10
+ gem "yard"
11
+ gem "rdoc"
12
+ gem "bundler"
13
+ gem "jeweler"
14
+ gem "simplecov"
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,39 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ git (1.2.5)
6
+ jeweler (1.8.4)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rdoc
11
+ json (1.7.5)
12
+ multi_json (1.3.7)
13
+ rake (0.9.2.2)
14
+ rdoc (3.12)
15
+ json (~> 1.4)
16
+ rspec (2.11.0)
17
+ rspec-core (~> 2.11.0)
18
+ rspec-expectations (~> 2.11.0)
19
+ rspec-mocks (~> 2.11.0)
20
+ rspec-core (2.11.1)
21
+ rspec-expectations (2.11.3)
22
+ diff-lcs (~> 1.1.3)
23
+ rspec-mocks (2.11.3)
24
+ simplecov (0.7.1)
25
+ multi_json (~> 1.0)
26
+ simplecov-html (~> 0.7.1)
27
+ simplecov-html (0.7.1)
28
+ yard (0.8.3)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ bundler
35
+ jeweler
36
+ rdoc
37
+ rspec
38
+ simplecov
39
+ yard
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 max thom stahl
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # The Gambler
2
+
3
+ Yet another poker hand evaluator. I'm writing it because poker hand evaluators
4
+ are useful to me in some of my side projects. If it's useful to you, too, all
5
+ the better.
6
+
7
+ ## Usage
8
+
9
+ Include the gem in your Gemfile:
10
+
11
+ gem 'the_gambler'
12
+
13
+ Start rockin':
14
+
15
+ card = Card.new "AH"
16
+ hand = Hand.new "QH", card, %w{10 D}
17
+
18
+ You can initialize `Card`s using a (case-insensitive) String (_e.g._ `"JC"`),
19
+ an Array (_e.g._ `['9', 'S']`), or a Hash (_e.g._ `{rank: 'Q', suit: :diamonds}`).
20
+
21
+ ## Beta progress
22
+
23
+ Right now, the following stuff works. Assume anything not mentioned in this list doesn't work properly.
24
+
25
+ * Blackjack hand evaluation
26
+ * (Very) Rough poker hand evaluation (i.e., flush > three of a kind, but not distinguishing between different instances of hands)
27
+ * Exact poker hand evaluation of all types of hands (!!!).
28
+
29
+ ### Known issues
30
+
31
+ * Hands with the same ranking but different high cards are valued equally.
32
+
33
+ ## Contributing to the_gambler
34
+
35
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
36
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
37
+ * Fork the project.
38
+ * Start a feature/bugfix branch.
39
+ * Commit and push until you are happy with your contribution.
40
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
41
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
42
+
43
+ ## Copyright
44
+
45
+ Copyright (c) 2012 max thom stahl. See LICENSE.txt for
46
+ further details.
47
+
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "the_gambler"
18
+ gem.homepage = "http://github.com/mstahl/the_gambler"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Classes and modules for card-playing apps.}
21
+ gem.description = %Q{
22
+ Really common tasks in programs that use playing cards are a pain to implement. This is my
23
+ implementation. Use it. Or don't.
24
+ }
25
+ gem.email = "max@villainousindustries.com"
26
+ gem.authors = ["max thom stahl"]
27
+ # dependencies defined in Gemfile
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rspec/core'
32
+ require 'rspec/core/rake_task'
33
+ RSpec::Core::RakeTask.new(:spec) do |spec|
34
+ spec.pattern = FileList['spec/**/*_spec.rb']
35
+ end
36
+
37
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
38
+ spec.pattern = 'spec/**/*_spec.rb'
39
+ spec.rcov = true
40
+ end
41
+
42
+ task :default => :spec
43
+
44
+ require 'yard'
45
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,4 @@
1
+ Dir[File.join(File.dirname(__FILE__), 'the_gambler', '**', '*.rb')].each {|f| require f}
2
+
3
+ module TheGambler
4
+ end
@@ -0,0 +1,31 @@
1
+ module TheGambler
2
+ module Blackjack
3
+
4
+ def blackjack?
5
+ contents.count == 2 and contents.select(&:face_card?).count == 1 and contents.select(&:ace?).count == 1
6
+ end
7
+
8
+ def blackjack_value
9
+ value = 0
10
+ # Add up all the values in the hand that aren't aces.
11
+ contents.reject(&:ace?).each do |card|
12
+ if card.face_card? then
13
+ value += 10
14
+ else
15
+ value += card.numerical_value
16
+ end
17
+ end
18
+
19
+ # Now add up the aces greedily
20
+ contents.select(&:ace?).each do |card|
21
+ if 21 - value >= 11 then
22
+ value += 11
23
+ else
24
+ value += 1
25
+ end
26
+ end
27
+
28
+ value
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,67 @@
1
+ module TheGambler
2
+ class Card
3
+ attr_reader :raw
4
+
5
+ SUIT_STRINGS = %w{S C H D}
6
+ SUIT_SYMBOLS = %w{spades clubs hearts diamonds}.map(&:to_sym)
7
+ RANKS = %w{2 3 4 5 6 7 8 9 10 J Q K A}
8
+
9
+ def initialize(arg)
10
+ case arg.class.to_s
11
+ when 'String'
12
+ if arg =~ /([ajqk2-9]|10)([SCHD])/i then
13
+ rank, suit = $1.upcase, $2
14
+ else
15
+ raise ArgumentError.new("Invalid string: '#{arg}'")
16
+ end
17
+ when 'Array'
18
+ if RANKS.include?(arg[0]) and SUIT_STRINGS.include?(arg[1]) then
19
+ rank, suit = arg[0].upcase, arg[1]
20
+ else
21
+ raise ArgumentError.new("Invalid array: #{arg.inspect}")
22
+ end
23
+ when 'Hash'
24
+ if RANKS.include?(arg[:rank]) and SUIT_STRINGS.include?(arg[:suit]) then
25
+ rank, suit = arg[:rank].upcase, arg[:suit]
26
+ else
27
+ raise ArgumentError.new("Invalid hash: #{arg.inspect}")
28
+ end
29
+ when 'Card','TheGambler::Card'
30
+ rank, suit = arg.rank, arg.suit
31
+ else
32
+ raise ArgumentError.new("Must supply either a String, an Array, or a Hash, not a #{arg.class.to_s}")
33
+ end
34
+
35
+ if SUIT_STRINGS.include?(suit)
36
+ @raw = 13 * SUIT_STRINGS.index(suit) + RANKS.index(rank)
37
+ elsif SUIT_SYMBOLS.include?(suit)
38
+ @raw = 13 * SUIT_SYMBOLS.index(suit) + RANKS.index(rank)
39
+ end
40
+ end
41
+
42
+ def ace?
43
+ rank == 'A'
44
+ end
45
+
46
+ def face_card?
47
+ %w{J Q K}.include?(rank)
48
+ end
49
+
50
+ def numerical_value
51
+ RANKS.index(rank) + 2
52
+ end
53
+
54
+ def rank
55
+ RANKS[@raw % 13]
56
+ end
57
+
58
+ def suit
59
+ SUIT_SYMBOLS[@raw / 13]
60
+ end
61
+
62
+ def to_s
63
+ "#{rank}#{SUIT_STRINGS[@raw / 13]}"
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,4 @@
1
+ module TheGambler
2
+ class Deck
3
+ end
4
+ end
@@ -0,0 +1,16 @@
1
+ require 'the_gambler/poker'
2
+ require 'the_gambler/blackjack'
3
+
4
+ module TheGambler
5
+ class Hand
6
+ include TheGambler::Blackjack
7
+ include TheGambler::Poker
8
+
9
+ attr_reader :contents
10
+
11
+ def initialize(*args)
12
+ @contents = []
13
+ args.each{|a| @contents << Card.new(a) }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,94 @@
1
+ module TheGambler
2
+ module Poker
3
+ POKER_HANDS = %w{
4
+ high_card one_pair two_pair three_of_a_kind straight flush full_house four_of_a_kind straight_flush royal_flush
5
+ }
6
+
7
+ # Valuation methods ===========================================================================
8
+
9
+ def poker_value
10
+ if royal_flush? then
11
+ 10e10
12
+ elsif straight_flush? then
13
+ if contents.map(&:numerical_value).sort == [2, 3, 4, 5, 14] then
14
+ 10e9 + 1
15
+ else
16
+ 10e9 + contents.max_by(&:numerical_value).numerical_value
17
+ end
18
+ elsif four_of_a_kind? then
19
+ c = contents.group_by(&:numerical_value)
20
+ 10e8 + c.keys.max_by{|k| c[k].count}
21
+ elsif full_house? then
22
+ c = contents.group_by(&:numerical_value)
23
+ 10e7 + c.keys.detect{|k| c[k].count == 3} * 13 + c.keys.detect{|k| c[k].count == 2}
24
+ elsif flush? then
25
+ 10e6 + contents.max_by(&:numerical_value).numerical_value
26
+ elsif straight? then
27
+ if contents.map(&:numerical_value).sort == [2, 3, 4, 5, 14] then
28
+ 10e5 + 1
29
+ else
30
+ 10e5 + contents.max_by(&:numerical_value).numerical_value
31
+ end
32
+ elsif three_of_a_kind? then
33
+ c = contents.group_by(&:numerical_value)
34
+ 10e4 + c.keys.detect{|k| c[k].count == 3}
35
+ elsif two_pair? then
36
+ c = contents.group_by(&:numerical_value)
37
+
38
+ pair_one, pair_two = c.keys.select{|k| c[k].count == 2}.minmax
39
+
40
+ 10e3 + 13 * pair_two + pair_one
41
+ elsif one_pair? then
42
+ c = contents.group_by(&:numerical_value)
43
+ 10e2 + c.keys.detect{|k| c[k].count == 2}
44
+ elsif high_card? then
45
+ contents.max_by(&:numerical_value).numerical_value
46
+ end
47
+ end
48
+
49
+ # Different kinds of hands ====================================================================
50
+
51
+ def high_card?
52
+ true # Every hand has a high card.
53
+ end
54
+
55
+ def one_pair?
56
+ contents.sort_by(&:numerical_value).map(&:to_s).join =~ %r{(.{1,2})[SCHD]\1[SCHD]}i
57
+ end
58
+
59
+ def two_pair?
60
+ contents.sort_by(&:numerical_value).map(&:to_s).join =~ %r{(.{1,2})[SCHD]\1[SCHD].*(.{1,2})[SCHD]\2[SCHD]}i
61
+ end
62
+
63
+ def three_of_a_kind?
64
+ contents.sort_by(&:numerical_value).map(&:to_s).join =~ %r{(.{1,2})[SCHD]\1[SCHD]\1[SCHD]}i
65
+ end
66
+
67
+ def straight?
68
+ values = contents.map(&:numerical_value).sort
69
+ values == (values.min..values.max).to_a or values == [2, 3, 4, 5, 14]
70
+ end
71
+
72
+ def flush?
73
+ contents.map(&:suit).uniq.count == 1
74
+ end
75
+
76
+ def full_house?
77
+ as_string = contents.sort_by(&:numerical_value).map(&:to_s).join
78
+ as_string =~ %r{(.{1,2})[SCHD]\1[SCHD]\1[SCHD](.{1,2})[SCHD]\2[SCHD]} or as_string =~ %r{(.{1,2})[SCHD]\1[SCHD](.{1,2})[SCHD]\2[SCHD]\2[SCHD]}
79
+ end
80
+
81
+ def four_of_a_kind?
82
+ contents.sort_by(&:numerical_value).map(&:to_s).join =~ %r{(.{1,2})[SCHD]\1[SCHD]\1[SCHD]\1[SCHD]}i
83
+ end
84
+
85
+ def straight_flush?
86
+ straight? and flush?
87
+ end
88
+
89
+ def royal_flush?
90
+ contents.sort_by(&:numerical_value).map(&:to_s).join =~ %r{\A10([SCHD])J\1Q\1K\1A\1\Z}i
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+
5
+ describe Card do
6
+ describe '#initialize' do
7
+
8
+ it 'can take a String as its argument' do
9
+ (->{ Card.new "AD" }).should_not raise_error
10
+ end
11
+
12
+ it 'can take an Array as its argument' do
13
+ (->{ Card.new ['A', 'D'] }).should_not raise_error
14
+ end
15
+
16
+ it 'can take a Hash as its argument' do
17
+ (->{ Card.new rank: 'A', suit: 'D' }).should_not raise_error
18
+ end
19
+
20
+ it 'can take a Card as its argument' do
21
+ (->{ Card.new(Card.new rank: 'A', suit: 'D') }).should_not raise_error
22
+ end
23
+
24
+ describe 'with hash' do
25
+ Card::RANKS.each do |rank|
26
+ Card::SUIT_STRINGS.each_with_index do |suit, i|
27
+ it "should correctly parse {rank: #{rank}, suit: #{suit}}" do
28
+ card = Card.new rank: rank, suit: suit
29
+ card.rank.should eq(rank)
30
+ card.suit.should eq(Card::SUIT_SYMBOLS[i])
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ describe 'with array' do
37
+ Card::RANKS.each do |rank|
38
+ Card::SUIT_STRINGS.each_with_index do |suit, i|
39
+ it "should correctly parse [#{rank}, #{suit}]" do
40
+ card = Card.new [rank, suit]
41
+ card.rank.should eq(rank)
42
+ card.suit.should eq(Card::SUIT_SYMBOLS[i])
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ describe 'with string' do
49
+ Card::RANKS.each do |rank|
50
+ Card::SUIT_STRINGS.each_with_index do |suit, i|
51
+ it "should correctly parse '#{rank}#{suit}'" do
52
+ card = Card.new "#{rank}#{suit}"
53
+ card.rank.should eq(rank)
54
+ card.suit.should eq(Card::SUIT_SYMBOLS[i])
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+
5
+ describe Deck do
6
+ let(:deck) { Deck.new }
7
+
8
+ it 'should default to sorted, not shuffled' do
9
+
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "flush hand" do
5
+ %w{S C H D}.each do |suit|
6
+ it "should correctly identify a flush of #{suit}'s" do
7
+ Hand.new("4#{suit}", "7#{suit}", "10#{suit}", "A#{suit}", "J#{suit}").flush?.should be_true
8
+ end
9
+ end
10
+
11
+ describe 'valuation' do
12
+ it 'should recognize that a 10-high flush is more valuable than a 9-high flush' do
13
+ Hand.new(*%w{10S 5S 2S 6S 9S}).poker_value.should > Hand.new(*%w{9S 5S 2S 4S 3S}).poker_value
14
+ end
15
+
16
+ it 'should recognize that an ace-high flush is more valuable than a 10-high flush' do
17
+ Hand.new(*%w{AH QH 10H 2H 7H}).poker_value.should > Hand.new(*%w{10S 5S 2S 6S 9S}).poker_value
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "four of a kind hand" do
5
+ Card::RANKS.each do |rank|
6
+ it "should correctly identify four #{rank}'s" do
7
+ Hand.new("#{rank}C", "#{rank}C", "#{rank}C", "#{rank}C", "#{(Card::RANKS - [rank]).shuffle.first}H").four_of_a_kind?.should be_true
8
+ end
9
+ end
10
+
11
+ describe 'valuation' do
12
+ it 'should recognize that four 10s is more valuable than four 5s' do
13
+ Hand.new(*%w{10S 10H 10C 10D AS}).poker_value.should > Hand.new(*%w{5S 5C 5H 5D 10D}).poker_value
14
+ end
15
+
16
+ it 'should recognize that four aces is more valuable than four kings' do
17
+ Hand.new(*%w{AS AH AC AD AS}).poker_value.should > Hand.new(*%w{KS KC KH KD 10D}).poker_value
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "full house hand" do
5
+ Card::RANKS.each do |primary_rank|
6
+ (Card::RANKS - [primary_rank]).each do |secondary_rank|
7
+ it "should correctly identify #{primary_rank}'s over #{secondary_rank}'s" do
8
+ Hand.new("#{primary_rank}C", "#{primary_rank}H", "#{primary_rank}S", "#{secondary_rank}C", "#{secondary_rank}D").full_house?.should be_true
9
+ end
10
+ end
11
+ end
12
+
13
+ describe 'valuation' do
14
+ it 'should recognize that 10s over 5s is more valuable than 9s over 4s' do
15
+ Hand.new(*%w{10S 10C 10D 5D 5C}).poker_value.should > Hand.new(*%w{9C 9H 9D 4D 4C}).poker_value
16
+ end
17
+
18
+ it 'should recognize that 10s over 5s is more valuable than 10s over 4s' do
19
+ Hand.new(*%w{10S 10C 10D 5D 5C}).poker_value.should > Hand.new(*%w{10S 10C 10D 4D 4C}).poker_value
20
+ end
21
+
22
+ it 'should recognize that 10s over 5s is more valuable than 5s over 10s' do
23
+ Hand.new(*%w{10S 10C 10D 5D 5C}).poker_value.should > Hand.new(*%w{10S 10C 5D 5D 5C}).poker_value
24
+ end
25
+
26
+ it 'should recognize that 10s over aces is more valuable than 10s over 4s' do
27
+ Hand.new(*%w{10S 10C 10D AD AC}).poker_value.should > Hand.new(*%w{10S 10C 10D 4D 4C}).poker_value
28
+ end
29
+
30
+ it 'should recognize that aces over 2s is more valuable than kings over aces' do
31
+ Hand.new(*%w{AS AC AD 2D 2C}).poker_value.should > Hand.new(*%w{KS KC KD AD AC}).poker_value
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "high card hand" do
5
+
6
+ describe 'valuation' do
7
+ it 'should recognize that ace high is more valuable than king high' do
8
+ Hand.new(*%w{AH QD 10C 5H 2D}).poker_value.should > Hand.new(*%w{KH 10H 8C 4C 2D}).poker_value
9
+ end
10
+
11
+ it 'should recognize that queen high is more valuable than 8 high' do
12
+ Hand.new(*%w{QD 10C 7D 4H 2D}).poker_value.should > Hand.new(*%w{8D 5H 7D 2C 3H}).poker_value
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "one pair hand" do
5
+
6
+ describe 'valuation' do
7
+ it 'should recognize that a pair of aces is more valuable than a pair of kings' do
8
+ Hand.new(*%w{AH AD 10C 5H 2D}).poker_value.should > Hand.new(*%w{KH KH 8C 4C 2D}).poker_value
9
+ end
10
+
11
+ it 'should recognize that a pair of queens is more valuable than a pair of 10s' do
12
+ Hand.new(*%w{QD QC 7D 4H 2D}).poker_value.should > Hand.new(*%w{10D 10H 7D 2C 3H}).poker_value
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "royal flush hand" do
5
+ %w{S C H D}.each do |suit|
6
+ it "should correctly identify a flush of #{suit}'s" do
7
+ Hand.new("A#{suit}", "K#{suit}", "Q#{suit}", "J#{suit}", "10#{suit}").royal_flush?.should be_true
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "straight flush hands" do
5
+
6
+ %w{S C H D}.each do |suit|
7
+ let(:nine_high) { Hand.new("5#{suit}", "6#{suit}", "7#{suit}", "8#{suit}", "9#{suit}") }
8
+ let(:five_high) { Hand.new("5#{suit}", "4#{suit}", "3#{suit}", "2#{suit}", "A#{suit}") }
9
+ let(:ace_high) { Hand.new("10#{suit}", "J#{suit}", "K#{suit}", "Q#{suit}", "A#{suit}") }
10
+
11
+ it 'should recognize a trivial straight flush' do
12
+ nine_high.straight_flush?.should be_true
13
+ end
14
+
15
+ it 'should recognize a 5-high straight flush' do
16
+ five_high.straight_flush?.should be_true
17
+ end
18
+
19
+ it 'should recognize an ace-high straight flush' do
20
+ ace_high.straight_flush?.should be_true
21
+ end
22
+
23
+ describe 'valuation' do
24
+ it 'should recognize that a 9-high straight flush is more valuable than a 5-high flush' do
25
+ nine_high.poker_value.should > five_high.poker_value
26
+ end
27
+
28
+ it 'should recognize that an ace-high straight flush is more valuable than a 9-high straight flush' do
29
+ ace_high.poker_value.should > nine_high.poker_value
30
+ end
31
+
32
+ it 'should recognize that an ace-high straight flush is more valuable than a 5-high straight flush' do
33
+ ace_high.poker_value.should > five_high.poker_value
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "straight hands" do
5
+
6
+ let(:ten_high) { Hand.new('6D', '7C', '8D', '9D', '10C') }
7
+ let(:nine_high) { Hand.new('5D', '6C', '7D', '8D', '9C') }
8
+ let(:ace_high) { Hand.new('AD', 'KC', 'QD', 'JD', '10C') }
9
+ let(:five_high) { Hand.new('AD', '2C', '3D', '4D', '5C') }
10
+
11
+ it 'should recognize a trivial straight' do
12
+ Hand.new(*%w{5D 6C 7H 8S 9D}).straight?.should be_true
13
+ end
14
+
15
+ it 'should recognize a 5-high straight' do
16
+ Hand.new(*%w{5D 4C 3H 2S AD}).straight?.should be_true
17
+ end
18
+
19
+ it 'should recognize an ace-high straight' do
20
+ Hand.new(*%w{10D JC KH QS AD}).straight?.should be_true
21
+ end
22
+
23
+ context "compared to other straight hands" do
24
+ it "should value an ace-high straight higher than a 9-high straight" do
25
+ ace_high.poker_value.should > nine_high.poker_value
26
+ end
27
+
28
+ it "should value an ace-high straight higher than a 5-high straight" do
29
+ ace_high.poker_value.should > five_high.poker_value
30
+ end
31
+
32
+ it "should value a 10-high straight higher than a 9-high straight" do
33
+ ten_high.poker_value.should > nine_high.poker_value
34
+ end
35
+
36
+ it "should value a 10-high straight higher than a 5-high straight" do
37
+ ten_high.poker_value.should > five_high.poker_value
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "three of a kind hand" do
5
+ Card::RANKS.each do |rank|
6
+ it "should correctly identify three #{rank}'s" do
7
+ a = (Card::RANKS - [rank]).shuffle.first
8
+ b = (Card::RANKS - [rank, a]).shuffle.first
9
+
10
+ Hand.new("#{rank}C", "#{rank}H", "#{rank}D", "#{a}H", "#{b}H").three_of_a_kind?.should be_true
11
+ end
12
+ end
13
+
14
+ describe 'valuation' do
15
+ it 'should recognize that three 10s is more valuable than three 5s' do
16
+ Hand.new(*%w{10S 10H 10C 9D AS}).poker_value.should > Hand.new(*%w{5S 5C 5H 3D 10D}).poker_value
17
+ end
18
+
19
+ it 'should recognize that three aces is more valuable than three kings' do
20
+ Hand.new(*%w{AS AH AC 10D 7C}).poker_value.should > Hand.new(*%w{KS KC KH QD 10D}).poker_value
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+ describe "two pair hand" do
5
+ Card::RANKS.each do |primary_rank|
6
+ (Card::RANKS - [primary_rank]).each do |secondary_rank|
7
+ it "should correctly identify #{primary_rank}'s over #{secondary_rank}'s" do
8
+ Hand.new(
9
+ "#{primary_rank}C", "#{primary_rank}H",
10
+ "#{secondary_rank}C", "#{secondary_rank}D",
11
+ "#{(Card::RANKS - [primary_rank, secondary_rank]).first}H"
12
+ ).two_pair?.should be_true
13
+ end
14
+ end
15
+ end
16
+
17
+ describe 'valuation' do
18
+ it 'should value two pair queens and 10s higher than two pair jacks and 10s' do
19
+ Hand.new(*%w{QH QD 10H 10C 8D}).poker_value.should > Hand.new(*%w{JH JD 10H 10C 8D}).poker_value
20
+ end
21
+
22
+ it 'should value two pair 10s and 4s higher than 10s and 2s' do
23
+ Hand.new(*%w{10H 10C 4H 4C 8D}).poker_value.should > Hand.new(*%w{10H 10C 2D 2H 8D}).poker_value
24
+ end
25
+
26
+ it 'should value two pair aces and 2s higher than kings and tens' do
27
+ Hand.new(*%w{AH AD 2D 2H 8D}).poker_value.should > Hand.new(*%w{KH KD 10D 10C 8C}).poker_value
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,148 @@
1
+ require 'spec_helper'
2
+
3
+ module TheGambler
4
+
5
+ describe Hand do
6
+
7
+ include_examples "poker hands"
8
+
9
+ describe '#blackjack?' do
10
+ it 'should identify JH AC as a blackjack' do
11
+ Hand.new(*%w{JH AC}).should be_blackjack
12
+ end
13
+
14
+ it 'should not identify AC 4D as a blackjack' do
15
+ Hand.new(*%w{AC 4D}).should_not be_blackjack
16
+ end
17
+ end
18
+
19
+ describe '#blackjack_value' do
20
+ context 'without aces' do
21
+ it 'should value basic two-card hands' do
22
+ Hand.new(*%w{2D 9D}).blackjack_value.should eq(11)
23
+ end
24
+
25
+ it 'should value hands with face cards' do
26
+ Hand.new(*%w{QD 8C}).blackjack_value.should eq(18)
27
+ end
28
+ end
29
+
30
+ context 'with aces' do
31
+ it 'should value a soft 19' do
32
+ Hand.new(*%w{AD 8C}).blackjack_value.should eq(19)
33
+ end
34
+
35
+ it 'should value 21 with two face cards' do
36
+ Hand.new(*%w{QD JC AH}).blackjack_value.should eq(21)
37
+ end
38
+
39
+ it 'should value 21 with 5 cards' do
40
+ Hand.new(*%w{5D 2D 3C 10H AS}).blackjack_value.should eq(21)
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ describe '#poker_value' do
47
+ it 'should value a royal_flush over a straight_flush' do
48
+ royal_flush.poker_value.should > straight_flush.poker_value
49
+ end
50
+
51
+ it 'should value a straight_flush over a four_of_a_kind' do
52
+ straight_flush.poker_value.should > four_of_a_kind.poker_value
53
+ end
54
+
55
+ it 'should value a four_of_a_kind over a full_house' do
56
+ four_of_a_kind.poker_value.should > full_house.poker_value
57
+ end
58
+
59
+ it 'should value a full_house over a flush' do
60
+ full_house.poker_value.should > flush.poker_value
61
+ end
62
+
63
+ it 'should value a flush over a straight' do
64
+ flush.poker_value.should > straight.poker_value
65
+ end
66
+
67
+ it 'should value a straight over a three_of_a_kind' do
68
+ straight.poker_value.should > three_of_a_kind.poker_value
69
+ end
70
+
71
+ it 'should value a three_of_a_kind over a two_pair' do
72
+ three_of_a_kind.poker_value.should > two_pair.poker_value
73
+ end
74
+
75
+ it 'should value a two_pair over a one_pair' do
76
+ two_pair.poker_value.should > one_pair.poker_value
77
+ end
78
+
79
+ it 'should value a one_pair over a high_card' do
80
+ one_pair.poker_value.should > high_card.poker_value
81
+ end
82
+ end
83
+
84
+ describe '#royal_flush?' do
85
+ it 'should identify royal flushes' do
86
+ royal_flush.royal_flush?.should be_true
87
+ end
88
+ end
89
+
90
+ describe '#straight_flush' do
91
+ it 'should identify straight flushes' do
92
+ straight_flush.straight_flush?.should be_true
93
+ end
94
+ end
95
+
96
+ describe '#four_of_a_kind' do
97
+ it 'should identify four of a kind' do
98
+ four_of_a_kind.four_of_a_kind?.should be_true
99
+ end
100
+ end
101
+
102
+ describe '#full_house' do
103
+ it 'should identify full houses' do
104
+ full_house.full_house?.should be_true
105
+ end
106
+ end
107
+
108
+ describe '#flush' do
109
+ it 'should identify flushes' do
110
+ flush.flush?.should be_true
111
+ straight_flush.flush?.should be_true
112
+ end
113
+ end
114
+
115
+ describe '#straight' do
116
+ it 'should identify straights' do
117
+ straight.straight?.should be_true
118
+ straight_flush.straight?.should be_true
119
+ end
120
+ end
121
+
122
+ describe '#three_of_a_kind' do
123
+ it 'should identify three of a kind' do
124
+ three_of_a_kind.three_of_a_kind?.should be_true
125
+ end
126
+ end
127
+
128
+ describe '#two_pair' do
129
+ it 'should identify two pair' do
130
+ two_pair.two_pair?.should be_true
131
+ end
132
+ end
133
+
134
+ describe '#one_pair' do
135
+ it 'should identify one pair' do
136
+ one_pair.one_pair?.should be_true
137
+ end
138
+ end
139
+
140
+ describe '#high_card' do
141
+ it 'should identify high card' do
142
+ high_card.high_card?.should be_true
143
+ end
144
+ end
145
+
146
+
147
+ end
148
+ end
File without changes
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'the_gambler'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,12 @@
1
+ shared_examples "poker hands" do
2
+ let(:royal_flush) { TheGambler::Hand.new "AD", "KD", "QD", "JD", "10D" }
3
+ let(:straight_flush) { TheGambler::Hand.new "JC", "10C", "9C", "8C", "7C" }
4
+ let(:four_of_a_kind) { TheGambler::Hand.new "4D", "4C", "4S", "4H", "AC" }
5
+ let(:full_house) { TheGambler::Hand.new "5S", "5H", "5D", "8S", "8H" }
6
+ let(:flush) { TheGambler::Hand.new "2D", "7D", "8D", "QD", "3D" }
7
+ let(:straight) { TheGambler::Hand.new "AC", "2D", "3D", "4D", "5D" }
8
+ let(:three_of_a_kind) { TheGambler::Hand.new "AD", "AC", "AS", "JD", "10D" }
9
+ let(:two_pair) { TheGambler::Hand.new "AD", "AC", "QD", "QS", "10D" }
10
+ let(:one_pair) { TheGambler::Hand.new "AD", "AC", "QD", "JD", "10D" }
11
+ let(:high_card) { TheGambler::Hand.new "AD", "3H", "QD", "JD", "10D" }
12
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "TheGambler" do
4
+ end
@@ -0,0 +1,86 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "the_gambler"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["max thom stahl"]
12
+ s.date = "2012-11-18"
13
+ s.description = "\n Really common tasks in programs that use playing cards are a pain to implement. This is my\n implementation. Use it. Or don't.\n "
14
+ s.email = "max@villainousindustries.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "lib/the_gambler.rb",
29
+ "lib/the_gambler/blackjack.rb",
30
+ "lib/the_gambler/card.rb",
31
+ "lib/the_gambler/deck.rb",
32
+ "lib/the_gambler/hand.rb",
33
+ "lib/the_gambler/poker.rb",
34
+ "spec/cards/initialization_spec.rb",
35
+ "spec/decks/deck_spec.rb",
36
+ "spec/hands/hand_flush_spec.rb",
37
+ "spec/hands/hand_four_of_a_kind_spec.rb",
38
+ "spec/hands/hand_full_house_spec.rb",
39
+ "spec/hands/hand_high_card_spec.rb",
40
+ "spec/hands/hand_one_pair_spec.rb",
41
+ "spec/hands/hand_royal_flush_spec.rb",
42
+ "spec/hands/hand_straight_flush_spec.rb",
43
+ "spec/hands/hand_straight_spec.rb",
44
+ "spec/hands/hand_three_of_a_kind_spec.rb",
45
+ "spec/hands/hand_two_pair_spec.rb",
46
+ "spec/hands/hands_spec.rb",
47
+ "spec/players/player_spec.rb",
48
+ "spec/spec_helper.rb",
49
+ "spec/support/shared_examples/shared_poker_hands.rb",
50
+ "spec/the_gambler_spec.rb",
51
+ "the_gambler.gemspec"
52
+ ]
53
+ s.homepage = "http://github.com/mstahl/the_gambler"
54
+ s.licenses = ["MIT"]
55
+ s.require_paths = ["lib"]
56
+ s.rubygems_version = "1.8.24"
57
+ s.summary = "Classes and modules for card-playing apps."
58
+
59
+ if s.respond_to? :specification_version then
60
+ s.specification_version = 3
61
+
62
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
63
+ s.add_development_dependency(%q<rspec>, [">= 0"])
64
+ s.add_development_dependency(%q<yard>, [">= 0"])
65
+ s.add_development_dependency(%q<rdoc>, [">= 0"])
66
+ s.add_development_dependency(%q<bundler>, [">= 0"])
67
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
68
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
69
+ else
70
+ s.add_dependency(%q<rspec>, [">= 0"])
71
+ s.add_dependency(%q<yard>, [">= 0"])
72
+ s.add_dependency(%q<rdoc>, [">= 0"])
73
+ s.add_dependency(%q<bundler>, [">= 0"])
74
+ s.add_dependency(%q<jeweler>, [">= 0"])
75
+ s.add_dependency(%q<simplecov>, [">= 0"])
76
+ end
77
+ else
78
+ s.add_dependency(%q<rspec>, [">= 0"])
79
+ s.add_dependency(%q<yard>, [">= 0"])
80
+ s.add_dependency(%q<rdoc>, [">= 0"])
81
+ s.add_dependency(%q<bundler>, [">= 0"])
82
+ s.add_dependency(%q<jeweler>, [">= 0"])
83
+ s.add_dependency(%q<simplecov>, [">= 0"])
84
+ end
85
+ end
86
+
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: the_gambler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - max thom stahl
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: yard
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rdoc
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: jeweler
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: simplecov
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: ! "\n Really common tasks in programs that use playing cards are a
111
+ pain to implement. This is my\n implementation. Use it. Or don't.\n "
112
+ email: max@villainousindustries.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files:
116
+ - LICENSE.txt
117
+ - README.md
118
+ files:
119
+ - .document
120
+ - .rspec
121
+ - Gemfile
122
+ - Gemfile.lock
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - VERSION
127
+ - lib/the_gambler.rb
128
+ - lib/the_gambler/blackjack.rb
129
+ - lib/the_gambler/card.rb
130
+ - lib/the_gambler/deck.rb
131
+ - lib/the_gambler/hand.rb
132
+ - lib/the_gambler/poker.rb
133
+ - spec/cards/initialization_spec.rb
134
+ - spec/decks/deck_spec.rb
135
+ - spec/hands/hand_flush_spec.rb
136
+ - spec/hands/hand_four_of_a_kind_spec.rb
137
+ - spec/hands/hand_full_house_spec.rb
138
+ - spec/hands/hand_high_card_spec.rb
139
+ - spec/hands/hand_one_pair_spec.rb
140
+ - spec/hands/hand_royal_flush_spec.rb
141
+ - spec/hands/hand_straight_flush_spec.rb
142
+ - spec/hands/hand_straight_spec.rb
143
+ - spec/hands/hand_three_of_a_kind_spec.rb
144
+ - spec/hands/hand_two_pair_spec.rb
145
+ - spec/hands/hands_spec.rb
146
+ - spec/players/player_spec.rb
147
+ - spec/spec_helper.rb
148
+ - spec/support/shared_examples/shared_poker_hands.rb
149
+ - spec/the_gambler_spec.rb
150
+ - the_gambler.gemspec
151
+ homepage: http://github.com/mstahl/the_gambler
152
+ licenses:
153
+ - MIT
154
+ post_install_message:
155
+ rdoc_options: []
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ! '>='
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ segments:
165
+ - 0
166
+ hash: 417615722838553205
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ none: false
169
+ requirements:
170
+ - - ! '>='
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubyforge_project:
175
+ rubygems_version: 1.8.24
176
+ signing_key:
177
+ specification_version: 3
178
+ summary: Classes and modules for card-playing apps.
179
+ test_files: []