acpc_poker_basic_proxy 2.0.0 → 3.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/lib/acpc_poker_basic_proxy/basic_proxy.rb +34 -7
- data/lib/acpc_poker_basic_proxy/dealer_stream.rb +146 -0
- data/lib/acpc_poker_basic_proxy/version.rb +1 -1
- data/lib/acpc_poker_basic_proxy.rb +1 -1
- data/spec/basic_proxy_spec.rb +76 -5
- data/spec/coverage/index.html +1 -1
- data/spec/dealer_stream_spec.rb +1 -2
- data/spec/support/spec_helper.rb +25 -25
- metadata +3 -10
- data/lib/acpc_poker_basic_proxy/communication_logic/action_sender.rb +0 -45
- data/lib/acpc_poker_basic_proxy/communication_logic/dealer_stream.rb +0 -150
- data/lib/acpc_poker_basic_proxy/communication_logic/match_state_receiver.rb +0 -18
- data/lib/acpc_poker_basic_proxy/communication_logic.rb +0 -8
- data/spec/action_sender_spec.rb +0 -91
- data/spec/match_state_receiver_spec.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 424876fe9b5e2498c3b29df7337b975d8f77aa9f
|
4
|
+
data.tar.gz: 5a49ff106ad812d598577c778c5801ab44d37f89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c3e54d32b12534faf18d81d243e2d44047ee16a4c7a57388dcf4ff9df66cab6c38488ef74a4c05f3f80b63c8fe7bcb17332ee94ca4ee935fa0f3e052f33fd98
|
7
|
+
data.tar.gz: 333608628e63fe3cb6eb66d6a37f8ebcc6fb0633b872b1cf2880c48b1e3a2f6653dcc345df8dba8b1a759792ebb1e7f94da0ed7bfcf946a541fd0212146a9379
|
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'acpc_dealer'
|
2
|
+
require 'acpc_poker_types'
|
2
3
|
|
3
|
-
require 'acpc_poker_basic_proxy/
|
4
|
-
require 'acpc_poker_basic_proxy/communication_logic/action_sender'
|
5
|
-
require 'acpc_poker_basic_proxy/communication_logic/match_state_receiver'
|
4
|
+
require 'acpc_poker_basic_proxy/dealer_stream'
|
6
5
|
|
7
6
|
require 'contextual_exceptions'
|
8
7
|
using ContextualExceptions::ClassRefinement
|
@@ -10,11 +9,39 @@ using ContextualExceptions::ClassRefinement
|
|
10
9
|
# A bot that connects to a dealer as a proxy.
|
11
10
|
module AcpcPokerBasicProxy
|
12
11
|
class BasicProxy
|
13
|
-
exceptions :initial_match_state_not_yet_received
|
12
|
+
exceptions :initial_match_state_not_yet_received, :illegal_action_format
|
13
|
+
|
14
|
+
CONCATENATED_MODIFIABLE_ACTIONS = AcpcPokerTypes::PokerAction::MODIFIABLE_ACTIONS.to_a.join
|
15
|
+
|
16
|
+
# Sends the given +action+ to through the given +connection+ in the ACPC
|
17
|
+
# format.
|
18
|
+
# @param [#write, #ready_to_write?] connection The connection through which the +action+
|
19
|
+
# should be sent.
|
20
|
+
# @param [#to_s] match_state The current match state.
|
21
|
+
# @param [#to_s] action The action to be sent through the +connection+.
|
22
|
+
# @return [Integer] The number of bytes written.
|
23
|
+
# @raise (see #validate_match_state)
|
24
|
+
# @raise (see #validate_action)
|
25
|
+
def self.send_action(connection, match_state, action)
|
26
|
+
validate_action action
|
27
|
+
|
28
|
+
full_action = "#{AcpcPokerTypes::MatchState.parse(match_state.to_s)}:#{action.to_s}"
|
29
|
+
connection.write full_action
|
30
|
+
end
|
31
|
+
|
32
|
+
# @raise IllegalActionFormat
|
33
|
+
def self.validate_action(action)
|
34
|
+
raise IllegalActionFormat unless self.valid_action?(action)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.valid_action?(action)
|
38
|
+
action.to_s.match(/^[#{AcpcPokerTypes::PokerAction::CONCATONATED_ACTIONS}]$/) ||
|
39
|
+
action.to_s.match(/^[#{CONCATENATED_MODIFIABLE_ACTIONS}]\d+$/)
|
40
|
+
end
|
14
41
|
|
15
42
|
# @param [AcpcDealer::ConnectionInformation] dealer_information Information about the dealer to which this bot should connect.
|
16
43
|
def initialize(dealer_information)
|
17
|
-
@dealer_communicator =
|
44
|
+
@dealer_communicator = DealerStream.new dealer_information.port_number, dealer_information.host_name, dealer_information.millisecond_response_timeout
|
18
45
|
end
|
19
46
|
|
20
47
|
# @param [PokerAction] action The action to be sent.
|
@@ -23,12 +50,12 @@ module AcpcPokerBasicProxy
|
|
23
50
|
# @raise (see ActionSender#send_action)
|
24
51
|
def send_action(action)
|
25
52
|
raise InitialMatchStateNotYetReceived unless @match_state
|
26
|
-
|
53
|
+
BasicProxy.send_action @dealer_communicator, @match_state, action
|
27
54
|
end
|
28
55
|
|
29
56
|
# @see MatchStateReceiver#receive_match_state
|
30
57
|
def receive_match_state!
|
31
|
-
@match_state =
|
58
|
+
@match_state = AcpcPokerTypes::MatchState.receive @dealer_communicator
|
32
59
|
end
|
33
60
|
end
|
34
61
|
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
module AcpcPokerBasicProxy
|
5
|
+
module IoRefinement
|
6
|
+
refine IO do
|
7
|
+
# Checks if the socket is ready to be read from.
|
8
|
+
# @param [Integer] timeout_in_seconds Amount of time to wait for the sever to respond, in seconds. Must be positive or +nil+.
|
9
|
+
# @return [Boolean] +true+ if the socket is ready to be read from, +false+ otherwise.
|
10
|
+
def ready_to_read?(timeout_in_seconds=nil)
|
11
|
+
read_array = [self]
|
12
|
+
write_array = nil
|
13
|
+
error_array = nil
|
14
|
+
|
15
|
+
select?(read_array, write_array, error_array, timeout_in_seconds)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Checks if the socket is ready to be written to.
|
19
|
+
# @param [Integer] timeout_in_seconds Amount of time to wait for the sever to respond, in seconds. Must be positive or +nil+.
|
20
|
+
# @return [Boolean] +true+ if the socket is ready to be written to, +false+ otherwise.
|
21
|
+
def ready_to_write?(timeout_in_seconds=nil)
|
22
|
+
read_array = nil
|
23
|
+
write_array = [self]
|
24
|
+
error_array = nil
|
25
|
+
|
26
|
+
select?(read_array, write_array, error_array, timeout_in_seconds)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# @see IO#select
|
32
|
+
def select?(read_array, write_array=[], error_array=[], timeout_in_seconds=nil)
|
33
|
+
IO.select(read_array, write_array, error_array, timeout_in_seconds) != nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
using AcpcPokerBasicProxy::IoRefinement
|
39
|
+
|
40
|
+
require 'contextual_exceptions'
|
41
|
+
using ContextualExceptions::ClassRefinement
|
42
|
+
|
43
|
+
# Communication service to the ACPC Dealer.
|
44
|
+
# It acts solely as an abstraction of the communication protocol and
|
45
|
+
# implements the main Ruby communication interface through 'gets' and 'puts'
|
46
|
+
# methods.
|
47
|
+
module AcpcPokerBasicProxy
|
48
|
+
class DealerStream < DelegateClass(TCPSocket)
|
49
|
+
exceptions :unable_to_connect_to_dealer, :unable_to_write_to_dealer, :unable_to_get_from_dealer
|
50
|
+
|
51
|
+
# @return [String] The ACPC dealer version label.
|
52
|
+
VERSION_LABEL = 'VERSION'
|
53
|
+
|
54
|
+
# @return [Hash] The ACPC dealer version numbers.
|
55
|
+
VERSION_NUMBERS = {:major => 2, :minor => 0, :revision => 0}
|
56
|
+
|
57
|
+
# @return [String] Dealer specified string terminator.
|
58
|
+
TERMINATION_STRING = "\r\n"
|
59
|
+
|
60
|
+
# @param [Integer] port The port on which to connect to the dealer.
|
61
|
+
# @param [String] host_name The host on which the dealer is running. Defaults to 'localhost'
|
62
|
+
# @param [Integer] millisecond_response_timeout The dealer's response timeout, in milliseconds.
|
63
|
+
# @raise AcpcDealerConnectionError, UnableToWriteToDealer
|
64
|
+
def initialize(port, host_name='localhost', millisecond_response_timeout=nil)
|
65
|
+
begin
|
66
|
+
@dealer_socket = TCPSocket.new(host_name, port)
|
67
|
+
super @dealer_socket
|
68
|
+
|
69
|
+
@response_timeout_in_seconds = if millisecond_response_timeout
|
70
|
+
millisecond_response_timeout/(10**3).to_r
|
71
|
+
else
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
send_version_string_to_dealer
|
76
|
+
rescue UnableToWriteToDealer
|
77
|
+
raise
|
78
|
+
rescue Errno::ECONNREFUSED => e
|
79
|
+
handle_error UnableToConnectToDealer, "Unable to connect to the dealer on #{host_name} through port #{port}", e
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Retrieves a string from the dealer.
|
84
|
+
#
|
85
|
+
# @return [String] A string from the dealer.
|
86
|
+
# @raise UnableToGetFromDealer
|
87
|
+
def gets
|
88
|
+
begin
|
89
|
+
string_from_dealer
|
90
|
+
rescue => e
|
91
|
+
handle_error UnableToGetFromDealer, "Unable to get a string from the dealer", e
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Sends a given +string+ to the dealer.
|
96
|
+
#
|
97
|
+
# @param [String] string The string to send.
|
98
|
+
# @return (see #send_string_to_dealer)
|
99
|
+
# @raise UnableToWriteToDealer
|
100
|
+
def write(string)
|
101
|
+
begin
|
102
|
+
num_bytes_written = send_string_to_dealer string
|
103
|
+
rescue => e
|
104
|
+
handle_error UnableToWriteToDealer, "Unable to send the string, \"#{string}\", to the dealer", e
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# @see TCPSocket#ready_to_write?
|
109
|
+
def ready_to_write?
|
110
|
+
@dealer_socket.ready_to_write? @response_timeout_in_seconds
|
111
|
+
end
|
112
|
+
|
113
|
+
# @see TCPSocket#ready_to_read?
|
114
|
+
def ready_to_read?
|
115
|
+
@dealer_socket.ready_to_read? @response_timeout_in_seconds
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def handle_error(exception, message, context_exception)
|
121
|
+
close if @dealer_socket && !closed?
|
122
|
+
raise exception.with_context(message, context_exception)
|
123
|
+
end
|
124
|
+
|
125
|
+
# @return (see #send_string_to_dealer)
|
126
|
+
def send_version_string_to_dealer
|
127
|
+
version_string = "#{VERSION_LABEL}:#{VERSION_NUMBERS[:major]}.#{VERSION_NUMBERS[:minor]}.#{VERSION_NUMBERS[:revision]}"
|
128
|
+
begin
|
129
|
+
send_string_to_dealer version_string
|
130
|
+
rescue => e
|
131
|
+
handle_error UnableToWriteToDealer, "Unable to send version string, \"#{version_string}\", to the dealer", e
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# @return [Integer] The number of bytes written to the dealer.
|
136
|
+
def send_string_to_dealer(string)
|
137
|
+
raise unless ready_to_write?
|
138
|
+
@dealer_socket.write(string + TERMINATION_STRING)
|
139
|
+
end
|
140
|
+
|
141
|
+
def string_from_dealer
|
142
|
+
raise unless ready_to_read?
|
143
|
+
@dealer_socket.gets.chomp
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/spec/basic_proxy_spec.rb
CHANGED
@@ -7,11 +7,9 @@ require 'acpc_poker_types/acpc_dealer_data/poker_match_data'
|
|
7
7
|
require 'acpc_dealer'
|
8
8
|
|
9
9
|
require 'acpc_poker_basic_proxy/basic_proxy'
|
10
|
-
require 'acpc_poker_basic_proxy/communication_logic/action_sender'
|
11
10
|
|
12
11
|
include AcpcPokerBasicProxy
|
13
12
|
include AcpcPokerTypes
|
14
|
-
include CommunicationLogic
|
15
13
|
include AcpcDealerData
|
16
14
|
|
17
15
|
describe BasicProxy do
|
@@ -25,10 +23,19 @@ describe BasicProxy do
|
|
25
23
|
DealerStream.expects(:new).once.with(port_number, host_name, millisecond_response_timeout).returns(@dealer_communicator)
|
26
24
|
|
27
25
|
@patient = BasicProxy.new delaer_info
|
26
|
+
|
27
|
+
@connection = MiniTest::Mock.new
|
28
|
+
@match_state = AcpcDealerData::PokerMatchData.parse_files(
|
29
|
+
MatchLog.all[0].actions_file_path,
|
30
|
+
MatchLog.all[0].results_file_path,
|
31
|
+
MatchLog.all[0].player_names,
|
32
|
+
AcpcDealer::DEALER_DIRECTORY,
|
33
|
+
1
|
34
|
+
).data[0].data[0].action_message.state
|
28
35
|
end
|
29
36
|
|
30
37
|
it 'given a sequence of match states and actions, it properly sends and receives them' do
|
31
|
-
|
38
|
+
MatchLog.all.each do |log_description|
|
32
39
|
match = PokerMatchData.parse_files(
|
33
40
|
log_description.actions_file_path,
|
34
41
|
log_description.results_file_path,
|
@@ -52,7 +59,7 @@ describe BasicProxy do
|
|
52
59
|
|
53
60
|
if action && match_state == match.current_hand.next_action.state && match.current_hand.next_action.seat == seat
|
54
61
|
|
55
|
-
|
62
|
+
BasicProxy.expects(:send_action).once.with(@dealer_communicator, match_state.to_s, action)
|
56
63
|
|
57
64
|
@patient.send_action(action)
|
58
65
|
end
|
@@ -61,10 +68,74 @@ describe BasicProxy do
|
|
61
68
|
end
|
62
69
|
end
|
63
70
|
end
|
64
|
-
|
65
71
|
describe '#send_action' do
|
66
72
|
it 'raises an exception if a match state was not received before an action was sent' do
|
67
73
|
-> {@patient.send_action(mock('PokerAction'))}.must_raise BasicProxy::InitialMatchStateNotYetReceived
|
68
74
|
end
|
69
75
|
end
|
76
|
+
describe "#send_action" do
|
77
|
+
it 'does not send an illegal action and raises an exception' do
|
78
|
+
-> do
|
79
|
+
BasicProxy.send_action(@connection, @match_state, 'illegal action format')
|
80
|
+
end.must_raise BasicProxy::IllegalActionFormat
|
81
|
+
end
|
82
|
+
it 'raises an exception if the given match state does not have the proper format' do
|
83
|
+
-> do
|
84
|
+
BasicProxy.send_action(@connection, 'illegal match state format', PokerAction::CALL)
|
85
|
+
end.must_raise MatchState::IncompleteMatchState
|
86
|
+
end
|
87
|
+
it 'can send all legal actions through the provided connection without a modifier' do
|
88
|
+
PokerAction::ACTIONS.each do |action|
|
89
|
+
action_that_should_be_sent = @match_state.to_s + ":#{action}"
|
90
|
+
@connection.expect :write, nil, [action_that_should_be_sent]
|
91
|
+
|
92
|
+
BasicProxy.send_action @connection, @match_state, action
|
93
|
+
end
|
94
|
+
end
|
95
|
+
it 'does not send legal unmodifiable actions that have a modifier and raises an exception' do
|
96
|
+
(PokerAction::ACTIONS - PokerAction::MODIFIABLE_ACTIONS).each do |unmodifiable_action|
|
97
|
+
-> do
|
98
|
+
BasicProxy.send_action(@connection, @match_state, unmodifiable_action + 9001.to_s)
|
99
|
+
end.must_raise BasicProxy::IllegalActionFormat
|
100
|
+
end
|
101
|
+
end
|
102
|
+
it 'can send all legal modifiable actions through the provided connection with a modifier' do
|
103
|
+
PokerAction::MODIFIABLE_ACTIONS.each do |action|
|
104
|
+
arbitrary_modifier = 9001
|
105
|
+
action_string = action + arbitrary_modifier.to_s
|
106
|
+
action_that_should_be_sent = @match_state.to_s + ":#{action_string}"
|
107
|
+
@connection.expect :write, nil, [action_that_should_be_sent]
|
108
|
+
|
109
|
+
BasicProxy.send_action @connection, @match_state, action_string
|
110
|
+
end
|
111
|
+
end
|
112
|
+
it 'works for all test data examples' do
|
113
|
+
MatchLog.all.each do |log_description|
|
114
|
+
match = AcpcDealerData::PokerMatchData.parse_files(
|
115
|
+
log_description.actions_file_path,
|
116
|
+
log_description.results_file_path,
|
117
|
+
log_description.player_names,
|
118
|
+
AcpcDealer::DEALER_DIRECTORY,
|
119
|
+
20
|
120
|
+
)
|
121
|
+
match.for_every_seat! do |seat|
|
122
|
+
match.for_every_hand! do
|
123
|
+
match.for_every_turn! do
|
124
|
+
next unless match.current_hand.next_action
|
125
|
+
|
126
|
+
from_player_message = match.current_hand.next_action.state
|
127
|
+
seat_taking_action = match.current_hand.next_action.seat
|
128
|
+
action = match.current_hand.next_action.action
|
129
|
+
|
130
|
+
action_that_should_be_sent = "#{from_player_message.to_s}:#{action.to_acpc}"
|
131
|
+
|
132
|
+
@connection.expect :write, nil, [action_that_should_be_sent]
|
133
|
+
|
134
|
+
BasicProxy.send_action @connection, from_player_message, action
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
70
141
|
end
|
data/spec/coverage/index.html
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
<img src="./assets/0.7.1/loading.gif" alt="loading"/>
|
15
15
|
</div>
|
16
16
|
<div id="wrapper" style="display:none;">
|
17
|
-
<div class="timestamp">Generated <abbr class="timeago" title="2013-05-
|
17
|
+
<div class="timestamp">Generated <abbr class="timeago" title="2013-05-16T14:30:23-06:00">2013-05-16T14:30:23-06:00</abbr></div>
|
18
18
|
<ul class="group_tabs"></ul>
|
19
19
|
|
20
20
|
<div id="content">
|
data/spec/dealer_stream_spec.rb
CHANGED
@@ -3,10 +3,9 @@ require_relative 'support/spec_helper'
|
|
3
3
|
|
4
4
|
require 'socket'
|
5
5
|
|
6
|
-
require 'acpc_poker_basic_proxy/
|
6
|
+
require 'acpc_poker_basic_proxy/dealer_stream'
|
7
7
|
|
8
8
|
include AcpcPokerBasicProxy
|
9
|
-
include CommunicationLogic
|
10
9
|
|
11
10
|
describe DealerStream do
|
12
11
|
after do
|
data/spec/support/spec_helper.rb
CHANGED
@@ -39,6 +39,31 @@ class MatchLog
|
|
39
39
|
|
40
40
|
attr_reader :results_file_name, :actions_file_name, :player_names, :dealer_log_directory
|
41
41
|
|
42
|
+
def self.all
|
43
|
+
[
|
44
|
+
MatchLog.new(
|
45
|
+
'2p.limit.h1000.r0.log',
|
46
|
+
'2p.limit.h1000.r0.actions.log',
|
47
|
+
['p1', 'p2']
|
48
|
+
),
|
49
|
+
MatchLog.new(
|
50
|
+
'2p.nolimit.h1000.r0.log',
|
51
|
+
'2p.nolimit.h1000.r0.actions.log',
|
52
|
+
['p1', 'p2']
|
53
|
+
),
|
54
|
+
MatchLog.new(
|
55
|
+
'3p.limit.h1000.r0.log',
|
56
|
+
'3p.limit.h1000.r0.actions.log',
|
57
|
+
['p1', 'p2', 'p3']
|
58
|
+
),
|
59
|
+
MatchLog.new(
|
60
|
+
'3p.nolimit.h1000.r0.log',
|
61
|
+
'3p.nolimit.h1000.r0.actions.log',
|
62
|
+
['p1', 'p2', 'p3']
|
63
|
+
)
|
64
|
+
]
|
65
|
+
end
|
66
|
+
|
42
67
|
def initialize(results_file_name, actions_file_name, player_names)
|
43
68
|
@results_file_name = results_file_name
|
44
69
|
@actions_file_name = actions_file_name
|
@@ -52,29 +77,4 @@ class MatchLog
|
|
52
77
|
def results_file_path
|
53
78
|
"#{DEALER_LOG_DIRECTORY}/#{@results_file_name}"
|
54
79
|
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def match_logs
|
58
|
-
[
|
59
|
-
MatchLog.new(
|
60
|
-
'2p.limit.h1000.r0.log',
|
61
|
-
'2p.limit.h1000.r0.actions.log',
|
62
|
-
['p1', 'p2']
|
63
|
-
),
|
64
|
-
MatchLog.new(
|
65
|
-
'2p.nolimit.h1000.r0.log',
|
66
|
-
'2p.nolimit.h1000.r0.actions.log',
|
67
|
-
['p1', 'p2']
|
68
|
-
),
|
69
|
-
MatchLog.new(
|
70
|
-
'3p.limit.h1000.r0.log',
|
71
|
-
'3p.limit.h1000.r0.actions.log',
|
72
|
-
['p1', 'p2', 'p3']
|
73
|
-
),
|
74
|
-
MatchLog.new(
|
75
|
-
'3p.nolimit.h1000.r0.log',
|
76
|
-
'3p.nolimit.h1000.r0.actions.log',
|
77
|
-
['p1', 'p2', 'p3']
|
78
|
-
)
|
79
|
-
]
|
80
80
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acpc_poker_basic_proxy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dustin Morrill
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: acpc_poker_types
|
@@ -145,15 +145,11 @@ extra_rdoc_files: []
|
|
145
145
|
files:
|
146
146
|
- lib/acpc_poker_basic_proxy.rb
|
147
147
|
- lib/acpc_poker_basic_proxy/basic_proxy.rb
|
148
|
-
- lib/acpc_poker_basic_proxy/communication_logic.rb
|
149
|
-
- lib/acpc_poker_basic_proxy/communication_logic/action_sender.rb
|
150
|
-
- lib/acpc_poker_basic_proxy/communication_logic/match_state_receiver.rb
|
151
|
-
- lib/acpc_poker_basic_proxy/communication_logic/dealer_stream.rb
|
152
148
|
- lib/acpc_poker_basic_proxy/version.rb
|
149
|
+
- lib/acpc_poker_basic_proxy/dealer_stream.rb
|
153
150
|
- Rakefile
|
154
151
|
- acpc_poker_basic_proxy.gemspec
|
155
152
|
- README.md
|
156
|
-
- spec/match_state_receiver_spec.rb
|
157
153
|
- spec/support/dealer_logs/3p.limit.h1000.r0.actions.log
|
158
154
|
- spec/support/dealer_logs/3p.limit.h1000.r0.log
|
159
155
|
- spec/support/dealer_logs/3p.nolimit.h1000.r0.actions.log
|
@@ -206,7 +202,6 @@ files:
|
|
206
202
|
- spec/coverage/assets/0.7.1/favicon_red.png
|
207
203
|
- spec/coverage/assets/0.7.1/loading.gif
|
208
204
|
- spec/dealer_stream_spec.rb
|
209
|
-
- spec/action_sender_spec.rb
|
210
205
|
homepage: https://github.com/dmorrill10/acpc_poker_basic_proxy
|
211
206
|
licenses: []
|
212
207
|
metadata: {}
|
@@ -231,7 +226,6 @@ signing_key:
|
|
231
226
|
specification_version: 4
|
232
227
|
summary: ACPC Poker Basic Proxy
|
233
228
|
test_files:
|
234
|
-
- spec/match_state_receiver_spec.rb
|
235
229
|
- spec/support/dealer_logs/3p.limit.h1000.r0.actions.log
|
236
230
|
- spec/support/dealer_logs/3p.limit.h1000.r0.log
|
237
231
|
- spec/support/dealer_logs/3p.nolimit.h1000.r0.actions.log
|
@@ -284,4 +278,3 @@ test_files:
|
|
284
278
|
- spec/coverage/assets/0.7.1/favicon_red.png
|
285
279
|
- spec/coverage/assets/0.7.1/loading.gif
|
286
280
|
- spec/dealer_stream_spec.rb
|
287
|
-
- spec/action_sender_spec.rb
|
@@ -1,45 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'acpc_poker_types/poker_action'
|
3
|
-
require 'acpc_poker_types/match_state'
|
4
|
-
|
5
|
-
require 'contextual_exceptions'
|
6
|
-
using ContextualExceptions::ClassRefinement
|
7
|
-
|
8
|
-
# Sends poker actions according to the ACPC protocol.
|
9
|
-
module AcpcPokerBasicProxy
|
10
|
-
module CommunicationLogic
|
11
|
-
class ActionSender
|
12
|
-
exceptions :illegal_action_format
|
13
|
-
|
14
|
-
CONCATENATED_MODIFIABLE_ACTIONS = AcpcPokerTypes::PokerAction::MODIFIABLE_ACTIONS.to_a.join
|
15
|
-
|
16
|
-
# Sends the given +action+ to through the given +connection+ in the ACPC
|
17
|
-
# format.
|
18
|
-
# @param [#write, #ready_to_write?] connection The connection through which the +action+
|
19
|
-
# should be sent.
|
20
|
-
# @param [#to_s] match_state The current match state.
|
21
|
-
# @param [#to_s] action The action to be sent through the +connection+.
|
22
|
-
# @return [Integer] The number of bytes written.
|
23
|
-
# @raise (see #validate_match_state)
|
24
|
-
# @raise (see #validate_action)
|
25
|
-
def self.send_action(connection, match_state, action)
|
26
|
-
validate_action action
|
27
|
-
|
28
|
-
full_action = "#{AcpcPokerTypes::MatchState.parse(match_state.to_s)}:#{action.to_s}"
|
29
|
-
connection.write full_action
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
# @raise IllegalActionFormat
|
35
|
-
def self.validate_action(action)
|
36
|
-
raise IllegalActionFormat unless self.valid_action?(action)
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.valid_action?(action)
|
40
|
-
action.to_s.match(/^[#{AcpcPokerTypes::PokerAction::CONCATONATED_ACTIONS}]$/) ||
|
41
|
-
action.to_s.match(/^[#{CONCATENATED_MODIFIABLE_ACTIONS}]\d+$/)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,150 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'delegate'
|
3
|
-
|
4
|
-
module AcpcPokerBasicProxy
|
5
|
-
module CommunicationLogic
|
6
|
-
module IoRefinement
|
7
|
-
refine IO do
|
8
|
-
# Checks if the socket is ready to be read from.
|
9
|
-
# @param [Integer] timeout_in_seconds Amount of time to wait for the sever to respond, in seconds. Must be positive or +nil+.
|
10
|
-
# @return [Boolean] +true+ if the socket is ready to be read from, +false+ otherwise.
|
11
|
-
def ready_to_read?(timeout_in_seconds=nil)
|
12
|
-
read_array = [self]
|
13
|
-
write_array = nil
|
14
|
-
error_array = nil
|
15
|
-
|
16
|
-
select?(read_array, write_array, error_array, timeout_in_seconds)
|
17
|
-
end
|
18
|
-
|
19
|
-
# Checks if the socket is ready to be written to.
|
20
|
-
# @param [Integer] timeout_in_seconds Amount of time to wait for the sever to respond, in seconds. Must be positive or +nil+.
|
21
|
-
# @return [Boolean] +true+ if the socket is ready to be written to, +false+ otherwise.
|
22
|
-
def ready_to_write?(timeout_in_seconds=nil)
|
23
|
-
read_array = nil
|
24
|
-
write_array = [self]
|
25
|
-
error_array = nil
|
26
|
-
|
27
|
-
select?(read_array, write_array, error_array, timeout_in_seconds)
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
# @see IO#select
|
33
|
-
def select?(read_array, write_array=[], error_array=[], timeout_in_seconds=nil)
|
34
|
-
IO.select(read_array, write_array, error_array, timeout_in_seconds) != nil
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
using AcpcPokerBasicProxy::CommunicationLogic::IoRefinement
|
41
|
-
|
42
|
-
require 'contextual_exceptions'
|
43
|
-
using ContextualExceptions::ClassRefinement
|
44
|
-
|
45
|
-
# Communication service to the ACPC Dealer.
|
46
|
-
# It acts solely as an abstraction of the communication protocol and
|
47
|
-
# implements the main Ruby communication interface through 'gets' and 'puts'
|
48
|
-
# methods.
|
49
|
-
module AcpcPokerBasicProxy
|
50
|
-
module CommunicationLogic
|
51
|
-
class DealerStream < DelegateClass(TCPSocket)
|
52
|
-
exceptions :unable_to_connect_to_dealer, :unable_to_write_to_dealer, :unable_to_get_from_dealer
|
53
|
-
|
54
|
-
# @return [String] The ACPC dealer version label.
|
55
|
-
VERSION_LABEL = 'VERSION'
|
56
|
-
|
57
|
-
# @return [Hash] The ACPC dealer version numbers.
|
58
|
-
VERSION_NUMBERS = {:major => 2, :minor => 0, :revision => 0}
|
59
|
-
|
60
|
-
# @return [String] Dealer specified string terminator.
|
61
|
-
TERMINATION_STRING = "\r\n"
|
62
|
-
|
63
|
-
# @param [Integer] port The port on which to connect to the dealer.
|
64
|
-
# @param [String] host_name The host on which the dealer is running. Defaults to 'localhost'
|
65
|
-
# @param [Integer] millisecond_response_timeout The dealer's response timeout, in milliseconds.
|
66
|
-
# @raise AcpcDealerConnectionError, UnableToWriteToDealer
|
67
|
-
def initialize(port, host_name='localhost', millisecond_response_timeout=nil)
|
68
|
-
begin
|
69
|
-
@dealer_socket = TCPSocket.new(host_name, port)
|
70
|
-
super @dealer_socket
|
71
|
-
|
72
|
-
@response_timeout_in_seconds = if millisecond_response_timeout
|
73
|
-
millisecond_response_timeout/(10**3).to_r
|
74
|
-
else
|
75
|
-
nil
|
76
|
-
end
|
77
|
-
|
78
|
-
send_version_string_to_dealer
|
79
|
-
rescue UnableToWriteToDealer
|
80
|
-
raise
|
81
|
-
rescue Errno::ECONNREFUSED => e
|
82
|
-
handle_error UnableToConnectToDealer, "Unable to connect to the dealer on #{host_name} through port #{port}", e
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# Retrieves a string from the dealer.
|
87
|
-
#
|
88
|
-
# @return [String] A string from the dealer.
|
89
|
-
# @raise UnableToGetFromDealer
|
90
|
-
def gets
|
91
|
-
begin
|
92
|
-
string_from_dealer
|
93
|
-
rescue => e
|
94
|
-
handle_error UnableToGetFromDealer, "Unable to get a string from the dealer", e
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Sends a given +string+ to the dealer.
|
99
|
-
#
|
100
|
-
# @param [String] string The string to send.
|
101
|
-
# @return (see #send_string_to_dealer)
|
102
|
-
# @raise UnableToWriteToDealer
|
103
|
-
def write(string)
|
104
|
-
begin
|
105
|
-
num_bytes_written = send_string_to_dealer string
|
106
|
-
rescue => e
|
107
|
-
handle_error UnableToWriteToDealer, "Unable to send the string, \"#{string}\", to the dealer", e
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# @see TCPSocket#ready_to_write?
|
112
|
-
def ready_to_write?
|
113
|
-
@dealer_socket.ready_to_write? @response_timeout_in_seconds
|
114
|
-
end
|
115
|
-
|
116
|
-
# @see TCPSocket#ready_to_read?
|
117
|
-
def ready_to_read?
|
118
|
-
@dealer_socket.ready_to_read? @response_timeout_in_seconds
|
119
|
-
end
|
120
|
-
|
121
|
-
private
|
122
|
-
|
123
|
-
def handle_error(exception, message, context_exception)
|
124
|
-
close if @dealer_socket && !closed?
|
125
|
-
raise exception.with_context(message, context_exception)
|
126
|
-
end
|
127
|
-
|
128
|
-
# @return (see #send_string_to_dealer)
|
129
|
-
def send_version_string_to_dealer
|
130
|
-
version_string = "#{VERSION_LABEL}:#{VERSION_NUMBERS[:major]}.#{VERSION_NUMBERS[:minor]}.#{VERSION_NUMBERS[:revision]}"
|
131
|
-
begin
|
132
|
-
send_string_to_dealer version_string
|
133
|
-
rescue => e
|
134
|
-
handle_error UnableToWriteToDealer, "Unable to send version string, \"#{version_string}\", to the dealer", e
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# @return [Integer] The number of bytes written to the dealer.
|
139
|
-
def send_string_to_dealer(string)
|
140
|
-
raise unless ready_to_write?
|
141
|
-
@dealer_socket.write(string + TERMINATION_STRING)
|
142
|
-
end
|
143
|
-
|
144
|
-
def string_from_dealer
|
145
|
-
raise unless ready_to_read?
|
146
|
-
@dealer_socket.gets.chomp
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'acpc_poker_types/match_state'
|
3
|
-
|
4
|
-
# Receives match state strings.
|
5
|
-
module AcpcPokerBasicProxy
|
6
|
-
module CommunicationLogic
|
7
|
-
class MatchStateReceiver
|
8
|
-
|
9
|
-
# Receives a match state string from the given +connection+.
|
10
|
-
# @param [#gets] connection The connection from which a match state string should be received.
|
11
|
-
# @return [MatchState] The match state string that was received from the +connection+ or +nil+ if none could be received.
|
12
|
-
def self.receive_match_state(connection)
|
13
|
-
raw_match_state = connection.gets
|
14
|
-
AcpcPokerTypes::MatchState.parse raw_match_state
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,8 +0,0 @@
|
|
1
|
-
require 'acpc_poker_basic_proxy/communication_logic/dealer_stream'
|
2
|
-
require 'acpc_poker_basic_proxy/communication_logic/match_state_receiver'
|
3
|
-
require 'acpc_poker_basic_proxy/communication_logic/action_sender'
|
4
|
-
|
5
|
-
module AcpcPokerBasicProxy
|
6
|
-
module CommunicationLogic
|
7
|
-
end
|
8
|
-
end
|
data/spec/action_sender_spec.rb
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
|
2
|
-
require_relative 'support/spec_helper'
|
3
|
-
|
4
|
-
require 'acpc_poker_types/acpc_dealer_data/poker_match_data'
|
5
|
-
require 'acpc_poker_types/poker_action'
|
6
|
-
require 'acpc_poker_types/match_state'
|
7
|
-
require 'acpc_dealer'
|
8
|
-
|
9
|
-
require 'acpc_poker_basic_proxy/communication_logic/action_sender'
|
10
|
-
|
11
|
-
include AcpcPokerBasicProxy::CommunicationLogic
|
12
|
-
include AcpcPokerTypes
|
13
|
-
|
14
|
-
describe ActionSender do
|
15
|
-
before(:each) do
|
16
|
-
@connection = MiniTest::Mock.new
|
17
|
-
@match_state = AcpcDealerData::PokerMatchData.parse_files(
|
18
|
-
match_logs[0].actions_file_path,
|
19
|
-
match_logs[0].results_file_path,
|
20
|
-
match_logs[0].player_names,
|
21
|
-
AcpcDealer::DEALER_DIRECTORY,
|
22
|
-
1
|
23
|
-
).data[0].data[0].action_message.state
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "#send_action" do
|
27
|
-
it 'does not send an illegal action and raises an exception' do
|
28
|
-
-> do
|
29
|
-
ActionSender.send_action(@connection, @match_state, 'illegal action format')
|
30
|
-
end.must_raise ActionSender::IllegalActionFormat
|
31
|
-
end
|
32
|
-
it 'raises an exception if the given match state does not have the proper format' do
|
33
|
-
-> do
|
34
|
-
ActionSender.send_action(@connection, 'illegal match state format', PokerAction::CALL)
|
35
|
-
end.must_raise MatchState::IncompleteMatchState
|
36
|
-
end
|
37
|
-
it 'can send all legal actions through the provided connection without a modifier' do
|
38
|
-
PokerAction::ACTIONS.each do |action|
|
39
|
-
action_that_should_be_sent = @match_state.to_s + ":#{action}"
|
40
|
-
@connection.expect :write, nil, [action_that_should_be_sent]
|
41
|
-
|
42
|
-
ActionSender.send_action @connection, @match_state, action
|
43
|
-
end
|
44
|
-
end
|
45
|
-
it 'does not send legal unmodifiable actions that have a modifier and raises an exception' do
|
46
|
-
(PokerAction::ACTIONS - PokerAction::MODIFIABLE_ACTIONS).each do |unmodifiable_action|
|
47
|
-
-> do
|
48
|
-
ActionSender.send_action(@connection, @match_state, unmodifiable_action + 9001.to_s)
|
49
|
-
end.must_raise ActionSender::IllegalActionFormat
|
50
|
-
end
|
51
|
-
end
|
52
|
-
it 'can send all legal modifiable actions through the provided connection with a modifier' do
|
53
|
-
PokerAction::MODIFIABLE_ACTIONS.each do |action|
|
54
|
-
arbitrary_modifier = 9001
|
55
|
-
action_string = action + arbitrary_modifier.to_s
|
56
|
-
action_that_should_be_sent = @match_state.to_s + ":#{action_string}"
|
57
|
-
@connection.expect :write, nil, [action_that_should_be_sent]
|
58
|
-
|
59
|
-
ActionSender.send_action @connection, @match_state, action_string
|
60
|
-
end
|
61
|
-
end
|
62
|
-
it 'works for all test data examples' do
|
63
|
-
match_logs.each do |log_description|
|
64
|
-
match = AcpcDealerData::PokerMatchData.parse_files(
|
65
|
-
log_description.actions_file_path,
|
66
|
-
log_description.results_file_path,
|
67
|
-
log_description.player_names,
|
68
|
-
AcpcDealer::DEALER_DIRECTORY,
|
69
|
-
60
|
70
|
-
)
|
71
|
-
match.for_every_seat! do |seat|
|
72
|
-
match.for_every_hand! do
|
73
|
-
match.for_every_turn! do
|
74
|
-
next unless match.current_hand.next_action
|
75
|
-
|
76
|
-
from_player_message = match.current_hand.next_action.state
|
77
|
-
seat_taking_action = match.current_hand.next_action.seat
|
78
|
-
action = match.current_hand.next_action.action
|
79
|
-
|
80
|
-
action_that_should_be_sent = "#{from_player_message.to_s}:#{action.to_acpc}"
|
81
|
-
|
82
|
-
@connection.expect :write, nil, [action_that_should_be_sent]
|
83
|
-
|
84
|
-
ActionSender.send_action @connection, from_player_message, action
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
|
2
|
-
require_relative 'support/spec_helper'
|
3
|
-
|
4
|
-
require 'acpc_dealer'
|
5
|
-
require 'acpc_poker_types'
|
6
|
-
|
7
|
-
require 'acpc_poker_basic_proxy/communication_logic/match_state_receiver'
|
8
|
-
require 'acpc_poker_basic_proxy/communication_logic/dealer_stream'
|
9
|
-
|
10
|
-
describe AcpcPokerBasicProxy::CommunicationLogic::MatchStateReceiver do
|
11
|
-
before(:each) do
|
12
|
-
@connection = MiniTest::Mock.new
|
13
|
-
end
|
14
|
-
|
15
|
-
describe "#receive_matchstate_string" do
|
16
|
-
it 'receives matchstate strings properly' do
|
17
|
-
match_logs.each do |log_description|
|
18
|
-
match = AcpcPokerTypes::AcpcDealerData::PokerMatchData.parse_files(
|
19
|
-
log_description.actions_file_path,
|
20
|
-
log_description.results_file_path,
|
21
|
-
log_description.player_names,
|
22
|
-
AcpcDealer::DEALER_DIRECTORY,
|
23
|
-
60
|
24
|
-
)
|
25
|
-
match.for_every_seat! do |seat|
|
26
|
-
match.for_every_hand! do
|
27
|
-
match.for_every_turn! do
|
28
|
-
@connection.expect(:gets, match.current_hand.current_match_state.to_s)
|
29
|
-
|
30
|
-
AcpcPokerBasicProxy::CommunicationLogic::MatchStateReceiver
|
31
|
-
.receive_match_state(@connection).must_equal match.current_hand.current_match_state
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|