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