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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c75b02eafb76e6ecd4895bad73bf7abdeb0377e1
4
- data.tar.gz: 13699952e83f7ed0812880b7de60eed4ede2bacb
3
+ metadata.gz: 424876fe9b5e2498c3b29df7337b975d8f77aa9f
4
+ data.tar.gz: 5a49ff106ad812d598577c778c5801ab44d37f89
5
5
  SHA512:
6
- metadata.gz: d093f6987211ffa930f1231edc270861ac4afd19b360f31f0b2da6da22a49a6f6726687a5165c6458a2a67d44c38e83abcfbb7d9fbc3a3c8e12ce0dc4c5b502d
7
- data.tar.gz: c66b69e39fa01f95b1b5aeb7eecd6b073bf6a9cd3da7f6320455b209720c553a6f7c8d47d519448a420b9024c7e2f7111adc7ea2a3254e3b0032c0cd5d0e25a1
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/communication_logic/dealer_stream'
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 = CommunicationLogic::DealerStream.new dealer_information.port_number, dealer_information.host_name, dealer_information.millisecond_response_timeout
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
- CommunicationLogic::ActionSender.send_action @dealer_communicator, @match_state, action
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 = CommunicationLogic::MatchStateReceiver.receive_match_state @dealer_communicator
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
@@ -1,3 +1,3 @@
1
1
  module AcpcPokerBasicProxy
2
- VERSION = "2.0.0"
2
+ VERSION = "3.0.0"
3
3
  end
@@ -1,7 +1,7 @@
1
1
  require 'acpc_poker_basic_proxy/version'
2
2
 
3
3
  require 'acpc_poker_basic_proxy/basic_proxy'
4
- require 'acpc_poker_basic_proxy/communication_logic'
4
+ require 'acpc_poker_basic_proxy/dealer_stream'
5
5
 
6
6
  module AcpcPokerBasicProxy
7
7
  end
@@ -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
- match_logs.each do |log_description|
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
- ActionSender.expects(:send_action).once.with(@dealer_communicator, match_state.to_s, action)
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
@@ -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-08T18:52:12-06:00">2013-05-08T18:52:12-06:00</abbr></div>
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">
@@ -3,10 +3,9 @@ require_relative 'support/spec_helper'
3
3
 
4
4
  require 'socket'
5
5
 
6
- require 'acpc_poker_basic_proxy/communication_logic/dealer_stream'
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
@@ -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: 2.0.0
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-09 00:00:00.000000000 Z
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
@@ -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