hands 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'rspec', '~>2.7.0'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Sam Soffes
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Run specs'
6
+ RSpec::Core::RakeTask.new do |t|
7
+ t.rspec_opts = ['--color']
8
+ end
data/Readme.markdown ADDED
@@ -0,0 +1,73 @@
1
+ # Hands
2
+
3
+ Simple library for calculating poker hands.
4
+
5
+ Currently this gem is very limited. I plan on adding outs, odds, and other actually useful stuff. I started writing this on a plane as a personal challenge. It's current state is crude, although tested and works.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ``` ruby
12
+ gem 'hands'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install hands
22
+
23
+ ## Usage
24
+
25
+ ``` ruby
26
+ # Best hand detection
27
+ straight = Hands::Hand.new
28
+ straight << Hands::Card['A', :hearts]
29
+ straight << Hands::Card[2, :spades]
30
+ straight << Hands::Card[3, :diamonds]
31
+ straight << Hands::Card[4, :hearts]
32
+ straight << Hands::Card[5, :clubs]
33
+ straight.best_hand[:type] # 'straight'
34
+
35
+ # Hand comparison
36
+ pair = Hands::Hand.new
37
+ pair << Hands::Card[2, :hearts]
38
+ pair << Hands::Card[2, :clubs]
39
+ pair << Hands::Card[3, :diamonds]
40
+ pair << Hands::Card[4, :hearts]
41
+ pair << Hands::Card[5, :clubs]
42
+
43
+ flush = Hands::Hand.new
44
+ flush << Hands::Card[6, :hearts]
45
+ flush << Hands::Card[7, :hearts]
46
+ flush << Hands::Card[8, :hearts]
47
+ flush << Hands::Card[2, :hearts]
48
+ flush << Hands::Card[4, :hearts]
49
+ flush > pair # true
50
+
51
+ # Card comparison
52
+ card1 = Hands::Card.new(:value => 2, :suite => :hearts)
53
+ card2 = Hands::Card.new(:value => 3, :suite => :clubs)
54
+ card2 > card1 # true
55
+ ```
56
+
57
+ ## Running Tests
58
+
59
+ Running and reading the tests is (for now) the best way to see the functionality of this gem.
60
+
61
+ ```
62
+ $ bundle
63
+ $ bundle exec rake spec
64
+ ```
65
+
66
+ ## Contributing
67
+
68
+ 1. Fork it
69
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
70
+ 3. Write a passing spec
71
+ 4. Commit your changes (`git commit -am 'Added some feature'`)
72
+ 5. Push to the branch (`git push origin my-new-feature`)
73
+ 6. Create new Pull Request
data/hands.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/hands/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ['Sam Soffes']
6
+ gem.email = ['sam@samsoff.es']
7
+ gem.description = 'Simple library for various poker hands calculations.'
8
+ gem.summary = 'Simple library for various poker hands calculations.'
9
+ gem.homepage = 'http://github.com/samsoffes/hands'
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = 'hands'
15
+ gem.require_paths = ['lib']
16
+ gem.version = Hands::VERSION
17
+ end
data/lib/hands/card.rb ADDED
@@ -0,0 +1,90 @@
1
+ module Hands
2
+ class Card
3
+ include Comparable
4
+
5
+ attr_accessor :suite
6
+ attr_accessor :value
7
+
8
+ def self.[](first = nil, second = nil)
9
+ self.new(first, second)
10
+ end
11
+
12
+ def initialize(first = nil, suite = nil)
13
+ # Value provided
14
+ if first.is_a?(Integer) or first.is_a?(String)
15
+ self.value = first
16
+
17
+ # Hash provided
18
+ elsif first.is_a?(Hash)
19
+ self.value = first[:value] if first[:value]
20
+ self.suite = first[:suite] if first[:suite]
21
+ end
22
+
23
+ # Set suite
24
+ self.suite = suite if suite
25
+ end
26
+
27
+ def value=(val)
28
+ # Integer
29
+ if val.is_a?(Integer)
30
+ # Number range
31
+ if val > 0 and val <= 10
32
+ @value = val
33
+ return
34
+
35
+ # Face card or ace range
36
+ elsif val > 10 and val < 15
37
+ @value = VALUES[val - 2]
38
+ return
39
+ end
40
+
41
+ # String
42
+ elsif val.is_a?(String) and VALUES.include?(val.downcase)
43
+ @value = val.downcase
44
+ return
45
+ end
46
+
47
+ # Invalid
48
+ @value = nil
49
+ end
50
+
51
+ def inspect
52
+ if self.is_valid?
53
+ "#{super} #{self.description}"
54
+ else
55
+ super
56
+ end
57
+ end
58
+
59
+ def is_valid?
60
+ SUITES.include?(self.suite.to_s) and VALUES.include?(self.value.to_s.downcase)
61
+ end
62
+
63
+ def is_invalid?
64
+ !self.is_valid?
65
+ end
66
+
67
+ def description
68
+ if self.is_valid?
69
+ "#{VALUE_DESCRIPTIONS[self.value_index].capitalize} of #{self.suite.to_s.capitalize}"
70
+ else
71
+ 'invalid'
72
+ end
73
+ end
74
+
75
+ def <=>(other_card, check_suite = false)
76
+ # TODO: Handle invalid cards
77
+ result = self.value_index <=> other_card.value_index
78
+ return self.suite_index <=> other_card.suite_index if result == 0 and check_suite
79
+ result
80
+ end
81
+
82
+ def suite_index
83
+ SUITES.index(self.suite.to_s.downcase)
84
+ end
85
+
86
+ def value_index
87
+ VALUES.index(self.value.to_s.downcase)
88
+ end
89
+ end
90
+ end
data/lib/hands/hand.rb ADDED
@@ -0,0 +1,136 @@
1
+ module Hands
2
+ class Hand
3
+ include Comparable
4
+
5
+ attr_accessor :cards
6
+
7
+ def cards
8
+ @cards ||= []
9
+ end
10
+
11
+ def <=>(other_hand)
12
+ response = (self.hand_index <=> other_hand.hand_index)
13
+
14
+ # If the hands tie, see which is higher (i.e. higher pair)
15
+ if response == 0
16
+ self.cards <=> other_hand.cards
17
+ else
18
+ response
19
+ end
20
+ end
21
+
22
+ def <<(card)
23
+ self.cards << card
24
+ end
25
+
26
+ def suites
27
+ self.cards.collect(&:suite).uniq
28
+ end
29
+
30
+ def best_hand
31
+ response = {}
32
+ HAND_ORDER.reverse.each do |type|
33
+ cards = self.send(type.to_sym)
34
+ next unless cards
35
+ response[:type] = type
36
+ response[:cards] = cards
37
+ break
38
+ end
39
+ response
40
+ end
41
+
42
+ def high_card
43
+ self.cards.sort.reverse
44
+ end
45
+
46
+ def pair
47
+ self.pairs(1)
48
+ end
49
+
50
+ def two_pair
51
+ self.pairs(2)
52
+ end
53
+
54
+ def three_of_a_kind
55
+ nil
56
+ end
57
+
58
+ def straight
59
+ return nil unless self.cards.length == 5
60
+ cs = self.cards.sort.reverse
61
+
62
+ # Ace's low
63
+ if cs.first.value == 'a' and cs[1].value == 5
64
+ # Move ace to end
65
+ ace = cs.first
66
+ cs = cs[1..4]
67
+ cs << ace
68
+
69
+ # Check succession
70
+ csr = cs.reverse
71
+ 4.times do |i|
72
+ next if i == 0
73
+ return nil unless csr[i].value_index == i - 1
74
+ end
75
+
76
+ # Normal
77
+ else
78
+ # Check range
79
+ return nil unless cs.first.value_index - cs.last.value_index == 4
80
+
81
+ # Check succession
82
+ 4.times do |i|
83
+ return nil unless cs[i].value_index == cs[i + 1].value_index + 1
84
+ end
85
+ end
86
+ cs
87
+ end
88
+
89
+ def flush
90
+ # If all of the cards are the same suite, we have a flush
91
+ return nil unless self.suites.length == 1
92
+ self.cards.sort.reverse
93
+ end
94
+
95
+ def full_house
96
+ # TODO: Implement
97
+ nil
98
+ end
99
+
100
+ def four_of_a_kind
101
+ # TODO: Implement
102
+ nil
103
+ end
104
+
105
+ def straight_flush
106
+ # TODO: Implement
107
+ nil
108
+ end
109
+
110
+ protected
111
+
112
+ def hand_index
113
+ best = self.best_hand
114
+ return -1 if best.nil?
115
+ HAND_ORDER.index(best[:type].to_s)
116
+ end
117
+
118
+ def duplicates
119
+ pairs = self.cards.collect(&:value)
120
+ pairs.uniq.select{ |e| (pairs - [e]).size < pairs.size - 1 }
121
+ end
122
+
123
+ def pairs(min)
124
+ dupes = self.duplicates
125
+ return nil if dupes.length < min
126
+
127
+ hand = self.cards.select do |card|
128
+ dupes.include?(card.value)
129
+ end
130
+
131
+ hand = hand.sort.reverse
132
+ hand << (self.cards - hand).sort.reverse
133
+ hand.flatten
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,3 @@
1
+ module Hands
2
+ VERSION = '0.0.1'
3
+ end
data/lib/hands.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'hands/version'
2
+ require 'hands/card'
3
+ require 'hands/hand'
4
+
5
+ module Hands
6
+ SUITES = %w{ clubs diamonds hearts spades }
7
+ VALUES = %w{ 2 3 4 5 6 7 8 9 10 j q k a }
8
+ VALUE_DESCRIPTIONS = %w{ two three four five six seven eight nine ten jack queen king ace }
9
+
10
+ # See http://www.pagat.com/poker/rules/ranking.html
11
+ HAND_ORDER = %w{ high_card pair two_pair three_of_a_kind straight flush full_house four_of_a_kind straight_flush }
12
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hands::Card do
4
+ it 'should validate cards' do
5
+ card = Hands::Card.new
6
+ card.is_valid?.should eq(false)
7
+ card.is_invalid?.should eq(true)
8
+
9
+ card.suite = :hearts
10
+ card.is_valid?.should eq(false)
11
+
12
+ card.value = 9
13
+ card.is_valid?.should eq(true)
14
+
15
+ card.suite = 17
16
+ card.is_valid?.should eq(false)
17
+ end
18
+
19
+ it 'should allow for integers instead of characters for high cards' do
20
+ card1 = Hands::Card.new(:value => 11, :suite => :clubs)
21
+
22
+ card1.is_valid?.should eq(true)
23
+ card1.value.should eq('j')
24
+
25
+ card2 = Hands::Card.new(:value => 'j', :suite => :clubs)
26
+ card2.should eq(card1)
27
+ end
28
+
29
+ it 'should be comparable' do
30
+ card1 = Hands::Card.new(:value => 2, :suite => :hearts)
31
+ card2 = Hands::Card.new(:value => 3, :suite => :clubs)
32
+
33
+ (card2 > card1).should eq(true)
34
+ (card1 < card2).should eq(true)
35
+
36
+ card1.value = 3
37
+ (card1 == card2).should eq(true)
38
+ (card1.<=>(card2, true)).should eq(1)
39
+ (card2.<=>(card1, true)).should eq(-1)
40
+ end
41
+
42
+ it 'should be sortable' do
43
+ c2 = Hands::Card.new(:value => 2, :suite => :hearts)
44
+ c3 = Hands::Card.new(:value => 3, :suite => :hearts)
45
+ c4 = Hands::Card.new(:value => 4, :suite => :hearts)
46
+ c5 = Hands::Card.new(:value => 5, :suite => :hearts)
47
+ c6 = Hands::Card.new(:value => 6, :suite => :hearts)
48
+ c7 = Hands::Card.new(:value => 7, :suite => :hearts)
49
+ c8 = Hands::Card.new(:value => 8, :suite => :hearts)
50
+ c9 = Hands::Card.new(:value => 9, :suite => :hearts)
51
+ c10 = Hands::Card.new(:value => 10, :suite => :hearts)
52
+ cJ = Hands::Card.new(:value => 'j', :suite => :hearts)
53
+ cQ = Hands::Card.new(:value => 'q', :suite => :hearts)
54
+ cK = Hands::Card.new(:value => 'k', :suite => :hearts)
55
+ cA = Hands::Card.new(:value => 'a', :suite => :hearts)
56
+
57
+ cards = [c2, c3, c4, c5, c6, c7, c8, c9, c10, cJ, cQ, cK, cA]
58
+ cards.sort.should eq(cards)
59
+ end
60
+ end
@@ -0,0 +1,147 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hands::Hand do
4
+ it 'should order different types of hands' do
5
+ pair = Hands::Hand.new
6
+ pair << Hands::Card[2, :hearts]
7
+ pair << Hands::Card[2, :clubs]
8
+
9
+ flush = Hands::Hand.new
10
+ flush << Hands::Card[6, :hearts]
11
+ flush << Hands::Card[7, :hearts]
12
+ flush << Hands::Card[8, :hearts]
13
+ flush << Hands::Card[2, :hearts]
14
+ flush << Hands::Card[4, :hearts]
15
+ (flush > pair).should eq(true)
16
+ end
17
+
18
+ it 'should order same types of hands' do
19
+ small_pair = Hands::Hand.new
20
+ small_pair << Hands::Card[2, :hearts]
21
+ small_pair << Hands::Card[2, :clubs]
22
+
23
+ large_pair = Hands::Hand.new
24
+ large_pair << Hands::Card['A', :hearts]
25
+ large_pair << Hands::Card['A', :clubs]
26
+ (large_pair > small_pair).should eq(true)
27
+
28
+ small_kicker = Hands::Hand.new
29
+ small_kicker << Hands::Card['A', :spades]
30
+ small_kicker << Hands::Card['A', :diamonds]
31
+ small_kicker << Hands::Card[2, :diamonds]
32
+ small_kicker << Hands::Card[3, :diamonds]
33
+ small_kicker << Hands::Card[4, :diamonds]
34
+
35
+ big_kicker = Hands::Hand.new
36
+ big_kicker << Hands::Card['A', :hearts]
37
+ big_kicker << Hands::Card['A', :clubs]
38
+ big_kicker << Hands::Card[10, :diamonds]
39
+ big_kicker << Hands::Card[9, :diamonds]
40
+ big_kicker << Hands::Card[7, :diamonds]
41
+ (big_kicker > small_kicker).should eq(true)
42
+ end
43
+
44
+ it 'should collect suites' do
45
+ hand = Hands::Hand.new
46
+ hand << Hands::Card[2, :hearts]
47
+ hand << Hands::Card[2, :clubs]
48
+ hand.suites.should eq([:hearts, :clubs])
49
+
50
+ hand << Hands::Card[3, :clubs]
51
+ hand.suites.should eq([:hearts, :clubs])
52
+ end
53
+
54
+ it 'should recognize high card' do
55
+ hand = Hands::Hand.new
56
+ hand << Hands::Card[2, :hearts]
57
+ hand << Hands::Card[9, :clubs]
58
+ hand << Hands::Card[7, :hearts]
59
+ hand << Hands::Card['a', :spades]
60
+ hand << Hands::Card[4, :diamonds]
61
+ hand.high_card.collect(&:value).should eq(['a', 9, 7, 4, 2])
62
+ hand.pair.should eql(nil)
63
+ hand.two_pair.should eql(nil)
64
+ end
65
+
66
+ it 'should recognize a pair' do
67
+ hand = Hands::Hand.new
68
+ hand << Hands::Card[9, :hearts]
69
+ hand << Hands::Card[9, :clubs]
70
+ hand << Hands::Card[7, :hearts]
71
+ hand << Hands::Card[2, :spades]
72
+ hand << Hands::Card[4, :diamonds]
73
+ hand.pair.collect(&:value).should eq([9, 9, 7, 4, 2])
74
+ hand.two_pair.should eql(nil)
75
+ end
76
+
77
+ it 'should recognize two pair' do
78
+ hand = Hands::Hand.new
79
+ hand << Hands::Card[7, :hearts]
80
+ hand << Hands::Card[7, :spades]
81
+ hand << Hands::Card[4, :diamonds]
82
+ hand << Hands::Card[9, :hearts]
83
+ hand << Hands::Card[9, :clubs]
84
+ hand.two_pair.collect(&:value).should eq([9, 9, 7, 7, 4])
85
+ hand.two_pair.should eql(hand.pair)
86
+ end
87
+
88
+ it 'should recognize three of a kind'
89
+
90
+ it 'should recognize a straight' do
91
+ hand1 = Hands::Hand.new
92
+ hand1 << Hands::Card[2, :hearts]
93
+ hand1 << Hands::Card[3, :spades]
94
+ hand1 << Hands::Card[4, :diamonds]
95
+ hand1 << Hands::Card[5, :hearts]
96
+ hand1 << Hands::Card[6, :clubs]
97
+ hand1.best_hand[:type].should eq('straight')
98
+
99
+ hand2 = Hands::Hand.new
100
+ hand2 << Hands::Card['A', :hearts]
101
+ hand2 << Hands::Card[2, :spades]
102
+ hand2 << Hands::Card[3, :diamonds]
103
+ hand2 << Hands::Card[4, :hearts]
104
+ hand2 << Hands::Card[5, :clubs]
105
+ hand2.best_hand[:type].should eq('straight')
106
+
107
+ hand3 = Hands::Hand.new
108
+ hand3 << Hands::Card[10, :hearts]
109
+ hand3 << Hands::Card['J', :spades]
110
+ hand3 << Hands::Card['Q', :diamonds]
111
+ hand3 << Hands::Card['K', :hearts]
112
+ hand3 << Hands::Card['A', :clubs]
113
+ hand3.best_hand[:type].should eq('straight')
114
+
115
+ hand4 = Hands::Hand.new
116
+ hand4 << Hands::Card['J', :spades]
117
+ hand4 << Hands::Card['Q', :diamonds]
118
+ hand4 << Hands::Card['K', :hearts]
119
+ hand4 << Hands::Card['A', :clubs]
120
+ hand4 << Hands::Card[2, :hearts]
121
+ hand4.best_hand[:type].should eq('high_card')
122
+ end
123
+
124
+ it 'should recognize a flush' do
125
+ hand = Hands::Hand.new
126
+ hand << Hands::Card[6, :hearts]
127
+ hand << Hands::Card[7, :hearts]
128
+ hand << Hands::Card[8, :hearts]
129
+ hand << Hands::Card[2, :hearts]
130
+ hand << Hands::Card[4, :hearts]
131
+ hand.best_hand[:type].should eq('flush')
132
+ end
133
+
134
+ it 'should recognize a full house'
135
+ it 'should recognize four of a kind'
136
+ it 'should recognize a straight flush'
137
+
138
+ it 'should recognize the best hand' do
139
+ hand = Hands::Hand.new
140
+ hand << Hands::Card[7, :hearts]
141
+ hand << Hands::Card[7, :spades]
142
+ hand << Hands::Card[4, :diamonds]
143
+ hand << Hands::Card[9, :hearts]
144
+ hand << Hands::Card[9, :clubs]
145
+ hand.best_hand[:type].should eq('two_pair')
146
+ end
147
+ end
@@ -0,0 +1,2 @@
1
+ require 'rspec'
2
+ require 'hands'
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hands
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sam Soffes
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-23 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Simple library for various poker hands calculations.
15
+ email:
16
+ - sam@samsoff.es
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - LICENSE
24
+ - Rakefile
25
+ - Readme.markdown
26
+ - hands.gemspec
27
+ - lib/hands.rb
28
+ - lib/hands/card.rb
29
+ - lib/hands/hand.rb
30
+ - lib/hands/version.rb
31
+ - spec/models/card_spec.rb
32
+ - spec/models/hand_spec.rb
33
+ - spec/spec_helper.rb
34
+ homepage: http://github.com/samsoffes/hands
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.11
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Simple library for various poker hands calculations.
58
+ test_files:
59
+ - spec/models/card_spec.rb
60
+ - spec/models/hand_spec.rb
61
+ - spec/spec_helper.rb