acpc_poker_types 0.0.10 → 1.0.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +7 -4
  3. data/acpc_poker_types.gemspec +4 -2
  4. data/lib/acpc_poker_types.rb +13 -12
  5. data/lib/acpc_poker_types/acpc_dealer_data.rb +9 -0
  6. data/lib/acpc_poker_types/acpc_dealer_data/action_messages.rb +133 -0
  7. data/lib/acpc_poker_types/acpc_dealer_data/hand_data.rb +182 -0
  8. data/lib/acpc_poker_types/acpc_dealer_data/hand_results.rb +79 -0
  9. data/lib/acpc_poker_types/acpc_dealer_data/log_file.rb +5 -0
  10. data/lib/acpc_poker_types/acpc_dealer_data/match_definition.rb +54 -0
  11. data/lib/acpc_poker_types/acpc_dealer_data/poker_match_data.rb +393 -0
  12. data/lib/acpc_poker_types/board_cards.rb +4 -4
  13. data/lib/acpc_poker_types/card.rb +16 -16
  14. data/lib/acpc_poker_types/chip_stack.rb +1 -1
  15. data/lib/acpc_poker_types/game_definition.rb +34 -38
  16. data/lib/acpc_poker_types/hand.rb +5 -5
  17. data/lib/acpc_poker_types/match_state.rb +31 -32
  18. data/lib/acpc_poker_types/pile_of_cards.rb +1 -1
  19. data/lib/acpc_poker_types/player.rb +16 -15
  20. data/lib/acpc_poker_types/poker_action.rb +6 -6
  21. data/lib/acpc_poker_types/rank.rb +2 -2
  22. data/lib/acpc_poker_types/suit.rb +4 -4
  23. data/lib/acpc_poker_types/version.rb +1 -1
  24. data/spec/action_messages_spec.rb +450 -0
  25. data/spec/board_cards_spec.rb +9 -9
  26. data/spec/card_spec.rb +20 -20
  27. data/spec/chip_stack_spec.rb +28 -29
  28. data/spec/game_definition_spec.rb +11 -11
  29. data/spec/hand_data_spec.rb +295 -0
  30. data/spec/hand_results_spec.rb +292 -0
  31. data/spec/hand_spec.rb +11 -11
  32. data/spec/match_definition_spec.rb +95 -0
  33. data/spec/match_state_spec.rb +105 -287
  34. data/spec/pile_of_cards_spec.rb +14 -14
  35. data/spec/player_spec.rb +61 -61
  36. data/spec/poker_action_spec.rb +49 -49
  37. data/spec/poker_match_data_spec.rb +388 -0
  38. data/spec/rank_spec.rb +19 -19
  39. data/spec/suit_spec.rb +19 -19
  40. data/spec/support/spec_helper.rb +28 -6
  41. metadata +55 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b64a898b7daba254af2ce7e70ee95148595a3d19
4
- data.tar.gz: 59be100328bdb941a5308fd477cdb92b080e1106
3
+ metadata.gz: 2cf33f094690cf356dd7bf7e367a3256308da9cf
4
+ data.tar.gz: 6310cec2471926602154f1f1483a95a3a7b3b682
5
5
  SHA512:
6
- metadata.gz: 936b7b4b35c17d22adc2ba8e0b444307435dbb310ed6aed0606a5789a17fef283f199a602aa55e4461336924ac152132c9c007de066a9c7da4b38104f980a416
7
- data.tar.gz: ea4ddaa471833908ef114cb0cc6d3400e0de9789ff80128f7583ea101fcbee0bdb08afe96f2b253e5a97ae11b5d40af34fc6f737c1dfb584867b7b06690460c0
6
+ metadata.gz: 7956c953263284117336feb0546745d697fe525611329472a630a69ba50003686aa350f4b90aa510516e153155dab2a774477fa88e80e67609523d4e8a61b30b
7
+ data.tar.gz: 6549e5f8b25318cae85297116808ce3a2e934c4617685979ad8910849bcab9e1a7e7722025c366a1540d03e5b1cc6d91186f7c2a9a4d195c8440c03f84e72dc3
data/Rakefile CHANGED
@@ -1,9 +1,12 @@
1
1
  #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
2
3
 
3
- require 'bundler/gem_tasks'
4
4
  require 'rake'
