OSDN Git Service

New feature: Zero least time per move.
[shogi-server/shogi-server.git] / test / baseclient.rb
1 require 'socket'
2 require 'stringio'
3 require 'thread'
4 require 'test/unit'
5
6 class SocketPlayer
7   def initialize(game_name, name, sente)
8     @game_name = game_name
9     @name = "%s_%s" % [game_name, name]
10     if sente == "*"
11       @turn_mark = sente
12     else
13       @turn_mark = sente ? "+" : "-"
14     end
15     @received_moves = 0
16     @socket = nil
17     @message = ""
18     @mutex = Mutex.new
19     @login_command = "LOGIN #{@name} dummy x1"
20   end
21   attr_reader :message
22   attr_accessor :login_command
23
24   def connect
25     port = 4000
26     @socket = TCPSocket.open("localhost", port)
27     @socket.sync = true
28     @message = ""
29     reader
30   end
31
32   def close
33     @socket.close if @socket && !@socket.closed?
34   end
35
36   def reader
37     @thread = Thread.new do
38       Thread.pass
39       loop do 
40 #        break if @socket.closed?
41         if r = select([@socket], nil, nil, 10)
42           str = r[0].first.gets
43           break if str.nil?
44           @mutex.synchronize do
45             if %r!^[\+\-]\d{4}\w{2},T\d+$! =~ str
46                 @received_moves += 1
47             end
48             @message << str
49           end
50         else
51           raise "timed out"
52         end
53       end
54     end
55   end
56
57   def stop_reader
58     @thread.kill if @thread
59   end
60
61   def wait(reg)
62     loop do 
63       @mutex.synchronize do
64         return if reg =~ @message
65       end
66       sleep 0.01
67     end
68   end
69
70   def wait_nmoves(n)
71     loop do
72       @mutex.synchronize do
73         return if @received_moves == n
74       end
75       sleep 0.01
76     end
77   end
78
79   def login
80     str = @login_command
81     $stderr.puts str if $DEBUG
82     @socket.puts str
83     wait %r!^LOGIN!
84   end
85
86   def game
87     str = "%%GAME #{@game_name}-1500-0 #{@turn_mark}"
88     $stderr.puts str if $DEBUG
89     @socket.puts str
90   end
91
92   def challenge
93     str = "%%CHALLENGE #{@game_name}-1500-0 #{@turn_mark}"
94     $stderr.puts str if $DEBUG
95     @socket.puts str
96   end
97
98   def wait_game
99     wait %r!^END Game_Summary!
100   end
101
102   def agree
103     @socket.puts "AGREE"
104   end
105
106   def wait_agree
107     wait %r!^START:!
108   end
109
110   def move(m)
111     @socket.puts m
112   end
113   def puts(m)
114     @socket.puts m
115   end
116
117   def toryo
118     @socket.puts "%TORYO"
119   end
120
121   def wait_finish
122     wait %r!^#(WIN|LOSE)!
123   end
124
125   def logout
126     stop_reader
127     @socket.puts "LOGOUT"
128   end
129
130 end
131
132 class SocketCSAPlayer < SocketPlayer
133   def initialize(game_name, name, sente)
134     super
135     @login_command = "LOGIN #{@name} dummy"
136   end
137
138   def login
139     str = @login_command
140     $stderr.puts str if $DEBUG
141     @socket.puts str
142     wait %r!^LOGIN!
143   end
144 end
145
146
147
148
149
150 class BaseClient < Test::Unit::TestCase
151   attr_accessor :game_name, :p1_name, :p2_name
152
153   def set_name
154     @game_name = self.class.name
155     @p1_name = "sente"
156     @p2_name = "gote"
157   end
158
159   def set_player
160     @p1 = SocketPlayer.new @game_name, @p1_name, true
161     @p2 = SocketPlayer.new @game_name, @p2_name, false
162   end
163
164   def setup
165     set_name
166     set_player
167     @nmoves = 0
168   end
169   attr_reader :src1, :src2
170
171   def teardown
172     @p1.close
173     @p2.close
174   end
175
176   def test_dummy
177     assert true
178   end
179
180   def login
181     sleep 0.1
182     @p1.connect
183     sleep 0.1
184     @p2.connect
185     sleep 0.1
186     @p1.login
187     sleep 0.1
188     @p2.login
189     sleep 0.1
190     @p1.game
191     sleep 0.1
192     @p2.game
193     sleep 0.1
194     @p1.wait_game
195     sleep 0.1
196     @p2.wait_game
197   end
198
199   def agree
200     sleep 0.1
201     @p1.agree
202     sleep 0.1
203     @p2.agree
204     sleep 0.1
205     @p1.wait_agree
206     sleep 0.1
207     @p2.wait_agree
208   end
209
210   def handshake
211     login
212     agree
213
214     move "+2726FU"
215     move "-3334FU"
216    
217     yield if block_given?
218
219     logout12
220     [@p1.message, @p2.message]
221   end
222
223   def move(m)
224     case m
225     when /^\+/
226       move1(m)
227     when /^\-/
228       move2(m)
229     else
230       raise "do not reach!"
231     end
232   end
233
234   def move1(m)
235     @p1.move m
236     @nmoves += 1
237     @p2.wait_nmoves @nmoves
238   end
239
240   def move2(m)
241     @p2.move m
242     @nmoves += 1
243     @p1.wait_nmoves @nmoves
244   end
245
246   def cmd(s)
247     @p1.move s
248     return @p1.message
249   end
250
251   def cmd2(s)
252     @p2.move s
253     return @p2.message
254   end
255
256   def wait_finish
257     @p1.wait_finish
258     @p2.wait_finish
259   end
260
261   def logout12
262     @p1.logout
263     @p2.logout
264   end
265
266   def logout21
267     @p2.logout
268     @p1.logout
269   end
270
271 end
272
273
274 class ReadFileClient < BaseClient
275   def filepath(csa_file_name)
276     return File.join(File.dirname(__FILE__), "csa", csa_file_name)
277   end
278
279   def handshake(csa)
280     login
281     agree
282
283     csa_io = StringIO.new(csa)
284     while line = csa_io.gets do
285       case line
286       when /^[\+\-]\d{4}\w{2}/
287         s = $&
288         $stderr.puts s if $DEBUG
289         move s
290       end
291     end
292   end
293 end # ReadFileClient
294
295
296 class CSABaseClient < BaseClient
297   ##
298   # In CSA mode, the server decides sente or gote at random; and sockets are closed
299   # just after the game ends (i.e. %TORYO is sent)
300   # 
301   def set_player
302     @p1 = SocketCSAPlayer.new @game_name, @p1_name, true
303     @p2 = SocketCSAPlayer.new @game_name, @p2_name, false
304   end
305
306   def teardown
307     @p1.stop_reader
308     @p2.stop_reader
309     super
310   end
311
312   def handshake
313     @p1.connect
314     @p2.connect
315     @p1.login
316     @p2.login
317     agree
318
319     if /Your_Turn:\+/ =~ @p1.message
320     else
321       @p1,@p2 = @p2,@p1
322     end
323
324     move "+7776FU"
325     move "-3334FU"
326     yield if block_given?
327     
328     [@p1.message, @p2.message]
329   end
330
331 end # CSABaseClient
332
333