acpc_poker_types 5.0.3 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/acpc_poker_types.gemspec +3 -2
  4. data/lib/acpc_poker_types.rb +1 -1
  5. data/lib/acpc_poker_types/acpc_dealer_data/hand_results.rb +5 -5
  6. data/lib/acpc_poker_types/acpc_dealer_data/poker_match_data.rb +290 -323
  7. data/lib/acpc_poker_types/game_definition.rb +54 -37
  8. data/lib/acpc_poker_types/hand_player.rb +119 -0
  9. data/lib/acpc_poker_types/match_state.rb +323 -204
  10. data/lib/acpc_poker_types/player_group.rb +62 -0
  11. data/lib/acpc_poker_types/poker_action.rb +13 -6
  12. data/lib/acpc_poker_types/version.rb +1 -1
  13. data/spec/game_definition_spec.rb +23 -4
  14. data/spec/hand_player_spec.rb +254 -0
  15. data/spec/match_state_spec.rb +655 -112
  16. data/spec/player_group_spec.rb +57 -0
  17. data/spec/poker_match_data_spec.rb +24 -21
  18. data/spec/support/spec_helper.rb +2 -16
  19. metadata +68 -148
  20. data/lib/acpc_poker_types/player.rb +0 -182
  21. data/spec/coverage/assets/0.7.1/application.css +0 -1110
  22. data/spec/coverage/assets/0.7.1/application.js +0 -626
  23. data/spec/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
  24. data/spec/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
  25. data/spec/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
  26. data/spec/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
  27. data/spec/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
  28. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
  29. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
  30. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
  31. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
  32. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
  33. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
  34. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
  35. data/spec/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
  36. data/spec/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
  37. data/spec/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
  38. data/spec/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
  39. data/spec/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
  40. data/spec/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
  41. data/spec/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
  42. data/spec/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
  43. data/spec/coverage/assets/0.7.1/favicon_green.png +0 -0
  44. data/spec/coverage/assets/0.7.1/favicon_red.png +0 -0
  45. data/spec/coverage/assets/0.7.1/favicon_yellow.png +0 -0
  46. data/spec/coverage/assets/0.7.1/loading.gif +0 -0
  47. data/spec/coverage/assets/0.7.1/magnify.png +0 -0
  48. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  49. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  50. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  51. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  52. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  53. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  54. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  55. data/spec/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  56. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
  57. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  58. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
  59. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
  60. data/spec/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  61. data/spec/coverage/index.html +0 -72
  62. data/spec/match_state_perf.rb +0 -14
  63. data/spec/player_spec.rb +0 -372
@@ -0,0 +1,62 @@
1
+ require 'delegate'
2
+
3
+ require 'acpc_poker_types/poker_action'
4
+ require 'acpc_poker_types/hand_player'
5
+
6
+ # Model to parse and manage information from a given match state string.
7
+ module AcpcPokerTypes
8
+
9
+ class PlayerGroup < DelegateClass(Array)
10
+ attr_reader :players
11
+
12
+ def initialize(all_hands, stacks, blinds)
13
+ @players = all_hands.length.times.map do |i|
14
+ HandPlayer.new all_hands[i], stacks[i], blinds[i]
15
+ end
16
+
17
+ super @players
18
+ end
19
+
20
+ def next_player_position(acting_player_position=-1)
21
+ (acting_player_position + 1) % length
22
+ end
23
+
24
+ def position_of_first_active_player(acting_player_position=0)
25
+ raise NoPlayerCouldHaveActed if all? { |player| player.inactive? }
26
+
27
+ # This must eventually exit because of the above assertion
28
+ while @players[acting_player_position].inactive?
29
+ acting_player_position = next_player_position(acting_player_position)
30
+ end
31
+ acting_player_position
32
+ end
33
+
34
+ def action_cost(acting_player_position, action, min_wager)
35
+ case action.to_s[0]
36
+ when PokerAction::CALL
37
+ amount_to_call acting_player_position
38
+ when PokerAction::RAISE
39
+ if action.modifier
40
+ action.modifier.to_i - players[acting_player_position].total_contribution
41
+ else
42
+ min_wager + amount_to_call(acting_player_position)
43
+ end
44
+ else
45
+ 0
46
+ end
47
+ end
48
+
49
+ def amount_to_call(acting_player_position)
50
+ ChipStack.new(
51
+ [
52
+ (
53
+ map do |player|
54
+ player.total_contribution
55
+ end
56
+ ).max - players[acting_player_position].total_contribution,
57
+ @players[acting_player_position].stack
58
+ ].min
59
+ )
60
+ end
61
+ end
62
+ end
@@ -64,16 +64,20 @@ module AcpcPokerTypes
64
64
  attr_reader :pot_gained_chips
65
65
 
66
66
  # @param [Symbol, String] action A representation of this action.
67
- # @param [Hash] context The context in which this action is being made. Recognized keys include +:modifier+,
68
- # +:pot_gained_chips+, and +:cost+.
67
+ # @param modifier [String] A modifier to attach to this action such as a wager size.
68
+ # @param cost [#to_f] The amount this action costs to the acting player.
69
69
  # @raise IllegalAction
70
- def initialize(action, modifier: nil, cost: AcpcPokerTypes::ChipStack.new(0))
70
+ def initialize(action, modifier: nil, cost: 0)
71
71
  validate_action!(action, modifier.strip)
72
- @cost = cost
72
+ @cost = cost.to_f
73
73
  end
74
74
 
75
75
  def ==(other_action)
76
- to_s == other_action.to_s
76
+ 0 == (self <=> other_action)
77
+ end
78
+
79
+ def <=>(other_action)
80
+ to_s <=> other_action.to_s
77
81
  end
78
82
 
79
83
  # @return [String] String representation of this action.
@@ -93,6 +97,7 @@ module AcpcPokerTypes
93
97
  end
94
98
 
95
99
  alias_method :to_acpc, :to_s
100
+ alias_method :to_str, :to_s
96
101
 
97
102
  # @return [Boolean] +true+ if this action has a modifier, +false+ otherwise.
98
103
  def has_modifier?
@@ -131,7 +136,9 @@ module AcpcPokerTypes
131
136
  end
132
137
 
133
138
  def validate_modifier
134
- raise(IllegalModification, "Illegal modifier: #{@modifier}") unless @modifier.blank? || AcpcPokerTypes::PokerAction::MODIFIABLE_ACTIONS.include?(@action)
139
+ unless @modifier.blank? || MODIFIABLE_ACTIONS.include?(@action)
140
+ raise(IllegalModification, "Illegal modifier: #{@modifier}")
141
+ end
135
142
  end
136
143
  end
137
144
  end
@@ -1,3 +1,3 @@
1
1
  module AcpcPokerTypes
2
- VERSION = '5.0.3'
2
+ VERSION = '6.0.0'
3
3
  end
@@ -1,16 +1,35 @@
1
-
2
1
  # Spec helper (must include first to track code coverage with SimpleCov)
3
2
  require_relative 'support/spec_helper'
4
3
 
5
4
  require 'acpc_dealer'
6
-
7
5
  require 'acpc_poker_types/game_definition'
8
-
9
6
  require 'acpc_poker_types/chip_stack'
10
7
 
11
- describe AcpcPokerTypes::GameDefinition do
8
+ include AcpcPokerTypes
9
+
10
+ describe GameDefinition do
12
11
  include AcpcDealer
13
12
 