5
- require 'rspec/core/rake_task'
5
+ require 'rake/testtask'
6
6
 
7
- RSpec::Core::RakeTask.new(:spec) do |t|
8
- ruby_opts = "-w"
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "lib" << 'spec/support'
9
+ t.test_files = FileList['spec/**/*_spec.rb']
10
+ t.verbose = false
11
+ t.warning = false # pry-rescue has a lot of warnings
9
12
  end
@@ -13,9 +13,11 @@ Gem::Specification.new do |s|
13
13
  s.add_dependency 'dmorrill10-utils'
14
14
  s.add_dependency 'acpc_dealer', '~> 0.0'
15
15
 
16
- s.add_development_dependency 'acpc_dealer_data', '~> 1.0'
17
16
  s.add_development_dependency 'celluloid', '~> 0.13'
18
- s.add_development_dependency 'rspec'
17
+ s.add_development_dependency 'awesome_print'
18
+ s.add_development_dependency 'minitest'
19
+ s.add_development_dependency 'turn'
20
+ s.add_development_dependency 'pry-rescue'
19
21
  s.add_development_dependency 'mocha'
20
22
  s.add_development_dependency 'simplecov'
21
23
 
@@ -1,15 +1,16 @@
1
- require File.expand_path("../acpc_poker_types/version", __FILE__)
1
+ require "acpc_poker_types/version"
2
2
 
3
- require File.expand_path("../acpc_poker_types/board_cards", __FILE__)
4
- require File.expand_path("../acpc_poker_types/card", __FILE__)
5
- require File.expand_path("../acpc_poker_types/chip_stack", __FILE__)
6
- require File.expand_path("../acpc_poker_types/game_definition", __FILE__)
7
- require File.expand_path("../acpc_poker_types/hand", __FILE__)
8
- require File.expand_path("../acpc_poker_types/match_state", __FILE__)
9
- require File.expand_path("../acpc_poker_types/pile_of_cards", __FILE__)
10
- require File.expand_path("../acpc_poker_types/player", __FILE__)
11
- require File.expand_path("../acpc_poker_types/rank", __FILE__)
12
- require File.expand_path("../acpc_poker_types/suit", __FILE__)
3
+ require "acpc_poker_types/board_cards"
4
+ require "acpc_poker_types/card"
5
+ require "acpc_poker_types/chip_stack"
6
+ require "acpc_poker_types/game_definition"
7
+ require "acpc_poker_types/hand"
8
+ require "acpc_poker_types/match_state"
9
+ require "acpc_poker_types/pile_of_cards"
10
+ require "acpc_poker_types/player"
11
+ require "acpc_poker_types/rank"
12
+ require "acpc_poker_types/suit"
13
+ require "acpc_poker_types/acpc_dealer_data"
13
14
 
14
15
  module AcpcPokerTypes
