OSDN Git Service

Simplify estimated rate of unrated players (less memory).
[shogi-server/shogi-server.git] / test / baseclient.rb
index eea2969..39ad817 100644 (file)
 require 'socket'
 require 'stringio'
+require 'thread'
 require 'test/unit'
 
+class SocketPlayer
+  def initialize(game_name, name, sente)
+    @game_name = game_name
+    @name = "%s_%s" % [game_name, name]
+    if sente == "*"
+      @turn_mark = sente
+    else
+      @turn_mark = sente ? "+" : "-"
+    end
+    @received_moves = 0
+    @socket = nil
+    @message = ""
+    @mutex = Mutex.new
+    @login_command = "LOGIN #{@name} dummy x1"
+  end
+  attr_reader :message
+  attr_accessor :login_command
+
+  def connect
+    port = 4000
+    @socket = TCPSocket.open("localhost", port)
+    @socket.sync = true
+    @message = ""
+    reader
+  end
+
+  def close
+    @socket.close if @socket && !@socket.closed?
+  end
+
+  def reader
+    @thread = Thread.new do
+      Thread.pass
+      loop do 
+#        break if @socket.closed?
+        if r = select([@socket], nil, nil, 10)
+          str = r[0].first.gets
+          break if str.nil?
+          @mutex.synchronize do
+            if %r!^[\+\-]\d{4}\w{2},T\d+$! =~ str
+                @received_moves += 1
+            end
+            @message << str
+          end
+        else
+          raise "timed out"
+        end
+      end
+    end
+  end
+
+  def stop_reader
+    @thread.kill if @thread
+  end
+
+  def wait(reg)
+    loop do 
+      @mutex.synchronize do
+        return if reg =~ @message
+      end
+      sleep 0.01
+    end
+  end
+
+  def wait_nmoves(n)
+    loop do
+      @mutex.synchronize do
+        return if @received_moves == n
+      end
+      sleep 0.01
+    end
+  end
+
+  def login
+    str = @login_command
+    $stderr.puts str if $DEBUG
+    @socket.puts str
+    wait %r!^LOGIN!
+  end
+
+  def game
+    str = "%%GAME #{@game_name}-1500-0 #{@turn_mark}"
+    $stderr.puts str if $DEBUG
+    @socket.puts str
+  end
+
+  def challenge
+    str = "%%CHALLENGE #{@game_name}-1500-0 #{@turn_mark}"
+    $stderr.puts str if $DEBUG
+    @socket.puts str
+  end
+
+  def wait_game
+    wait %r!^END Game_Summary!
+  end
+
+  def agree
+    @socket.puts "AGREE"
+  end
+
+  def wait_agree
+    wait %r!^START:!
+  end
+
+  def move(m)
+    @socket.puts m
+  end
+  def puts(m)
+    @socket.puts m
+  end
+
+  def toryo
+    @socket.puts "%TORYO"
+  end
+
+  def wait_finish
+    wait %r!^#(WIN|LOSE)!
+  end
+
+  def logout
+    stop_reader
+    @socket.puts "LOGOUT"
+  end
+
+end
+
+class SocketCSAPlayer < SocketPlayer
+  def initialize(game_name, name, sente)
+    super
+    @login_command = "LOGIN #{@name} dummy"
+  end
+
+  def login
+    str = @login_command
+    $stderr.puts str if $DEBUG
+    @socket.puts str
+    wait %r!^LOGIN!
+  end
+end
+
+
+
+
+
 class BaseClient < Test::Unit::TestCase
+  attr_accessor :game_name, :p1_name, :p2_name
+
+  def set_name
+    @game_name = self.class.name
+    @p1_name = "sente"
+    @p2_name = "gote"
+  end
+
+  def set_player
+    @p1 = SocketPlayer.new @game_name, @p1_name, true
+    @p2 = SocketPlayer.new @game_name, @p2_name, false
+  end
+
   def setup