13
+ describe '::new' do
14
+ it 'creates a new game definition from a hash' do
15
+ x_hash = {
16
+ betting_type: 'limit',
17
+ chip_stacks: [10] * 3,
18
+ number_of_players: 3,
19
+ blinds: [2] * 3,
20
+ raise_sizes: [2] * 3,
21
+ number_of_rounds: 4,
22
+ first_player_positions: [1] * 4,
23
+ max_number_of_wagers: [3] * 4,
24
+ number_of_suits: 4,
25
+ number_of_ranks: 13,
26
+ number_of_hole_cards: 2,
27
+ number_of_board_cards: [2] * 4
28
+ }
29
+ GameDefinition.new(x_hash).to_h.must_equal(x_hash)
30
+ end
31
+ end
32
+
14
33
  describe '::default_first_player_positions' do
15
34
  it 'works' do
16
35
  100.times do |number_of_rounds|
@@ -0,0 +1,254 @@
1
+
2
+ # Spec helper (must include first to track code coverage with SimpleCov)
3
+ require_relative 'support/spec_helper'
4
+
5
+ require 'acpc_dealer'
6
+ require 'acpc_poker_types/acpc_dealer_data/poker_match_data'
7
+
8
+ require 'acpc_poker_types/hand_player'
9
+ require 'acpc_poker_types/poker_action'
10
+ require 'acpc_poker_types/hand'
11
+ require 'acpc_poker_types/match_state'
12
+
13
+ include AcpcPokerTypes
14
+
15
+ describe HandPlayer do
16
+ INITIAL_CHIP_STACK = ChipStack.new 100000
17
+ ANTE = 100
18
+ HAND = Hand.from_acpc('AhKs')
19
+
20
+ def patient
21
+ @patient ||= HandPlayer.new HAND, INITIAL_CHIP_STACK, ANTE
22
+ end
23
+
24
+ describe '::new' do
25
+ it 'raises an exception if the player is unable to pay the ante' do
26
+ -> { HandPlayer.new HAND, INITIAL_CHIP_STACK, INITIAL_CHIP_STACK + 1 }.must_raise HandPlayer::UnableToPayAnte
27
+ end
28
+ it 'works' do
29
+ [0, 100].each do |ante|
30
+ @patient = HandPlayer.new HAND, INITIAL_CHIP_STACK, ante
31
+
32
+ @patient.hand.must_equal HAND
33
+ @patient.stack.must_equal INITIAL_CHIP_STACK - ante
34
+ @patient.ante.must_equal ante
35
+ @patient.initial_stack.must_equal INITIAL_CHIP_STACK
36
+ end
37
+ end
38
+ end
39
+ describe '#append_action!' do
40
+ describe 'raises an exception if it is not active' do
41
+ it 'if it has folded' do
42
+ x_actions = [['c', 'r100'], ['r200', 'c'], ['f']]
43
+ x_actions.each_with_index do |actions, round|
44
+ actions.each do |action|
45
+ patient.append_action! PokerAction.new(action), round
46
+ end
47
+ end
48
+
49
+ -> { patient.append_action!(PokerAction.new(PokerAction::CALL), x_actions.length - 1) }.must_raise HandPlayer::Inactive
50
+ end
51
+ it 'if it has gone all in' do
52
+ x_actions = [
53
+ [
54
+ PokerAction.new('c'),
55
+ PokerAction.new('r200')
56
+ ],
57
+ [
58
+ PokerAction.new('r400'),
59
+ PokerAction.new('c', cost: INITIAL_CHIP_STACK - ANTE)
60
+ ]
61
+ ]
62
+ x_actions.each_with_index do |actions, round|
63
+ actions.each do |action|
64
+ patient.append_action!(action, round)
65
+ end
66
+ end
67
+
68
+ -> { patient.append_action!(PokerAction.new(PokerAction::CALL), x_actions.length - 1) }.must_raise HandPlayer::Inactive
69
+ end
70
+ end
71
+ it 'works' do
72
+ x_actions = [['c', 'r100'], ['r200', 'c'], ['f']]
73
+ x_actions.each_with_index do |actions, round|
74
+ actions.each do |action|
75
+ patient.append_action! PokerAction.new(action), round
76
+ end
77
+ end
78
+
79
+ patient.actions.must_equal x_actions
80
+ end
81
+ end
82
+ describe '#folded?' do
83
+ it 'works' do
84
+ x_actions = [['c', 'r100'], ['r200', 'c']]
85
+ x_actions.each_with_index do |actions, round|
86
+ actions.each do |action|
87
+ patient.append_action!(PokerAction.new(action), round).folded?.must_equal false
88
+ end
89
+ end
90
+
91
+ patient.append_action!(PokerAction.new(PokerAction::FOLD), x_actions.length - 1).folded?.must_equal true
92
+ end
93
+ end
94
+ describe '#all_in?' do
95
+ it 'works' do
96
+ x_actions = [
97
+ [
98
+ PokerAction.new('c'),
99
+ PokerAction.new('r200')
100
+ ],
101
+ [
102
+ PokerAction.new('r400')
103
+ ]
104
+ ]
105
+ x_actions.each_with_index do |actions, round|
106
+ actions.each do |action|
107
+ patient.append_action!(action, round).all_in?.must_equal false
108
+ end
109
+ end
110
+
111
+ patient.append_action!(PokerAction.new('c', cost: INITIAL_CHIP_STACK)).all_in?.must_equal true
112
+ end
113
+ end
114
+ describe '#inactive?' do
115
+ it 'is true if it has gone all in' do
116
+ x_actions = [
117
+ [
118
+ PokerAction.new('c'),
119
+ PokerAction.new('r200')
120
+ ],
121
+ [
122
+ PokerAction.new('r400')
123
+ ]
124
+ ]
125
+ x_actions.each_with_index do |actions, round|
126
+ actions.each do |action|
127
+ patient.append_action!(action, round).inactive?.must_equal false
128
+ end
129
+ end
130
+
131
+ patient.append_action!(PokerAction.new('c', cost: INITIAL_CHIP_STACK - ANTE)).inactive?.must_equal true
132
+ end
133
+ it 'is true if it has folded' do
134
+ x_actions = [
135
+ [
136
+ PokerAction.new('c'),
137
+ PokerAction.new('r200')
138
+ ],
139
+ [
140
+ PokerAction.new('r400')
141
+ ]
142
+ ]
143
+ x_actions.each_with_index do |actions, round|
144
+ actions.each do |action|
145
+ patient.append_action!(action, round).inactive?.must_equal false
146
+ end
147
+ end
148
+
149
+ patient.append_action!(PokerAction.new(PokerAction::FOLD)).inactive?.must_equal true
150
+ end
151
+ end
152
+ describe '#contributions' do
153
+ it 'works' do
154
+ x_actions = [
155
+ [
156
+ PokerAction.new('c', cost: 100),
157
+ PokerAction.new('r200', cost: 100)
158
+ ],
159
+ [
160
+ PokerAction.new('r400', cost: 200),
161
+ PokerAction.new('c', cost: 0)
162
+ ]
163
+ ]
164
+ x_actions.each_with_index do |actions, round|
165
+ actions.each do |action|
166
+ patient.append_action!(action, round)
167
+ end
168
+ end
169
+
170
+ patient.contributions.must_equal(
171
+ x_actions.map do |actions|
172
+ actions.inject(0) { |sum, action| sum += action.cost }
173
+ end.unshift(ANTE)
174
+ )
175
+ end
176
+ end
177
+ describe '#total_contribution' do
178
+ it 'works' do
179
+ x_actions = [
180
+ [
181
+ PokerAction.new('c', cost: 100),
182
+ PokerAction.new('r200', cost: 100)
183
+ ],
184
+ [
185
+ PokerAction.new('r400', cost: 200),
186
+ PokerAction.new('c', cost: 0)
187
+ ]
188
+ ]
189
+ x_actions.each_with_index do |actions, round|
190
+ actions.each do |action|
191
+ patient.append_action!(action, round)
192
+ end
193
+ end
194
+
195
+ patient.total_contribution.must_equal(
196
+ ANTE + x_actions.flatten.inject(0) { |sum, action| sum += action.cost }
197
+ )
198
+ end
199
+ end
200
+
201
+ describe '#legal_actions' do
202
+ it 'works with fold' do
203
+ patient.legal_actions(0, amount_to_call: 100).sort.must_equal [
204
+ PokerAction.new(PokerAction::CALL),
205
+ PokerAction.new(PokerAction::RAISE, cost: INITIAL_CHIP_STACK - (ANTE + 100)),
206
+ PokerAction.new(PokerAction::FOLD)
207
+ ].sort
208
+ patient.append_action!(PokerAction.new('c', cost: 100))
209
+ .legal_actions(0, amount_to_call: 200).sort.must_equal [
210
+ PokerAction.new(PokerAction::CALL),
211
+ PokerAction.new(PokerAction::RAISE, cost: INITIAL_CHIP_STACK - (ANTE + 200)),
212
+ PokerAction.new(PokerAction::FOLD)
213
+ ].sort
214
+ patient.append_action!(PokerAction.new('f'))
215
+ .legal_actions(0).empty?.must_equal true
216
+ end
217
+ it 'works with all in' do
218
+ patient.legal_actions(0).sort.must_equal [
219
+ PokerAction.new(PokerAction::CHECK),
220
+ PokerAction.new(PokerAction::BET, cost: INITIAL_CHIP_STACK - ANTE),
221
+ ].sort
222
+ patient.append_action!(PokerAction.new('c', cost: 100))
223
+ .legal_actions(0).sort.must_equal [
224
+ PokerAction.new(PokerAction::CHECK),
225
+ PokerAction.new(PokerAction::BET, cost: INITIAL_CHIP_STACK - (ANTE + 100)),
226
+ ].sort
227
+ patient.append_action!(PokerAction.new('c', cost: INITIAL_CHIP_STACK - (ANTE + 100)))
228
+ .legal_actions(0).empty?.must_equal true
229
+ end
230
+ it 'works when an opponent is all in' do
231
+ patient.append_action!(PokerAction.new('c', cost: 100))
232
+ .legal_actions(0).sort.must_equal [
233
+ PokerAction.new(PokerAction::CHECK),
234
+ PokerAction.new(PokerAction::RAISE, cost: INITIAL_CHIP_STACK - (ANTE + 100))
235
+ ].sort
236
+ patient.legal_actions(0, amount_to_call: INITIAL_CHIP_STACK - (ANTE + 100))
237
+ .sort.must_equal [
238
+ PokerAction.new(PokerAction::CALL),
239
+ PokerAction.new(PokerAction::FOLD)
240
+ ].sort
241
+ end
242
+ it 'works when no more raises are allowed' do
243
+ patient.append_action!(PokerAction.new('c', cost: 100))
244
+ .legal_actions(0, wager_illegal: true).sort.must_equal [
245
+ PokerAction.new(PokerAction::CHECK)
246
+ ].sort
247
+ patient.legal_actions(0, amount_to_call: INITIAL_CHIP_STACK - (ANTE + 100))
248
+ .sort.must_equal [
249
+ PokerAction.new(PokerAction::CALL),
250
+ PokerAction.new(PokerAction::FOLD)
251
+ ].sort
252
+ end
253
+ end
254
+ end
@@ -1,53 +1,44 @@
1
-
2
- # Spec helper (must include first to track code coverage with SimpleCov)
3
- require File.expand_path('../support/spec_helper', __FILE__)
1
+ require_relative 'support/spec_helper'
4
2
 