15
- end
16
+ end
@@ -0,0 +1,9 @@
1
+ require 'acpc_poker_types/acpc_dealer_data/action_messages'
2
+ require 'acpc_poker_types/acpc_dealer_data/hand_data'
3
+ require 'acpc_poker_types/acpc_dealer_data/hand_results'
4
+ require 'acpc_poker_types/acpc_dealer_data/poker_match_data'
5
+ require 'acpc_poker_types/acpc_dealer_data/match_definition'
6
+ require 'acpc_poker_types/acpc_dealer_data/log_file'
7
+
8
+ module AcpcPokerTypes::AcpcDealerData
9
+ end
@@ -0,0 +1,133 @@
1
+
2
+ require 'acpc_poker_types/match_state'
3
+ require 'acpc_poker_types/poker_action'
4
+
5
+ require 'acpc_poker_types/acpc_dealer_data/match_definition'
6
+ require 'acpc_poker_types/acpc_dealer_data/log_file'
7
+
8
+ module AcpcPokerTypes::AcpcDealerData
9
+ class ActionMessages
10
+ attr_reader(
11
+ :data, :final_score, :match_def
12
+ )
13
+
14
+ ToMessage = Struct.new(
15
+ # @returns [Integer] Seat of the player receiving the message
16
+ :seat,
17
+ # @returns [AcpcPokerTypes::MatchState] Match state received by the player
18
+ :state
19
+ )
20
+
21
+ FromMessage = Struct.new(
22
+ # @returns [Integer] Seat of the player acting
23
+ :seat,
24
+ # @returns [AcpcPokerTypes::MatchState] Match state on which the action was taken
25
+ :state,
26
+ # @returns [AcpcPokerTypes::PokerAction] Action taken
27
+ :action
28
+ )
29
+
30
+ # @param [String] to_message TO message (message to player)
31
+ def self.parse_to_message(to_message)
32
+ if to_message.strip.match(
33
+ /^TO\s*(\d+)\s*at\s*[\d\.]+\s+(\S+)$/
34
+ )
35
+ ToMessage.new(
36
+ $1.to_i - 1,
37
+ AcpcPokerTypes::MatchState.parse($2)
38
+ )
39
+ else
40
+ nil
41
+ end
42
+ end
43
+
44
+ CONCATONATED_ACTIONS = AcpcPokerTypes::PokerAction::LEGAL_ACPC_CHARACTERS.to_a.join('')
45
+
46
+ # @param [String] from_message FROM message (message from player)
47
+ def self.parse_from_message(from_message)
48
+ if from_message.strip.match(
49
+ /^FROM\s*(\d+)\s*at\s*[\d\.]+\s*(#{AcpcPokerTypes::MatchState::LABEL}\S+):([#{CONCATONATED_ACTIONS}]\s*\d*)$/
50
+ )
51
+ FromMessage.new(
52
+ $1.to_i - 1,
53
+ AcpcPokerTypes::MatchState.parse($2),
54
+ AcpcPokerTypes::PokerAction.new($3)
55
+ )
56
+ else
57
+ nil
58
+ end
59
+ end
60
+
61
+ def self.parse_score(score_string)
62
+ if score_string.strip.match(
63
+ /^SCORE:([\d\-\.|]+):([\w|]+)$/
64
+ )
65
+
66
+ stack_changes = $1.split '|'
67
+ players = $2.split '|'
68
+
69
+ players.each_index.inject({}) do |player_results, j|
70
+ player_results[players[j].to_sym] = stack_changes[j].to_r
71
+ player_results
72
+ end
73
+ else
74
+ nil
75
+ end
76
+ end
77
+
78
+ def self.parse_to_or_from_message(message)
79
+ parsed_message = AcpcPokerTypes::AcpcDealerData::ActionMessages.parse_to_message(message)
80
+ if parsed_message.nil?
81
+ AcpcPokerTypes::AcpcDealerData::ActionMessages.parse_from_message(message)
82
+ else
83
+ parsed_message
84
+ end
85
+ end
86
+
87
+ def self.parse_file(
88
+ acpc_log_file_path,
89
+ player_names,
90
+ game_def_directory,
91
+ num_hands=nil
92
+ )
93
+ AcpcPokerTypes::AcpcDealerData::LogFile.open(acpc_log_file_path, 'r') do |file|
94
+ AcpcPokerTypes::AcpcDealerData::ActionMessages.parse file, player_names, game_def_directory, num_hands
95
+ end
96
+ end
97
+
98
+ alias_new :parse
99
+
100
+ def initialize(
101
+ acpc_log_statements,
102
+ player_names,
103
+ game_def_directory,
104
+ num_hands=nil
105
+ )
106
+ @final_score = nil
107
+ @match_def = nil
108
+ @data = acpc_log_statements.inject([]) do |accumulating_data, log_line|
109
+ if @match_def.nil?
110
+ @match_def = AcpcPokerTypes::AcpcDealerData::MatchDefinition.parse(log_line, player_names, game_def_directory)
111
+ else
112
+ parsed_message = AcpcPokerTypes::AcpcDealerData::ActionMessages.parse_to_or_from_message(log_line)
113
+ if parsed_message
114
+ if (
115
+ accumulating_data.empty? ||
116
+ accumulating_data.last.first[:state].hand_number != parsed_message[:state].hand_number
117
+ )
118
+ break accumulating_data if accumulating_data.length == num_hands
119
+
120
+ accumulating_data << []
121
+ end
122
+
123
+ accumulating_data.last << parsed_message
124
+ else
125
+ @final_score = AcpcPokerTypes::AcpcDealerData::ActionMessages.parse_score(log_line) unless @final_score
126
+ end
127
+ end
128
+
129
+ accumulating_data
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,182 @@
1
+
2
+ require 'dmorrill10-utils/class'
3
+
4
+ require 'acpc_poker_types/acpc_dealer_data/match_definition'
5
+
6
+ # Monkey patch for easy boundary checking
7
+ class Array
8
+ def in_bounds?(i)
9
+ i < length
10
+ end
11
+ end
12
+
13
+ module AcpcPokerTypes::AcpcDealerData
14
+ class HandData
15
+ exceptions :match_definitions_do_not_match, :invalid_data
16
+
17
+ attr_accessor(
18
+ # @returns [Array<Numeric>] Chip distribution at the end of the hand
19
+ :chip_distribution,
20
+ # @returns [MatchDefinition] Game definition and match parameters
21
+ :match_def,
22
+ # @returns [Integer] Zero-index turn number within the hand
23
+ :turn_number,
24
+ # @returns [Turn] Turn data
25
+ :data,
26
+ # @returns [Integer] Seat of the active player
27
+ :seat
28
+ )
29
+
30
+ # State messages are organized by seat
31
+ Turn = Struct.new(
32
+ # @returns [Array<MatchState>] Match states sent during this turn arranged by seat
33
+ :state_messages,
34
+ # @returns [ActionMessages::FromMessage] Action message sent during this turn
35
+ :action_message
36
+ )
37
+
38
+ def initialize(match_def, action_data, result)
39
+ @match_def = match_def
40
+
41
+ set_chip_distribution! result
42
+
43
+ set_data! action_data
44
+
45
+ @turn_number = nil
46
+ @seat = nil
47
+ end
48
+
49
+ def ==(other)
50
+ @match_def == other.match_def &&
51
+ @chip_distribution == other.chip_distribution &&
52
+ @data == other.data
53
+ end
54
+
55
+ def for_every_turn!(seat=0)
56
+ @seat = seat
57
+ @data.each_index do |i|
58
+ @turn_number = i
59
+
60
+ yield @turn_number
61
+ end
62
+
63
+ @turn_number = nil
64
+ self
65
+ end
66
+
67
+ def current_match_state(seat=@seat)
68
+ if @turn_number
69
+ @data[@turn_number].state_messages[seat]
70
+ else
71
+ nil
72
+ end
73
+ end
74
+
75
+ # @return [ActionMessage] Next action in the hand to be taken in the current turn.
76
+ def next_action
77
+ if @turn_number
78
+ @data[@turn_number].action_message
79
+ else
80
+ nil
81
+ end
82
+ end
83
+
84
+ def last_match_state(seat=@seat)
85
+ if @turn_number && @turn_number != 0
86
+ @data[@turn_number-1].state_messages[seat]
87
+ else
88
+ nil
89
+ end
90
+ end
91
+
92
+ def last_action
93
+ if @turn_number && @turn_number != 0
94
+ @data[@turn_number-1].action_message
95
+ else
96
+ nil
97
+ end
98
+ end
99
+
100
+ def final_turn?
101
+ if @turn_number
102
+ @turn_number >= @data.length - 1
103
+ else
104
+ nil
105
+ end
106
+ end
107
+
108
+ protected
109
+
110
+ def set_chip_distribution!(result)
111
+ @chip_distribution = []
112
+ result.each do |player_name, amount|
113
+ begin
114
+ @chip_distribution[@match_def.player_names.index(player_name.to_s)] = amount
115
+ rescue TypeError
116
+ raise PlayerNamesDoNotMatch
117
+ end
118
+ end
119
+ end
120
+
121
+ def set_data!(action_data)
122
+ number_of_state_messages = @match_def.game_def.number_of_players
123
+
124
+ @data = []
125
+ message_number = 0
126
+ while message_number < action_data.length
127
+ state_messages = action_data[message_number..message_number+number_of_state_messages-1]
128
+
129
+ assert_messages_have_no_actions state_messages
130
+
131
+ state_messages = process_state_messages state_messages
132
+
133
+ assert_messages_are_well_defined state_messages
134
+
135
+ message_number += number_of_state_messages
136
+
137
+ action_message = if action_data.in_bounds?(message_number) &&
138
+ action_data[message_number].respond_to?(:action)
139
+
140
+ message_number += 1
141
+ action_data[message_number-1]
142
+ else
143
+ assert_message_is_from_final_turn action_data, message_number, state_messages
144
+
145
+ nil
146
+ end
147
+
148
+ @data << Turn.new(state_messages, action_message)
149
+ end
150
+ end
151
+
152
+ private
153
+
154
+ def process_state_messages(state_messages)
155
+ state_messages.inject([]) do |messages, raw_messages|
156
+ messages[raw_messages.seat] = raw_messages.state
157
+ messages
158
+ end
159
+ end
160
+
161
+ def assert_messages_have_no_actions(state_messages)
162
+ if state_messages.any? { |message| message.respond_to?(:action) }
163
+ raise InvalidData, state_messages.find do |message|
164
+ !message.action.nil?
165
+ end.inspect
166
+ end
167
+ end
168
+
169
+ def assert_messages_are_well_defined(state_messages)
170
+ if state_messages.any? { |message| message.nil? }
171
+ raise InvalidData, state_messages.find { |message| message.nil? }.inspect
172
+ end
173
+ end
174
+
175
+ def assert_message_is_from_final_turn(action_data, message_number, state_messages)
176
+ if action_data.in_bounds?(message_number+1) &&
177
+ state_messages.last.round == action_data[message_number+1].state.round
178
+ raise InvalidData, action_data[message_number].inspect
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,79 @@
1
+
2
+ require 'dmorrill10-utils/class'
3
+
4
+ require 'acpc_poker_types/acpc_dealer_data/match_definition'
5
+ require 'acpc_poker_types/acpc_dealer_data/log_file'
6
+
7
+ module AcpcPokerTypes::AcpcDealerData
8
+ class HandResults
9
+ attr_reader :data, :final_score, :match_def
10
+
11
+ def self.parse_state(state_string)
12
+ if state_string.strip.match(
13
+ /^STATE:\d+:[cfr\d\/]+:[^:]+:([\d\-\.|]+):([\w|]+)$/
14
+ )
15
+
16
+ stack_changes = $1.split '|'
17
+ players = $2.split '|'
18
+
19
+ players.each_index.inject({}) do |player_results, j|
20
+ player_results[players[j].to_sym] = stack_changes[j].to_r
21
+ player_results
22
+ end
23
+ else
24
+ nil
25
+ end
26
+ end
27
+
28
+ def self.parse_score(score_string)
29
+ if score_string.strip.match(
30
+ /^SCORE:([\d\-\.|]+):([\w|]+)$/
31
+ )
32
+
33
+ stack_changes = $1.split '|'
34
+ players = $2.split '|'
35
+
36
+ players.each_index.inject({}) do |player_results, j|
37
+ player_results[players[j].to_sym] = stack_changes[j].to_r
38
+ player_results
39
+ end
40
+ else
41
+ nil
42
+ end
43
+ end
44
+
45
+ def self.parse_file(
46
+ acpc_log_file_path,
47
+ player_names,
48
+ game_def_directory,
49
+ num_hands=nil
50
+ )
51
+ AcpcPokerTypes::AcpcDealerData::LogFile.open(acpc_log_file_path, 'r') do |file|
52
+ AcpcPokerTypes::AcpcDealerData::HandResults.parse file, player_names, game_def_directory, num_hands
53
+ end
54
+ end
55
+
56
+ alias_new :parse
57
+
58
+ def initialize(acpc_log_statements, player_names, game_def_directory, num_hands=nil)
59
+ @final_score = nil
60
+ @match_def = nil
61
+
62
+ @data = acpc_log_statements.inject([]) do |accumulating_data, log_line|
63
+ if @match_def.nil?
64
+ @match_def = AcpcPokerTypes::AcpcDealerData::MatchDefinition.parse(log_line, player_names, game_def_directory)
65
+ else
66
+ parsed_message = AcpcPokerTypes::AcpcDealerData::HandResults.parse_state(log_line)
67
+ if parsed_message
68
+ accumulating_data << parsed_message
69
+ break accumulating_data if accumulating_data.length == num_hands
70
+ else
71
+ @final_score = AcpcPokerTypes::AcpcDealerData::HandResults.parse_score(log_line) unless @final_score
72
+ end
73
+ end
74
+
75
+ accumulating_data
76
+ end
77
+ end
78
+ end
79
+ end