texas_holdem 0.1.0 → 0.2.0

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 CHANGED
@@ -17,5 +17,6 @@ tmtags
17
17
  coverage
18
18
  rdoc
19
19
  pkg
20
+ *.gemspec
20
21
 
21
22
  ## PROJECT::SPECIFIC
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = texas_holdem
2
2
 
3
- Description goes here.
3
+ Ruby classes modelling a game Texas Holdem poker
4
4
 
5
5
  == Note on Patches/Pull Requests
6
6
 
data/Rakefile CHANGED
@@ -47,6 +47,7 @@ task :class, :name do |task,args|
47
47
  end}
48
48
 
49
49
  write_to "test/test_#{file_name}.rb", %Q{require 'helper'
50
+
50
51
  class #{class_name}Test < Test::Unit::TestCase
51
52
  def setup
52
53
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
data/lib/deck.rb CHANGED
@@ -17,17 +17,17 @@ class TexasHoldem::Deck
17
17
  def build
18
18
  @cards = []
19
19
 
20
- FACES.each_byte do |f|
21
- SUITS.each_byte {|s| @cards.push(f.chr + s.chr) }
20
+ FACES.each_byte do |face|
21
+ SUITS.each_byte {|suit| @cards.push(face.chr + suit.chr) }
22
22
  end
23
23
  end
24
24
 
25
25
  def shuffle
26
26
  3.times do
27
27
  shuf = []
28
- @cards.each do |c|
28
+ @cards.each do |card|
29
29
  loc = rand(shuf.size + 1)
30
- shuf.insert(loc, c)
30
+ shuf.insert(loc, card)
31
31
  end
32
32
  @cards = shuf.reverse
33
33
  end
data/lib/hand.rb CHANGED
@@ -32,10 +32,10 @@ class TexasHoldem::Hand
32
32
  end
33
33
 
34
34
  def winner
35
- if finished?
36
- @players.first.take_winnings @pot
37
- @players.first
38
- end
35
+ return unless finished?
36
+ winning_player = @players.first
37
+ winning_player.take_winnings @pot
38
+ winning_player
39
39
  end
40
40
 
41
41
  def finished?
@@ -48,7 +48,7 @@ class TexasHoldem::Hand
48
48
 
49
49
  def deal
50
50
  case round
51
- when :pocket : deal_pocket_cards && deduct_blinds # Move to BettingRound?
51
+ when :pocket : deal_pocket_cards && deduct_blinds
52
52
  when :flop : deal_community_cards 3
53
53
  when :turn : deal_community_cards 1
54
54
  when :river : deal_community_cards 1
@@ -0,0 +1,220 @@
1
+ module TexasHoldem
2
+ class PlayerHand
3
+ attr_reader :cards
4
+ include Comparable
5
+
6
+ def self.inherited(subclass)
7
+ (@hand_types || @hand_types = []) << subclass
8
+ end
9
+
10
+ # TODO: investigate Builder / Factory pattern
11
+ def self.create(cards)
12
+ # cards could be both OnePair and a FullHouse, so return the highest raking hand
13
+ @hand_types.map {|hand| hand.create(cards) }.compact.sort_by(&:score).last
14
+ end
15
+
16
+ def initialize(cards)
17
+ @cards = cards
18
+ @cards.gsub!(/J/, '11')
19
+ @cards.gsub!(/Q/, '12')
20
+ @cards.gsub!(/K/, '13')
21
+ @cards.gsub!(/A/, '14')
22
+ @cards = @cards.split.sort_by {|card| card.gsub(/\D/,'').to_i }.join ' '
23
+ end
24
+
25
+ def <=>(players_hand)
26
+ other_players_hand_score = players_hand.score
27
+ this_players_score = score
28
+
29
+ if this_players_score == other_players_hand_score
30
+ remaining_cards <=> players_hand.remaining_cards
31
+ else
32
+ this_players_score <=> other_players_hand_score
33
+ end
34
+ end
35
+
36
+ def score
37
+ base_score * 1000 + relative_score
38
+ end
39
+
40
+ def remaining_cards
41
+ @cards.gsub /\D/, ''
42
+ end
43
+
44
+ private
45
+
46
+ def face_values
47
+ @cards.gsub(/[scdh]/,'')
48
+ end
49
+
50
+ def face_values_array
51
+ face_values.to_a
52
+ end
53
+ end
54
+
55
+ class FourOfAKind < PlayerHand
56
+ Pattern = /(\d{1,2})[scdh] (?:\s\1[scdh]){3}/x
57
+
58
+ def self.create(cards)
59
+ new cards if cards.match Pattern
60
+ end
61
+
62
+ def name
63
+ 'four of a kind'
64
+ end
65
+
66
+ def base_score
67
+ 7
68
+ end
69
+
70
+ def relative_score
71
+ @cards[Pattern,1].to_i
72
+ end
73
+ end
74
+
75
+ class FullHouse < PlayerHand
76
+ Pattern = /^(?:(\d) \1{2} (\d) \2|(\d) \3 (\d) \4{2})/x
77
+
78
+ def self.create(cards)
79
+ new(cards) if cards.gsub(/\D/,'').split(//).sort.to_s.match Pattern
80
+ end
81
+
82
+ def name
83
+ 'full house'
84
+ end
85
+
86
+ def base_score
87
+ 6
88
+ end
89
+
90
+ def relative_score
91
+ @cards[ TexasHoldem::ThreeOfAKind::Pattern, 1 ].to_i
92
+ end
93
+ end
94
+
95
+ class Flush < PlayerHand
96
+ Pattern = /\d{1,2}([csdh]) (?:\s\d{1,2}\1){4} /x
97
+
98
+ def self.create(cards)
99
+ new(cards) if cards.match Pattern
100
+ end
101
+
102
+ def name
103
+ 'flush'
104
+ end
105
+
106
+ def base_score
107
+ 5
108
+ end
109
+
110
+ def relative_score
111
+ face_values_array.sort.last.to_i
112
+ end
113
+ end
114
+
115
+ class Straight < PlayerHand
116
+ def self.create(cards)
117
+ straight = new cards
118
+ card_values = straight.cards.gsub(/[scdh]/,'').split.map {|card_value| card_value.to_i }
119
+ new(cards) if (card_values.first..card_values.last).to_a == card_values
120
+ end
121
+
122
+ def name
123
+ 'straight'
124
+ end
125
+
126
+ def base_score
127
+ 4
128
+ end
129
+
130
+ def relative_score
131
+ face_values_array.last.to_i
132
+ end
133
+ end
134
+
135
+ class ThreeOfAKind < PlayerHand
136
+ Pattern = /(\d{1,2})[scdh] (?:\s\1[scdh]){2}/x
137
+
138
+ def self.create(cards)
139
+ new(cards) if cards.match Pattern
140
+ end
141
+
142
+ def name
143
+ 'three of a kind'
144
+ end
145
+
146
+ def base_score
147
+ 3
148
+ end
149
+
150
+ def relative_score
151
+ @cards[ Pattern, 1 ].to_i
152
+ end
153
+ end
154
+
155
+ class TwoPair < PlayerHand
156
+ Pattern = /(\d{1,2})[scdh] \1[scdh].*(\d{1,2})[scdh] \2[scdh]/
157
+
158
+ def self.create(cards)
159
+ new(cards) if cards.match Pattern
160
+ end
161
+
162
+ def name
163
+ 'two pair'
164
+ end
165
+
166
+ def base_score
167
+ 2
168
+ end
169
+
170
+ def relative_score
171
+ @cards[ Pattern , 2 ].to_i * 2
172
+ end
173
+
174
+ def remaining_cards
175
+ @cards.gsub(Pattern, '').gsub /\D/, ''
176
+ end
177
+ end
178
+
179
+ class OnePair < PlayerHand
180
+ Pattern = /(\d{1,2})[scdh] (?:\s\1[scdh]){1}/x
181
+
182
+ def self.create(cards)
183
+ new(cards) if cards.match Pattern
184
+ end
185
+
186
+ def name
187
+ 'one pair'
188
+ end
189
+
190
+ def base_score
191
+ 1
192
+ end
193
+
194
+ def relative_score
195
+ @cards[ Pattern , 1 ].to_i * 2
196
+ end
197
+
198
+ def remaining_cards
199
+ @cards.gsub(Pattern, '').gsub /\D/, ''
200
+ end
201
+ end
202
+
203
+ class HighCard < PlayerHand
204
+ def self.create(cards)
205
+ new(cards)
206
+ end
207
+
208
+ def name
209
+ 'high card'
210
+ end
211
+
212
+ def base_score
213
+ 0
214
+ end
215
+
216
+ def relative_score
217
+ 0
218
+ end
219
+ end
220
+ end
data/lib/texas_holdem.rb CHANGED
@@ -1,8 +1,4 @@
1
1
  module TexasHoldem
2
- require 'game'
3
- require 'hand'
4
- require 'player'
5
- require 'deck'
6
- require 'betting_round'
7
- require 'players_hand'
2
+ ClassFiles = Dir.entries( File.join( File.dirname(__FILE__), '..', 'lib' ) ).grep(/\.rb$/)
3
+ ClassFiles.each {|klass| require klass.gsub /\.rb/, ''}
8
4
  end
data/test/helper.rb CHANGED
@@ -20,12 +20,13 @@ class String
20
20
  include Test::Unit::Assertions
21
21
 
22
22
  def hand_name(name)
23
- assert_equal name, TexasHoldem::PlayersHand.new(self).name
23
+ hand = TexasHoldem::PlayerHand.create(self)
24
+ assert_equal name, hand.name, "\n#{hand.cards} not recognised as a #{name}\n"
24
25
  end
25
26
 
26
27
  def beats(loser)
27
- winner, loser = TexasHoldem::PlayersHand.new(self), TexasHoldem::PlayersHand.new(loser)
28
- assert( winner > loser )
28
+ winner, loser = TexasHoldem::PlayerHand.create(self), TexasHoldem::PlayerHand.create(loser)
29
+ assert winner > loser, "\n #{winner.name} (#{winner.score}) should beat #{loser.name} (#{loser.score})\n"
29
30
  end
30
31
  end
31
32
 
@@ -2,14 +2,12 @@ require 'helper'
2
2
 
3
3
  class HandTest < Test::Unit::TestCase
4
4
  test "should always have cards sorted in ascending order by face value" do
5
- hand = TexasHoldem::PlayersHand.new('Ad As 5c 6c Jd')
5
+ hand = TexasHoldem::PlayerHand.new('Ad As 5c 6c Jd')
6
6
  assert_equal "5c 6c 11d 14s 14d", hand.cards
7
7
  end
8
8
  end
9
9
 
10
10
  class HandIdentificationTest < Test::Unit::TestCase
11
- # TODO: add more non-sequential matches
12
-
13
11
  test "should recognise a high card" do
14
12
  '2d 3s 5c 6c Ad'.hand_name 'high card'
15
13
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: texas_holdem
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Keith McDonnell
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-29 00:00:00 +01:00
18
+ date: 2010-11-05 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -27,7 +27,6 @@ extensions: []
27
27
 
28
28
  extra_rdoc_files:
29
29
  - LICENSE
30
- - README
31
30
  - README.rdoc
32
31
  files:
33
32
  - .autotest
@@ -35,7 +34,6 @@ files:
35
34
  - .gitignore
36
35
  - Gemfile
37
36
  - LICENSE
38
- - README
39
37
  - README.rdoc
40
38
  - Rakefile
41
39
  - VERSION
@@ -44,7 +42,7 @@ files:
44
42
  - lib/game.rb
45
43
  - lib/hand.rb
46
44
  - lib/player.rb
47
- - lib/players_hand.rb
45
+ - lib/player_hand.rb
48
46
  - lib/texas_holdem.rb
49
47
  - test/helper.rb
50
48
  - test/test_betting_round.rb
@@ -52,7 +50,7 @@ files:
52
50
  - test/test_game.rb
53
51
  - test/test_hand.rb
54
52
  - test/test_player.rb
55
- - test/test_players_hand.rb
53
+ - test/test_player_hand.rb
56
54
  - test/test_texas_holdem.rb
57
55
  has_rdoc: true
58
56
  homepage: http://github.com/kmcd/texas_holdem
@@ -91,9 +89,9 @@ summary: Ruby classes modelling a game Texas Holdem poker
91
89
  test_files:
92
90
  - test/test_game.rb
93
91
  - test/test_player.rb
92
+ - test/test_player_hand.rb
94
93
  - test/test_betting_round.rb
95
94
  - test/test_hand.rb
96
95
  - test/test_deck.rb
97
- - test/test_players_hand.rb
98
96
  - test/helper.rb
99
97
  - test/test_texas_holdem.rb
data/README DELETED
File without changes
data/lib/players_hand.rb DELETED
@@ -1,132 +0,0 @@
1
- # TODO: rename to PlayerHand or Player::Hand
2
- class TexasHoldem::PlayersHand
3
- attr_reader :cards
4
-
5
- include Comparable
6
-
7
- # TODO: dry up [scdh] references
8
- MATCHES = {
9
- 'one pair' => /(\d{1,2})[scdh] (?:\s\1[scdh]){1}/x,
10
- 'two pair' => /(\d{1,2})[scdh] \1[scdh].*(\d{1,2})[scdh] \2[scdh]/,
11
- 'three of a kind' => /(\d{1,2})[scdh] (?:\s\1[scdh]){2}/x,
12
- 'four of a kind' => /(\d{1,2})[scdh] (?:\s\1[scdh]){3}/x,
13
- }
14
-
15
- def initialize(cards)
16
- @cards = cards
17
- @cards.gsub!(/J/, '11')
18
- @cards.gsub!(/Q/, '12')
19
- @cards.gsub!(/K/, '13')
20
- @cards.gsub!(/A/, '14')
21
- @cards = @cards.split.sort_by {|c| c.gsub(/\D/,'').to_i }.join ' '
22
- end
23
-
24
- def <=>(players_hand)
25
- if score == players_hand.score
26
- remaining_cards <=> players_hand.remaining_cards
27
- else
28
- score <=> players_hand.score
29
- end
30
- end
31
-
32
- def name
33
- # TODO: refactor to polymorphic type?
34
- if four_of_a_kind?
35
- 'four of a kind'
36
- elsif full_house?
37
- 'full house'
38
- elsif flush?
39
- 'flush'
40
- elsif straight?
41
- 'straight'
42
- elsif three_of_a_kind?
43
- 'three of a kind'
44
- elsif two_pair?
45
- 'two pair'
46
- elsif one_pair?
47
- 'one pair'
48
- else
49
- 'high card'
50
- end
51
- end
52
-
53
- def one_pair?
54
- @cards.match MATCHES['one pair']
55
- end
56
-
57
- def two_pair?
58
- @cards.match MATCHES['two pair']
59
- end
60
-
61
- def straight?
62
- card_sequence = face_values.split.map {|d| d.to_i }
63
- (card_sequence.first..card_sequence.last).to_a == card_sequence
64
- end
65
-
66
- def flush?
67
- @cards.match /\d{1,2}([csdh]) (?:\s\d{1,2}\1){4} /x
68
- end
69
-
70
- def three_of_a_kind?
71
- @cards.match MATCHES['three of a kind']
72
- end
73
-
74
- def full_house?
75
- # TODO: change this to [csdh]
76
- @cards.gsub(/\D/,'').match /^(?:(\d) \1{2} (\d) \2|(\d) \3 (\d) \4{2})/x
77
- end
78
-
79
- def four_of_a_kind?
80
- @cards.match MATCHES['four of a kind']
81
- end
82
-
83
- protected
84
-
85
- def score
86
- base_score + relative_score
87
- end
88
-
89
- def base_score
90
- case name
91
- when /one pair/ : 1
92
- when /two pair/ : 2
93
- when /three/ : 3
94
- when /straight/ : 4
95
- when /flush/ : 5
96
- when /full house/ : 6
97
- when /four/ : 7
98
- else
99
- 0
100
- end * 1000
101
- end
102
-
103
- def relative_score
104
- case name
105
- when /one pair/ : @cards[MATCHES['one pair'],1].to_i * 2
106
- when /two pair/ : @cards[MATCHES['two pair'],2].to_i * 2
107
- when /three/ : @cards[MATCHES['three of a kind'],1].to_i * 3
108
- when /straight/ : face_values.split.last.to_i
109
- when /flush/ : face_values.split.sort.last.to_i
110
- when /full house/ : @cards[MATCHES['three of a kind'],1].to_i
111
- when /four/ : @cards[MATCHES['four of a kind'],1].to_i
112
- else
113
- 0
114
- end
115
- end
116
-
117
- def remaining_cards
118
- case name
119
- when /high card/ : @cards
120
- when /one pair/ : @cards.gsub(MATCHES['one pair'], '')
121
- when /two pair/ : @cards.gsub(MATCHES['two pair'], '')
122
- else
123
- ''
124
- end.gsub /[a-z]/, ''
125
- end
126
-
127
- private
128
-
129
- def face_values
130
- @cards.gsub(/[scdh]/,'')
131
- end
132
- end