texas_holdem 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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