5
3
  require 'acpc_dealer'
6
4
 
7
- require "acpc_poker_types/match_state"
8
- require "acpc_poker_types/poker_action"
9
- require "acpc_poker_types/rank"
10
- require "acpc_poker_types/suit"
11
- require "acpc_poker_types/hand"
12
- require "acpc_poker_types/card"
13
- require 'acpc_poker_types/acpc_dealer_data/poker_match_data'
14
-
15
- describe AcpcPokerTypes::MatchState do
16
- describe '#parse' do
17
- describe 'raises an exception if ' do
18
- describe 'the raw matchstate string' do
19
- it 'is empty' do
20
- test_match_state_initialization_error ""
21
- end
22
-
23
- it 'is not in the proper format' do
24
- test_match_state_initialization_error "hello world"
25
- end
26
-
27
- it 'does not contain a position' do
28
- test_match_state_initialization_error AcpcPokerTypes::MatchState::LABEL + "::0::AhKd"
29
- end
30
-
31
- it 'does not contain a hand number' do
32
- test_match_state_initialization_error AcpcPokerTypes::MatchState::LABEL + ":0:::AsKc"
33
- end
34
-
35
- it 'does not contain cards' do
36
- test_match_state_initialization_error AcpcPokerTypes::MatchState::LABEL + ":0:0::"
37
- end
5
+ require_relative "../lib/acpc_poker_types/match_state"
6
+ require_relative "../lib/acpc_poker_types/poker_action"
7
+ require_relative "../lib/acpc_poker_types/rank"
8
+ require_relative "../lib/acpc_poker_types/suit"
9
+ require_relative "../lib/acpc_poker_types/hand"
10
+ require_relative "../lib/acpc_poker_types/card"
11
+ require_relative "../lib/acpc_poker_types/game_definition"
12
+
13
+ module MapWithIndex
14
+ refine Array do
15
+ def map_with_index
16
+ i = 0
17
+ map do |elem|
18
+ result = yield elem, i
19
+ i += 1
20
+ result
38
21
  end
39
22
  end
23
+ end
24
+ end
25
+ using MapWithIndex
26
+
27
+ include AcpcPokerTypes
28
+
29
+ describe MatchState do
30
+ describe '#parse' do
40
31
  it "parses every possible limit action" do
41
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":1:1:"
32
+ partial_match_state = MatchState::LABEL + ":1:1:"
42
33
  hole_cards = arbitrary_hole_card_hand
43
- AcpcPokerTypes::PokerAction::CANONICAL_ACTIONS.each do |action|
34
+ PokerAction::CANONICAL_ACTIONS.each do |action|
44
35
  match_state = partial_match_state + action + ":" + hole_cards.to_acpc
45
36
  patient = test_match_state_success match_state
46
37
  patient.last_action.to_s.must_equal action
47
38
  end
48
39
  end
49
40
  it "parses every possible hole card hand" do
50
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":2:2::"
41
+ partial_match_state = MatchState::LABEL + ":2:2::"
51
42
  for_every_hand do |hand|
52
43
  match_state = partial_match_state + hand.to_acpc + '|'
53
44
 
@@ -55,85 +46,85 @@ describe AcpcPokerTypes::MatchState do
55
46
  end
