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.
- checksums.yaml +4 -4
- data/Rakefile +7 -4
- data/acpc_poker_types.gemspec +4 -2
- data/lib/acpc_poker_types.rb +13 -12
- data/lib/acpc_poker_types/acpc_dealer_data.rb +9 -0
- data/lib/acpc_poker_types/acpc_dealer_data/action_messages.rb +133 -0
- data/lib/acpc_poker_types/acpc_dealer_data/hand_data.rb +182 -0
- data/lib/acpc_poker_types/acpc_dealer_data/hand_results.rb +79 -0
- data/lib/acpc_poker_types/acpc_dealer_data/log_file.rb +5 -0
- data/lib/acpc_poker_types/acpc_dealer_data/match_definition.rb +54 -0
- data/lib/acpc_poker_types/acpc_dealer_data/poker_match_data.rb +393 -0
- data/lib/acpc_poker_types/board_cards.rb +4 -4
- data/lib/acpc_poker_types/card.rb +16 -16
- data/lib/acpc_poker_types/chip_stack.rb +1 -1
- data/lib/acpc_poker_types/game_definition.rb +34 -38
- data/lib/acpc_poker_types/hand.rb +5 -5
- data/lib/acpc_poker_types/match_state.rb +31 -32
- data/lib/acpc_poker_types/pile_of_cards.rb +1 -1
- data/lib/acpc_poker_types/player.rb +16 -15
- data/lib/acpc_poker_types/poker_action.rb +6 -6
- data/lib/acpc_poker_types/rank.rb +2 -2
- data/lib/acpc_poker_types/suit.rb +4 -4
- data/lib/acpc_poker_types/version.rb +1 -1
- data/spec/action_messages_spec.rb +450 -0
- data/spec/board_cards_spec.rb +9 -9
- data/spec/card_spec.rb +20 -20
- data/spec/chip_stack_spec.rb +28 -29
- data/spec/game_definition_spec.rb +11 -11
- data/spec/hand_data_spec.rb +295 -0
- data/spec/hand_results_spec.rb +292 -0
- data/spec/hand_spec.rb +11 -11
- data/spec/match_definition_spec.rb +95 -0
- data/spec/match_state_spec.rb +105 -287
- data/spec/pile_of_cards_spec.rb +14 -14
- data/spec/player_spec.rb +61 -61
- data/spec/poker_action_spec.rb +49 -49
- data/spec/poker_match_data_spec.rb +388 -0
- data/spec/rank_spec.rb +19 -19
- data/spec/suit_spec.rb +19 -19
- data/spec/support/spec_helper.rb +28 -6
- metadata +55 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cf33f094690cf356dd7bf7e367a3256308da9cf
|
4
|
+
data.tar.gz: 6310cec2471926602154f1f1483a95a3a7b3b682
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 '
|
5
|
+
require 'rake/testtask'
|
6
6
|
|
7
|
-
|
8
|
-
|
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
|
data/acpc_poker_types.gemspec
CHANGED
@@ -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 '
|
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
|
|
data/lib/acpc_poker_types.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
require
|
1
|
+
require "acpc_poker_types/version"
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
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
|