-    port = 4000
-    params = {"Host" => "localhost", "Port" => port, "Prompt" => //}
-    @socket1 = TCPSocket.open("localhost", port)
-    @socket2 = TCPSocket.open("localhost", port)
+    set_name
+    set_player
+    @nmoves = 0
   end
+  attr_reader :src1, :src2
 
   def teardown
-    @socket1.close
-    @socket2.close
+    @p1.close
+    @p2.close
+  end
+
+  def test_dummy
+    assert true
   end
 
   def login
-    classname = self.class.name
-    cmd "LOGIN sente#{classname} dummy x1"
-    cmd "%%GAME test#{classname}-1500-0 +"
-    
-    cmd2 "LOGIN gote#{classname} dummy2 x1"
-    cmd2 "%%CHALLENGE test#{classname}-1500-0 -"
+    @p1.connect
+    @p2.connect
+    @p1.login
+    @p2.login
+    @p1.game
+    @p2.game
+    @p1.wait_game
+    @p2.wait_game
   end
 
   def agree
-    cmd  "AGREE"
-    sleep 0.5
-    cmd2 "AGREE"
+    @p1.agree
+    @p2.agree
+    @p1.wait_agree
+    @p2.wait_agree
   end
 
   def handshake
     login
-
-    sleep 2 # to wait for game matching
-
     agree
 
-    cmd  "+2726FU"
-    cmd2 "-3334FU"
+    move "+2726FU"
+    move "-3334FU"
    
     yield if block_given?
-    sleep 2
-    result  = cmd  "LOGOUT"
-    result2 = cmd2 "LOGOUT"
-    result  += read_nonblock(@socket1)
-    result2 += read_nonblock(@socket2)
-    [result, result2]
-  end
-
-  def read_nonblock(io)
-    sleep 0.05  
-    str = ""
-    begin
-      loop do   
-        str << io.read_nonblock(64)
-      end
-    rescue Errno::EAGAIN
-      # do nothing
-    rescue EOFError
-      # do nothing
+
+    logout12
+    [@p1.message, @p2.message]
+  end
+
+  def move(m)
+    case m
+    when /^\+/
+      move1(m)
+    when /^\-/
+      move2(m)
+    else
+      raise "do not reach!"
     end
-    str
+  end
+
+  def move1(m)
+    @p1.move m
+    @nmoves += 1
+    @p2.wait_nmoves @nmoves
+  end
+
+  def move2(m)
+    @p2.move m
+    @nmoves += 1
+    @p1.wait_nmoves @nmoves
   end
 
   def cmd(s)
-    # read the previous return
-    str = read_nonblock(@socket1)
-    @socket1.puts s if s && ! @socket1.closed?
-    str
+    @p1.move s
+    return @p1.message
   end
 
   def cmd2(s)
-    # read the previous return
-    str = read_nonblock(@socket2)
-    @socket2.puts s if s && ! @socket2.closed?
-    str
+    @p2.move s
+    return @p2.message
+  end
+
+  def wait_finish
+    @p1.wait_finish
+    @p2.wait_finish
   end
 
   def logout12
-    cmd  "LOGOUT"
-    cmd2 "LOGOUT"
-    sleep 1
+    @p1.logout
+    @p2.logout
   end
 
   def logout21
-    cmd2 "LOGOUT"
-    cmd  "LOGOUT"
-    sleep 1
+    @p2.logout
+    @p1.logout
   end
 
-  def test_dummy
-    assert true
-  end
 end
 
 
@@ -103,18 +266,56 @@ class ReadFileClient < BaseClient
 
   def handshake(csa)
     login
-    sleep 1
     agree
-    sleep 1
 
     csa_io = StringIO.new(csa)
     while line = csa_io.gets do
       case line
-      when /^\+\d{4}\w{2}/
-        cmd $&
-      when /^\-\d{4}\w{2}/
-        cmd2 $&
+      when /^[\+\-]\d{4}\w{2}/
+        s = $&
+        $stderr.puts s if $DEBUG
+        move s
       end
     end
   end
 end # ReadFileClient
+
+
+class CSABaseClient < BaseClient
+  ##
+  # In CSA mode, the server decides sente or gote at random; and sockets are closed
+  # just after the game ends (i.e. %TORYO is sent)
+  # 
+  def set_player
+    @p1 = SocketCSAPlayer.new @game_name, @p1_name, true
+    @p2 = SocketCSAPlayer.new @game_name, @p2_name, false
+  end
+
+  def teardown
+    @p1.stop_reader
+    @p2.stop_reader
+    super
+  end
+
+  def handshake
+    @p1.connect
+    @p2.connect
+    @p1.login
+    @p2.login
+    agree
+
+    if /Your_Turn:\+/ =~ @p1.message
+    else
+      @p1,@p2 = @p2,@p1
+    end
+
+    move "+7776FU"
+    move "-3334FU"
+    yield if block_given?
+    
+    [@p1.message, @p2.message]
+  end
+
+end # CSABaseClient
+
+