56
47
  end
57
48
  it "parses opponent hole card hands in a two player game where the user is not the dealer" do
58
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":0:2::"
49
+ partial_match_state = MatchState::LABEL + ":0:2::"
59
50
  for_every_hand do |hand|
60
51
  match_state = partial_match_state + arbitrary_hole_card_hand.to_acpc + '|' + hand.to_acpc
61
52
 
62
53
  patient = test_match_state_success match_state
63
54
 
64
- (patient.list_of_opponents_hole_cards.map do |opponent_hand|
55
+ (patient.opponent_hands.map do |opponent_hand|
65
56
  opponent_hand.to_acpc
66
57
  end).must_equal [hand.to_acpc]
67
58
  end
68
59
  end
69
60
  it "parses opponent hole card hands in a two player game where the user is the dealer" do
70
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":1:2::"
61
+ partial_match_state = MatchState::LABEL + ":1:2::"
71
62
  for_every_hand do |hand|
72
63
  match_state = partial_match_state + hand.to_acpc + '|' + arbitrary_hole_card_hand.to_acpc
73
64
 
74
65
  patient = test_match_state_success match_state
75
66
 
76
- (patient.list_of_opponents_hole_cards.map do |opponent_hand|
67
+ (patient.opponent_hands.map do |opponent_hand|
77
68
  opponent_hand.to_acpc
78
69
  end).must_equal [hand.to_acpc]
79
70
  end
80
71
  end
81
72
  it 'parses board cards properly for the flop' do
82
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":2:2::" + arbitrary_hole_card_hand.to_acpc
73
+ partial_match_state = MatchState::LABEL + ":2:2::" + arbitrary_hole_card_hand.to_acpc
83
74
 
84
- board_cards = '/AhKdQc'
85
- flop_match_state = partial_match_state + board_cards
75
+ community_cards = '/AhKdQc'
76
+ flop_match_state = partial_match_state + community_cards
86
77
 
87
- patient = AcpcPokerTypes::MatchState.parse flop_match_state
78
+ patient = MatchState.parse flop_match_state
88
79
 
89
- patient.board_cards.to_acpc.must_equal board_cards
80
+ patient.community_cards.to_acpc.must_equal community_cards
90
81
  end
91
82
  it 'parses board cards properly for the turn' do
92
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":2:2::" + arbitrary_hole_card_hand.to_acpc
93
- board_cards = '/AhKdQc/Jd'
94
- turn_match_state = partial_match_state + board_cards
83
+ partial_match_state = MatchState::LABEL + ":2:2::" + arbitrary_hole_card_hand.to_acpc
84
+ community_cards = '/AhKdQc/Jd'
85
+ turn_match_state = partial_match_state + community_cards
95
86
 
96
- patient = AcpcPokerTypes::MatchState.parse turn_match_state
87
+ patient = MatchState.parse turn_match_state
97
88
 
98
- patient.board_cards.to_acpc.must_equal board_cards
89
+ patient.community_cards.to_acpc.must_equal community_cards
99
90
  end
100
91
  it 'parses board cards properly for the river' do
101
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":2:2::" + arbitrary_hole_card_hand.to_acpc
102
- board_cards = '/AhKdQc/Jd/Th'
103
- river_match_state = partial_match_state + board_cards
92
+ partial_match_state = MatchState::LABEL + ":2:2::" + arbitrary_hole_card_hand.to_acpc
93
+ community_cards = '/AhKdQc/Jd/Th'
94
+ river_match_state = partial_match_state + community_cards
104
95
 
105
- patient = AcpcPokerTypes::MatchState.parse river_match_state
96
+ patient = MatchState.parse river_match_state
106
97
 
107
- patient.board_cards.to_acpc.must_equal board_cards
98
+ patient.community_cards.to_acpc.must_equal community_cards
108
99
  end
109
100
  it "parses a valid two player final match state" do
110
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":20:22:"
111
- all_actions = AcpcPokerTypes::PokerAction::CANONICAL_ACTIONS.to_a.join ''
101
+ partial_match_state = MatchState::LABEL + ":20:22:"
102
+ all_actions = PokerAction::CANONICAL_ACTIONS.to_a.join ''
112
103
  betting = all_actions
113
104
  number_of_rounds = 100
114
105
  (number_of_rounds-1).times do
115
106
  betting += "/#{all_actions}"
116
107
  end
117
- board_cards = arbitrary_roll_out number_of_rounds
108
+ community_cards = arbitrary_roll_out number_of_rounds
118
109
  hands = arbitrary_hole_card_hand.to_acpc + "|" + arbitrary_hole_card_hand.to_acpc
119
110
 
120
- match_state = partial_match_state + betting + ":" + hands + board_cards
111
+ match_state = partial_match_state + betting + ":" + hands + community_cards
121
112
 
122
113
  test_match_state_success match_state
123
114
  end
124
115
  it "parses a valid three player final match state" do
125
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":20:22:"
126
- all_actions = AcpcPokerTypes::PokerAction::CANONICAL_ACTIONS.to_a.join ''
116
+ partial_match_state = MatchState::LABEL + ":20:22:"
117
+ all_actions = PokerAction::CANONICAL_ACTIONS.to_a.join ''
127
118
  betting = all_actions
128
119
  number_of_rounds = 100
129
120
  (number_of_rounds-1).times do
130
121
  betting += "/#{all_actions}"
131
122
  end
132
- board_cards = arbitrary_roll_out number_of_rounds
123
+ community_cards = arbitrary_roll_out number_of_rounds
133
124
  hands = arbitrary_hole_card_hand.to_s + "|" +
134
125
  arbitrary_hole_card_hand.to_s + "|" + arbitrary_hole_card_hand.to_s
135
126
 
136
- match_state = partial_match_state + betting + ":" + hands + board_cards
127
+ match_state = partial_match_state + betting + ":" + hands + community_cards
137
128
 
138
129
  test_match_state_success match_state
139
130
  end
@@ -141,7 +132,7 @@ describe AcpcPokerTypes::MatchState do
141
132
 
142
133
  describe '#round' do
143
134
  it "properly reports the current round number" do
144
- partial_match_state = AcpcPokerTypes::MatchState::LABEL + ":0:0:"
135
+ partial_match_state = MatchState::LABEL + ":0:0:"
145
136
  betting = ""
146
137
  hand = arbitrary_hole_card_hand
147
138
  100.times do |i|
@@ -166,7 +157,7 @@ describe AcpcPokerTypes::MatchState do
166
157
  end
167
158
  hands.push arbitrary_hole_card_hand.to_acpc
168
159
  end
169
- match_state = AcpcPokerTypes::MatchState::LABEL + ':1:1::' + hands.join('|')
160
+ match_state = MatchState::LABEL + ':1:1::' + hands.join('|')
170
161
 
171
162
  patient = test_match_state_success match_state
172
163
  patient.number_of_players.must_equal expected_number_of_players
@@ -175,39 +166,39 @@ describe AcpcPokerTypes::MatchState do
175
166
 
176
167
  describe '#last_action, #round_in_which_last_action_taken, and #first_state_of_first_round' do
177
168
  it 'returns +nil+ if no previous action exists, or true in the case of #first_state_of_first_round' do
178
- initial_match_state = "#{AcpcPokerTypes::MatchState::LABEL}:1:1::#{arbitrary_hole_card_hand}"
179
- patient = AcpcPokerTypes::MatchState.parse initial_match_state
169
+ initial_match_state = "#{MatchState::LABEL}:1:1::#{arbitrary_hole_card_hand}"
170
+ patient = MatchState.parse initial_match_state
180
171
  patient.last_action.must_be_nil
181
172
  patient.round_in_which_last_action_taken.must_be_nil
182
173
  patient.first_state_of_first_round?.must_equal true
183
174
  end
184
175
  it 'works properly if a previous action exists' do
185
- AcpcPokerTypes::PokerAction::CANONICAL_ACTIONS.each do |first_action|
186
- AcpcPokerTypes::PokerAction::CANONICAL_ACTIONS.each do |second_action|
187
- AcpcPokerTypes::PokerAction::CANONICAL_ACTIONS.each do |third_action|
188
- partial_match_state = "#{AcpcPokerTypes::MatchState::LABEL}:1:1:"
176
+ PokerAction::CANONICAL_ACTIONS.each do |first_action|
177
+ PokerAction::CANONICAL_ACTIONS.each do |second_action|
178
+ PokerAction::CANONICAL_ACTIONS.each do |third_action|
179
+ partial_match_state = "#{MatchState::LABEL}:1:1:"
189
180
 
190
181
  partial_match_state += first_action
191
182
  match_state = "#{partial_match_state}:#{arbitrary_hole_card_hand}"
192
183
 
193
- patient = AcpcPokerTypes::MatchState.parse match_state
194
- patient.last_action.must_equal AcpcPokerTypes::PokerAction.new(first_action)
184
+ patient = MatchState.parse match_state
185
+ patient.last_action.must_equal PokerAction.new(first_action)
195
186
  patient.round_in_which_last_action_taken.must_equal 0
196
187
  patient.first_state_of_first_round?.must_equal false
197
188
 
198
189
  partial_match_state += "#{second_action}/"
199
190
  match_state = "#{partial_match_state}:#{arbitrary_hole_card_hand}"
200
191
 
201
- patient = AcpcPokerTypes::MatchState.parse match_state
202
- patient.last_action.must_equal AcpcPokerTypes::PokerAction.new(second_action)
192
+ patient = MatchState.parse match_state
193
+ patient.last_action.must_equal PokerAction.new(second_action)
203
194
  patient.round_in_which_last_action_taken.must_equal 0
204
195
  patient.first_state_of_first_round?.must_equal false
205
196
 
206
197
  partial_match_state += third_action
207
198
  match_state = "#{partial_match_state}:#{arbitrary_hole_card_hand}"
208
199
 
209
- patient = AcpcPokerTypes::MatchState.parse match_state
210
- patient.last_action.must_equal AcpcPokerTypes::PokerAction.new(third_action)
200
+ patient = MatchState.parse match_state
201
+ patient.last_action.must_equal PokerAction.new(third_action)
211
202
  patient.round_in_which_last_action_taken.must_equal 1
212
203
  patient.first_state_of_first_round?.must_equal false
213
204
  end
@@ -215,84 +206,636 @@ describe AcpcPokerTypes::MatchState do
215
206
  end
216
207
  end
217
208
  end
218
- end
219
- describe "#receive_matchstate_string" do
220
- it 'receives matchstate strings properly' do
221
- @connection = mock 'Socket'
222
- MatchLog.all.each do |log_description|
223
- match = AcpcPokerTypes::AcpcDealerData::PokerMatchData.parse_files(
224
- log_description.actions_file_path,
225
- log_description.results_file_path,
226
- log_description.player_names,
227
- AcpcDealer::DEALER_DIRECTORY,
228
- 20
209
+ describe "#receive_matchstate_string" do
210
+ it 'receives matchstate strings properly' do
211
+ @connection = mock 'Socket'
212
+ MatchLog.all.each do |log_description|
213
+ match = AcpcDealerData::PokerMatchData.parse_files(
214
+ log_description.actions_file_path,
215
+ log_description.results_file_path,
216
+ log_description.player_names,
217
+ AcpcDealer::DEALER_DIRECTORY,
218
+ 20
219
+ )
220
+ match.for_every_seat! do |seat|
221
+ match.for_every_hand! do
222
+ match.for_every_turn! do
223
+ @connection.expects(:gets).returns(match.current_hand.current_match_state.to_s)
224
+
225
+ MatchState.receive(@connection).must_equal match.current_hand.current_match_state
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+ describe '#betting_sequence' do
233
+ it 'works' do
234
+ MatchState.parse(
235
+ "#{MatchState::LABEL}:0:0:crcc/ccc/rrfc:AhKs|"
236
+ ).betting_sequence.must_equal [
237
+ [
238
+ PokerAction.new(PokerAction::CALL),
239
+ PokerAction.new(PokerAction::RAISE),
240
+ PokerAction.new(PokerAction::CALL),
241
+ PokerAction.new(PokerAction::CALL)
242
+ ],
243
+ [
244
+ PokerAction.new(PokerAction::CALL),
245
+ PokerAction.new(PokerAction::CALL),
246
+ PokerAction.new(PokerAction::CALL)
247
+ ],
248
+ [
249
+ PokerAction.new(PokerAction::RAISE),
250
+ PokerAction.new(PokerAction::RAISE),
251
+ PokerAction.new(PokerAction::FOLD),
252
+ PokerAction.new(PokerAction::CALL)
253
+ ]
254
+ ]
255
+ end
256
+ end
257
+ describe '#players_at_hand_start' do
258
+ it 'returns HandPlayers with states set at the beginning of the hand' do
259
+ stacks = [100, 200, 150]
260
+ blinds = [0, 10, 5]
261
+ num_players = 3
262
+ (0..num_players-1).each do |position|
263
+ hands = num_players.times.map { Hand.new }
264
+
265
+ hands[position] = arbitrary_hole_card_hand
266
+
267
+ hand_string = hands.inject('') do |hand_string, hand|
268
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
269
+ end[0..-2]
270
+
271
+ match_state =
272
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrfc:#{hand_string}"
273
+
274
+ MatchState.new(
275
+ match_state
276
+ ).players_at_hand_start(
277
+ stacks,
278
+ blinds
279
+ ).each_with_index do |player, pos|
280
+ player.initial_stack.must_equal stacks[pos]
281
+ player.ante.must_equal blinds[pos]
282
+ player.hand.must_equal hands[pos]
283
+ end
284
+ end
285
+ end
286
+ end
287
+ describe '#every_action' do
288
+ it 'yields every action, plus the round number, and the acting player position relative to the dealer' do
289
+ wager_size = 10
290
+ x_game_def = GameDefinition.new(
291
+ first_player_positions: [3, 2, 2, 2],
292
+ chip_stacks: [100, 200, 150],
293
+ blinds: [0, 10, 5],
294
+ raise_sizes: [wager_size]*4,
295
+ number_of_ranks: 3
229
296
  )
230
- match.for_every_seat! do |seat|
231
- match.for_every_hand! do
232
- match.for_every_turn! do
233
- @connection.expects(:gets).returns(match.current_hand.current_match_state.to_s)
234
297
 
235
- AcpcPokerTypes::MatchState.receive(@connection).must_equal match.current_hand.current_match_state
298
+ (0..x_game_def.number_of_players-1).each do |position|
299
+ x_actions = [
300
+ {
301
+ action: PokerAction.new(PokerAction::CALL, cost: 5),
302
+ round: 0,
303
+ acting_player_position: 2
304
+ },
305
+ {
306
+ action: PokerAction.new(PokerAction::RAISE, cost: wager_size + 10),
307
+ round: 0,
308
+ acting_player_position: 0
309
+ },
310
+ {
311
+ action: PokerAction.new(PokerAction::CALL, cost: wager_size),
312
+ round: 0,
313
+ acting_player_position: 1
314
+ },
315
+ {
316
+ action: PokerAction.new(PokerAction::CALL, cost: wager_size),
317
+ round: 0,
318
+ acting_player_position: 2
319
+ },
320
+ {
321
+ action: PokerAction.new(PokerAction::CHECK),
322
+ round: 1,
323
+ acting_player_position: 1
324
+ },
325
+ {
326
+ action: PokerAction.new(PokerAction::CHECK),
327
+ round: 1,
328
+ acting_player_position: 2
329
+ },
330
+ {
331
+ action: PokerAction.new(PokerAction::CHECK),
332
+ round: 1,
333
+ acting_player_position: 0
334
+ },
335
+ {
336
+ action: PokerAction.new(PokerAction::BET, cost: wager_size),
337
+ round: 2,
338
+ acting_player_position: 1
339
+ },
340
+ {
341
+ action: PokerAction.new(PokerAction::RAISE, cost: 2 * wager_size),
342
+ round: 2,
343
+ acting_player_position: 2
344
+ },
345
+ {
346
+ action: PokerAction.new(PokerAction::FOLD),
347
+ round: 2,
348
+ acting_player_position: 0
349
+ },
350
+ {
351
+ action: PokerAction.new(PokerAction::CALL, cost: wager_size),
352
+ round: 2,
353
+ acting_player_position: 1
354
+ },
355
+ {
356
+ action: PokerAction.new(PokerAction::CHECK),
357
+ round: 3,
358
+ acting_player_position: 1
359
+ },
360
+ {
361
+ action: PokerAction.new(PokerAction::BET, cost: wager_size),
362
+ round: 3,
363
+ acting_player_position: 2
364
+ },
365
+ {
366
+ action: PokerAction.new(PokerAction::CALL, cost: wager_size),
367
+ round: 3,
368
+ acting_player_position: 1
369
+ }
370
+ ]
371
+ hands = x_game_def.number_of_players.times.map { Hand.new }
372
+
373
+ hands[position] = arbitrary_hole_card_hand
374
+
375
+ hand_string = hands.inject('') do |hand_string, hand|
376
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
377
+ end[0..-2]
378
+
379
+ match_state =
380
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrfc/crc:#{hand_string}"
381
+
382
+ MatchState.new(match_state).every_action(x_game_def) do |action, round, acting_player_position|
383
+ x_yields = x_actions.shift
384
+
385
+ action.must_equal x_yields[:action]
386
+ round.must_equal x_yields[:round]
387
+ acting_player_position.must_equal x_yields[:acting_player_position]
388
+ end
389
+ end
390
+ end
391
+ end
392
+ describe '#players' do
393
+ it 'return proper player states' do
394
+ wager_size = 10
395
+ x_game_def = GameDefinition.new(
396
+ first_player_positions: [3, 2, 2],
397
+ chip_stacks: [100, 200, 150],
398
+ blinds: [0, 10, 5],
399
+ raise_sizes: [wager_size]*3,
400
+ number_of_ranks: 3
401
+ )
402
+ x_actions = [
403
+ [
404
+ [
405
+ PokerAction.new(PokerAction::RAISE, cost: wager_size + 10),
406
+ ],
407
+ [
408
+ PokerAction.new(PokerAction::CHECK)
409
+ ],
410
+ [
411
+ PokerAction.new(PokerAction::FOLD)
412
+ ]
413
+ ],
414
+ [
415
+ [
416
+ PokerAction.new(PokerAction::CALL, cost: wager_size)
417
+ ],
418
+ [
419
+ PokerAction.new(PokerAction::CHECK)
420
+ ],
421
+ [
422
+ PokerAction.new(PokerAction::BET, cost: wager_size),
423
+ PokerAction.new(PokerAction::CALL, cost: wager_size)
424
+ ]
425
+ ],
426
+ [
427
+ [
428
+ PokerAction.new(PokerAction::CALL, cost: 5),
429
+ PokerAction.new(PokerAction::CALL, cost: wager_size)
430
+ ],
431
+ [
432
+ PokerAction.new(PokerAction::CHECK)
433
+ ],
434
+ [
435
+ PokerAction.new(PokerAction::RAISE, cost: 2 * wager_size)
436
+ ]
437
+ ]
438
+ ]
439
+ x_contributions = x_actions.map_with_index do |actions_per_player, i|
440
+ actions_per_player.map do |actions_per_round|
441
+ actions_per_round.inject(0) { |sum, action| sum += action.cost }
442
+ end.unshift(x_game_def.blinds[i])
443
+ end
444
+ x_winnings = [0, 0, x_contributions.flatten.inject(:+)]
445
+ x_stacks = x_game_def.chip_stacks.map_with_index do |chip_stack, i|
446
+ chip_stack - x_contributions[i].inject(:+) + x_winnings[i]
447
+ end
448
+ (0..x_game_def.number_of_players-1).each do |position|
449
+ hands = x_game_def.number_of_players.times.map do |i|
450
+ Hand.from_acpc "Ac#{i+2}h"
451
+ end
452
+
453
+ hand_string = hands.inject('') do |hand_string, hand|
454
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
455
+ end[0..-2]
456
+
457
+ match_state =
458
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrfc:#{hand_string}"
459
+
460
+ MatchState.new(
461
+ match_state
462
+ ).players(x_game_def).each_with_index do |player, pos|
463
+ player.initial_stack.must_equal x_game_def.chip_stacks[pos]
464
+ player.ante.must_equal x_game_def.blinds[pos]
465
+ player.hand.must_equal hands[pos]
466
+ player.actions.must_equal x_actions[pos]
467
+ player.contributions.must_equal x_contributions[pos]
468
+ player.winnings.must_equal x_winnings[pos]
469
+ player.stack.must_equal x_stacks[pos]
470
+ end
471
+ end
472
+ end
473
+ describe 'distributes chips properly' do
474
+ it 'when there is a showdown and one winner' do
475
+ wager_size = 10
476
+ x_game_def = GameDefinition.new(
477
+ first_player_positions: [3, 2, 2, 2],
478
+ chip_stacks: [100, 200, 150],
479
+ blinds: [0, 10, 5],
480
+ raise_sizes: [wager_size]*4,
481
+ number_of_ranks: 3
482
+ )
483
+ x_total_contributions = [5 * 10, 5 * 10, 5 * 10]
484
+ x_winnings = [0, 0, x_total_contributions.inject(:+)]
485
+ x_stacks = x_game_def.chip_stacks.map_with_index do |chip_stack, i|
486
+ chip_stack - x_total_contributions[i] + x_winnings[i]
487
+ end
488
+
489
+ (0..x_game_def.number_of_players-1).each do |position|
490
+ hands = x_game_def.number_of_players.times.map do |i|
491
+ Hand.from_acpc "Ac#{i+2}h"
492
+ end
493
+
494
+ hand_string = hands.inject('') do |hand_string, hand|
495
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
496
+ end[0..-2]
497
+
498
+ match_state =
499
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrcc/crcc:#{hand_string}"
500
+
501
+ MatchState.new(match_state).players(x_game_def).each_with_index do |player, i|
502
+ player.winnings.must_equal x_winnings[i]
503
+ player.stack.must_equal x_stacks[i]
504
+ player.total_contribution.must_equal x_total_contributions[i]
505
+ end
506
+ end
507
+ end
508
+ it 'when there is a tie' do
509
+ wager_size = 10
510
+ x_game_def = GameDefinition.new(
511
+ first_player_positions: [3, 2, 2, 2],
512
+ chip_stacks: [100, 200, 150],
513
+ blinds: [0, 10, 5],
514
+ raise_sizes: [wager_size]*4,
515
+ number_of_ranks: 3
516
+ )
517
+ x_total_contributions = [2 * 10, 5 * 10, 5 * 10]
518
+ x_winnings = [0, x_total_contributions.inject(:+)/2.0, x_total_contributions.inject(:+)/2.0]
519
+ x_stacks = x_game_def.chip_stacks.map_with_index do |chip_stack, i|
520
+ chip_stack - x_total_contributions[i] + x_winnings[i]
521
+ end
522
+
523
+ (0..x_game_def.number_of_players-1).each do |position|
524
+ hands = x_game_def.number_of_players.times.map do |i|
525
+ Hand.from_acpc "Ac2#{['s', 'h', 'd', 'c'][i%4]}"
526
+ end
527
+
528
+ hand_string = hands.inject('') do |hand_string, hand|
529
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
530
+ end[0..-2]
531
+
532
+ match_state =
533
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrfc/crc:#{hand_string}"
534
+
535
+ MatchState.new(match_state).players(x_game_def).each_with_index do |player, i|
536
+ player.winnings.must_equal x_winnings[i]
537
+ player.stack.must_equal x_stacks[i]
538
+ player.total_contribution.must_equal x_total_contributions[i]
539
+ end
540
+ end
541
+ end
542
+ it 'when all other players have folded' do
543
+ wager_size = 10
544
+ x_game_def = GameDefinition.new(
545
+ first_player_positions: [3, 2, 2, 2],
546
+ chip_stacks: [100, 200, 150],
547
+ blinds: [0, 10, 5],
548
+ raise_sizes: [wager_size]*4,
549
+ number_of_ranks: 3
550
+ )
551
+ x_total_contributions = [2 * 10, 3 * 10, 4 * 10]
552
+ x_winnings = [0, 0, x_total_contributions.inject(:+)]
553
+ x_stacks = x_game_def.chip_stacks.map_with_index do |chip_stack, i|
554
+ chip_stack - x_total_contributions[i] + x_winnings[i]
555
+ end
556
+
557
+ (0..x_game_def.number_of_players-1).each do |position|
558
+ hands = x_game_def.number_of_players.times.map { Hand.new }
559
+
560
+ hands[position] = arbitrary_hole_card_hand
561
+
562
+ hand_string = hands.inject('') do |hand_string, hand|
563
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
564
+ end[0..-2]
565
+
566
+ match_state =
567
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrff:#{hand_string}"
568
+
569
+ MatchState.new(match_state).players(x_game_def).each_with_index do |player, i|
570
+ player.winnings.must_equal x_winnings[i]
571
+ player.stack.must_equal x_stacks[i]
572
+ player.total_contribution.must_equal x_total_contributions[i]
236
573
  end
237
574
  end
238
575
  end
239
576
  end
577
+ describe '#pot' do
578
+ it 'works without side pots' do
579
+ wager_size = 10
580
+ x_game_def = GameDefinition.new(
581
+ first_player_positions: [3, 2, 2, 2],
582
+ chip_stacks: [100, 200, 150],
583
+ blinds: [0, 10, 5],
584
+ raise_sizes: [wager_size]*4,
585
+ number_of_ranks: 3
586
+ )
587
+ x_total_contributions = [2 * 10, 5 * 10, 5 * 10]
588
+
589
+ (0..x_game_def.number_of_players-1).each do |position|
590
+ hands = x_game_def.number_of_players.times.map do |i|
591
+ Hand.from_acpc "Ac2#{['s', 'h', 'd', 'c'][i%4]}"
592
+ end
593
+
594
+ hand_string = hands.inject('') do |hand_string, hand|
595
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
596
+ end[0..-2]
597
+
598
+ match_state =
599
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrfc/crc:#{hand_string}"
600
+
601
+ MatchState.new(match_state).pot(x_game_def).must_equal x_total_contributions.inject(:+)
602
+ end
603
+ end
604
+ end
605
+ end
606
+ describe '#every_action' do
607
+ it 'yields every action, plus the round number, and the acting player position relative to the dealer' do
608
+ wager_size = 10
609
+ x_game_def = GameDefinition.new(
610
+ first_player_positions: [3, 2, 2, 2],
611
+ chip_stacks: [100, 200, 150],
612
+ blinds: [0, 10, 5],
613
+ raise_sizes: [wager_size]*4,
614
+ number_of_ranks: 3
615
+ )
616
+
617
+ (0..x_game_def.number_of_players-1).each do |position|
618
+ x_actions = [
619
+ {
620
+ action: PokerAction.new(PokerAction::CALL, cost: 5),
621
+ round: 0,
622
+ acting_player_position: 2
623
+ },
624
+ {
625
+ action: PokerAction.new(PokerAction::RAISE, cost: wager_size + 10),
626
+ round: 0,
627
+ acting_player_position: 0
628
+ },
629
+ {
630
+ action: PokerAction.new(PokerAction::CALL, cost: wager_size),
631
+ round: 0,
632
+ acting_player_position: 1
633
+ },
634
+ {
635
+ action: PokerAction.new(PokerAction::CALL, cost: wager_size),
636
+ round: 0,
637
+ acting_player_position: 2
638
+ },
639
+ {
640
+ action: PokerAction.new(PokerAction::CHECK),
641
+ round: 1,
642
+ acting_player_position: 1
643
+ },
644
+ {
645
+ action: PokerAction.new(PokerAction::CHECK),
646
+ round: 1,
647
+ acting_player_position: 2
648
+ },
649
+ {
650
+ action: PokerAction.new(PokerAction::CHECK),
651
+ round: 1,
652
+ acting_player_position: 0
653
+ },
654
+ {
655
+ action: PokerAction.new(PokerAction::BET, cost: wager_size),
656
+ round: 2,
657
+ acting_player_position: 1
658
+ },
659
+ {
660
+ action: PokerAction.new(PokerAction::RAISE, cost: 2 * wager_size),
661
+ round: 2,
662
+ acting_player_position: 2
663
+ },
664
+ {
665
+ action: PokerAction.new(PokerAction::FOLD),
666
+ round: 2,
667
+ acting_player_position: 0
668
+ },
669
+ {
670
+ action: PokerAction.new(PokerAction::CALL, cost: wager_size),
671
+ round: 2,
672
+ acting_player_position: 1
673
+ },
674
+ {
675
+ action: PokerAction.new(PokerAction::CHECK),
676
+ round: 3,
677
+ acting_player_position: 1
678
+ },
679
+ {
680
+ action: PokerAction.new(PokerAction::BET, cost: wager_size),
681
+ round: 3,
682
+ acting_player_position: 2
683
+ },
684
+ {
685
+ action: PokerAction.new(PokerAction::CALL, cost: wager_size),
686
+ round: 3,
687
+ acting_player_position: 1
688
+ }
689
+ ]
690
+ hands = x_game_def.number_of_players.times.map { Hand.new }
691
+
692
+ hands[position] = arbitrary_hole_card_hand
693
+
694
+ hand_string = hands.inject('') do |hand_string, hand|
695
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
696
+ end[0..-2]
697
+
698
+ match_state =
699
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrfc/crc:#{hand_string}"
700
+
701
+ MatchState.new(match_state).player_acting_sequence(x_game_def).must_equal(
702
+ [[2, 0, 1, 2], [1, 2, 0], [1, 2, 0, 1], [1, 2, 1]]
703
+ )
704
+ end
705
+ end
706
+ end
707
+ describe 'hand_ended?' do
708
+ it 'works when there is a showdown' do
709
+ wager_size = 10
710
+ x_game_def = GameDefinition.new(
711
+ first_player_positions: [3, 2, 2, 2],
712
+ chip_stacks: [100, 200, 150],
713
+ blinds: [0, 10, 5],
714
+ raise_sizes: [wager_size]*4,
715
+ number_of_ranks: 3
716
+ )
717
+
718
+ (0..x_game_def.number_of_players-1).each do |position|
719
+ hands = x_game_def.number_of_players.times.map do |i|
720
+ Hand.from_acpc "Ac#{i+2}h"
721
+ end
722
+
723
+ hand_string = hands.inject('') do |hand_string, hand|
724
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
725
+ end[0..-2]
726
+
727
+ match_state =
728
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrfc/crc:#{hand_string}"
729
+
730
+ MatchState.new(match_state).hand_ended?(x_game_def).must_equal true
731
+ end
732
+ end
733
+ it 'works when there is not a showdown' do
734
+ wager_size = 10
735
+ x_game_def = GameDefinition.new(
736
+ first_player_positions: [3, 2, 2, 2],
737
+ chip_stacks: [100, 200, 150],
738
+ blinds: [0, 10, 5],
739
+ raise_sizes: [wager_size]*4,
740
+ number_of_ranks: 3
741
+ )
742
+
743
+ (0..x_game_def.number_of_players-1).each do |position|
744
+ hands = x_game_def.number_of_players.times.map { Hand.new }
745
+
746
+ hands[position] = arbitrary_hole_card_hand
747
+
748
+ hand_string = hands.inject('') do |hand_string, hand|
749
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
750
+ end[0..-2]
751
+
752
+ match_state =
753
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrfc/cr:#{hand_string}"
754
+
755
+ MatchState.new(match_state).hand_ended?(x_game_def).must_equal false
756
+ end
757
+ end
758
+ it 'works when all other players have folded' do
759
+ wager_size = 10
760
+ x_game_def = GameDefinition.new(
761
+ first_player_positions: [3, 2, 2, 2],
762
+ chip_stacks: [100, 200, 150],
763
+ blinds: [0, 10, 5],
764
+ raise_sizes: [wager_size]*4,
765
+ number_of_ranks: 3
766
+ )
767
+
768
+ (0..x_game_def.number_of_players-1).each do |position|
769
+ hands = x_game_def.number_of_players.times.map { Hand.new }
770
+
771
+ hands[position] = arbitrary_hole_card_hand
772
+
773
+ hand_string = hands.inject('') do |hand_string, hand|
774
+ hand_string << "#{hand}#{MatchState::HAND_SEPARATOR}"
775
+ end[0..-2]
776
+
777
+ match_state =
778
+ "#{MatchState::LABEL}:#{position}:0:crcc/ccc/rrff:#{hand_string}"
779
+
780
+ MatchState.new(match_state).hand_ended?(x_game_def).must_equal true
781
+ end
782
+ end
240
783
  end
241
784
  end
242
785
 
243
786
  def for_every_card
244
- AcpcPokerTypes::Rank::DOMAIN.map do |rank, rank_properties|
245
- AcpcPokerTypes::Suit::DOMAIN.map do |suit, suit_properties|
246
- yield AcpcPokerTypes::Card.from_components(rank, suit)
787
+ Rank::DOMAIN.map do |rank, rank_properties|
788
+ Suit::DOMAIN.map do |suit, suit_properties|
789
+ yield Card.from_components(rank, suit)
247
790
  end
248
791
  end
249
792
  end
250
793
  def for_every_hand
251
794
  for_every_card do |first_card|
252
795
  for_every_card do |second_card|
253
- yield AcpcPokerTypes::Hand.draw_cards(first_card, second_card)
796
+ yield Hand.draw_cards(first_card, second_card)
254
797
  end
255
798
  end
256
799
  end
257
800
  def test_match_state_initialization_error(incomplete_match_state)
258
- ->{AcpcPokerTypes::MatchState.parse incomplete_match_state}.must_raise(AcpcPokerTypes::MatchState::IncompleteMatchState)
801
+ ->{MatchState.parse incomplete_match_state}.must_raise(MatchState::IncompleteMatchState)
259
802
  end
260
803
  def test_match_state_success(match_state)
261
- patient = AcpcPokerTypes::MatchState.parse match_state
804
+ patient = MatchState.parse match_state
262
805
  patient.to_s.must_equal match_state
263
806
  patient
264
807
  end
265
808
  def arbitrary_flop
266
809
  flop = ""
267
810
  rank = 2
268
- (AcpcPokerTypes::Suit::DOMAIN.values.map { |suit| suit[:acpc_character] }).each do |suit|
269
- flop += rank.to_s + suit unless AcpcPokerTypes::Suit::DOMAIN[:clubs][:acpc_character] == suit
811
+ (Suit::DOMAIN.values.map { |suit| suit[:acpc_character] }).each do |suit|
812
+ flop += rank.to_s + suit unless Suit::DOMAIN[:clubs][:acpc_character] == suit
270
813
  rank += 1
271
814
  end
272
815
  flop
273
816
  end
274
817
 
275
818
  def arbitrary_roll_out(rounds)
276
- board_cards = ""
819
+ community_cards = ""
277
820
  (1..rounds-1).each do |round|
278
- board_cards += "/" + if round > 1
279
- '2' + AcpcPokerTypes::Suit::DOMAIN[:spades][:acpc_character]
821
+ community_cards += "/" + if round > 1
822
+ '2' + Suit::DOMAIN[:spades][:acpc_character]
280
823
  else
281
824
  arbitrary_flop
282
825
  end
283
826
  end
284
827
 
285
- board_cards
828
+ community_cards
286
829
  end
287
830
 
288
831
  # Construct an arbitrary hole card hand.
289
832
  #
290
- # @return [AcpcPokerTypes::Hand] An arbitrary hole card hand.
833
+ # @return [Hand] An arbitrary hole card hand.
291
834
  def arbitrary_hole_card_hand
292
- AcpcPokerTypes::Hand.from_acpc(
293
- AcpcPokerTypes::Rank::DOMAIN[:two][:acpc_character] +
294
- AcpcPokerTypes::Suit::DOMAIN[:spades][:acpc_character] +
295
- AcpcPokerTypes::Rank::DOMAIN[:three][:acpc_character] +
296
- AcpcPokerTypes::Suit::DOMAIN[:hearts][:acpc_character]
835
+ Hand.from_acpc(
836
+ Rank::DOMAIN[:two][:acpc_character] +
837
+ Suit::DOMAIN[:spades][:acpc_character] +
838
+ Rank::DOMAIN[:three][:acpc_character] +
839
+ Suit::DOMAIN[:hearts][:acpc_character]
297
840
  )
298
841
  end