acpc_poker_basic_proxy 2.0.0 → 3.0.0

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