OSDN Git Service

New feature: Zero least time per move.
authorDaigo Moriwaki <daigo@debian.org>
Mon, 13 Oct 2014 07:19:58 +0000 (16:19 +0900)
committerDaigo Moriwaki <daigo@debian.org>
Mon, 12 Jan 2015 12:54:42 +0000 (21:54 +0900)
New command line option: --least-time-per-move n

changelog
shogi-server
shogi_server.rb
shogi_server/game.rb
shogi_server/time_clock.rb
test/TC_ALL.rb
test/TC_fork.rb
test/TC_game.rb
test/TC_game_least_0.rb [new file with mode: 0644]
test/TC_time_clock.rb
test/baseclient.rb

index 0d877cc..25da4bb 100644 (file)
--- a/changelog
+++ b/changelog
            - A new log summary type, "max_moves_draw", has been assigned for games
              drawing with max moves.
              'summary:max_moves_draw:name_sente draw:name_gote draw
+         - Least time per move:
+           - New command line option: --least-time-per-move n
+             This opotion specifies a least time in second per move, which
+             was previously 1 sec but is now by default 1, meaning that a
+             decimal fraction of time for a move will be truncated.
 
 2014-07-19  Daigo Moriwaki <daigo at debian dot org>
 
index a20c410..d6695b7 100755 (executable)
@@ -76,6 +76,10 @@ OPTIONS
         port_number
                 a port number for the server to listen. 
                 4081 is often used.
+        --least-time-per-move n
+                Least time in second per move: 0, 1 (default 1).
+                  - 0: The new rule that CSA introduced in November 2014.
+                  - 1: The old rule before it.
         --max-moves n
                 when a game with the n-th move played does not end, make the game a draw.
                 Default 256. 0 disables this feature.
@@ -194,11 +198,12 @@ end
 def parse_command_line
   options = Hash::new
   parser = GetoptLong.new(
-    ["--daemon",            GetoptLong::REQUIRED_ARGUMENT],
-    ["--floodgate-games",   GetoptLong::REQUIRED_ARGUMENT],
-    ["--max-moves",         GetoptLong::REQUIRED_ARGUMENT],
-    ["--pid-file",          GetoptLong::REQUIRED_ARGUMENT],
-    ["--player-log-dir",    GetoptLong::REQUIRED_ARGUMENT])
+    ["--daemon",              GetoptLong::REQUIRED_ARGUMENT],
+    ["--floodgate-games",     GetoptLong::REQUIRED_ARGUMENT],
+    ["--least-time-per-move", GetoptLong::REQUIRED_ARGUMENT],
+    ["--max-moves",           GetoptLong::REQUIRED_ARGUMENT],
+    ["--pid-file",            GetoptLong::REQUIRED_ARGUMENT],
+    ["--player-log-dir",      GetoptLong::REQUIRED_ARGUMENT])
   parser.quiet = true
   begin
     parser.each_option do |name, arg|
@@ -270,6 +275,9 @@ def check_command_line
 
   $options["max-moves"] ||= 256
   $options["max-moves"] = $options["max-moves"].to_i
+
+  $options["least-time-per-move"] ||= 1
+  $options["least-time-per-move"] = $options["least-time-per-move"].to_i
 end
 
 # See if a file can be created in the directory.
index f1161c2..dd227ae 100644 (file)
@@ -49,7 +49,6 @@ Max_Identifier_Length = 32
 Default_Timeout = 60            # for single socket operation
 Default_Game_Name = "default-1500-0"
 One_Time = 10
-Least_Time_Per_Move = 1
 Login_Time = 300                # time for LOGIN
 Revision = "20131215"
 
index a10cb65..4ddd121 100644 (file)
@@ -71,7 +71,7 @@ class Game
       @total_time = $1.to_i
       @byoyomi = $2.to_i
 
-      @time_clock = TimeClock::factory(Least_Time_Per_Move, @game_name)
+      @time_clock = TimeClock::factory($options["least-time-per-move"], @game_name)
     end
 
     if (player0.sente)
@@ -369,7 +369,7 @@ BEGIN Time
 Time_Unit:#{@time_clock.time_unit}
 Total_Time:#{@total_time}
 Byoyomi:#{@byoyomi}
-Least_Time_Per_Move:#{Least_Time_Per_Move}
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
 Remaining_Time+:#{@sente.mytime}
 Remaining_Time-:#{@gote.mytime}
 Last_Move:#{@last_move}
@@ -403,7 +403,7 @@ BEGIN Time
 Time_Unit:#{@time_clock.time_unit}
 Total_Time:#{@total_time}
 Byoyomi:#{@byoyomi}
-Least_Time_Per_Move:#{Least_Time_Per_Move}
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
 END Time
 BEGIN Position
 #{@board.initial_string.chomp}
index 6426fda..1198042 100644 (file)
@@ -36,7 +36,11 @@ class TimeClock
     if (byoyomi_str == "060")
       @time_clock = StopWatchClock.new(least_time_per_move, total_time, byoyomi)
     else
-      @time_clock = ChessClock.new(least_time_per_move, total_time, byoyomi)
+      if least_time_per_move == 0
+        @time_clock = ChessClockWithLeastZero.new(least_time_per_move, total_time, byoyomi)
+      else
+        @time_clock = ChessClock.new(least_time_per_move, total_time, byoyomi)
+      end
     end
   end
 
@@ -48,7 +52,7 @@ class TimeClock
 
   # Returns thinking time duration
   #
-  def time_duration(start_time, end_time)
+  def time_duration(mytime, start_time, end_time)
     # implement this
     return 9999999
   end
@@ -69,7 +73,7 @@ class TimeClock
   # Updates a player's remaining time and returns thinking time.
   #
   def process_time(player, start_time, end_time)
-    t = time_duration(start_time, end_time)
+    t = time_duration(player.mytime, start_time, end_time)
     
     player.mytime -= t
     if (player.mytime < 0)
@@ -87,12 +91,12 @@ class ChessClock < TimeClock
     super
   end
 
-  def time_duration(start_time, end_time)
+  def time_duration(mytime, start_time, end_time)
     return [(end_time - start_time).floor, @least_time_per_move].max
   end
 
   def timeout?(player, start_time, end_time)
-    t = time_duration(start_time, end_time)
+    t = time_duration(player.mytime, start_time, end_time)
 
     if ((player.mytime - t <= -@byoyomi) && 
         ((@total_time > 0) || (@byoyomi > 0)))
@@ -107,6 +111,42 @@ class ChessClock < TimeClock
   end
 end
 
+# Calculates thinking time with chess clock, truncating decimal seconds for
+# thinking time. This is a new rule that CSA introduced in November 2014.
+#
+# least_time_per_move should be 0.
+# byoyomi should be more than 0.
+#
+class ChessClockWithLeastZero < ChessClock
+  def initialize(least_time_per_move, total_time, byoyomi)
+    if least_time_per_move != 0
+      raise ArgumentError, "least_time_per_move #{least_time_per_move} should be 0."
+    end
+    super
+  end
+
+  def time_duration(mytime, start_time, end_time)
+    t = end_time - start_time
+    if @byoyomi > 0
+      # If the remaining thinking time covers the duration t, floor it.
+      if mytime + @byoyomi - t > 0
+        t = t.floor
+      else
+        t = t.ceil
+      end
+    else
+      # Thinking time only
+      t = t.floor
+    end
+
+    return t
+  end
+
+  def to_s
+    return "ChessClockWithLeastZero: LeastTimePerMove %d; TotalTime %d; Byoyomi %d" % [@least_time_per_move, @total_time, @byoyomi]
+  end
+end
+
 class StopWatchClock < TimeClock
   def initialize(least_time_per_move, total_time, byoyomi)
     super
@@ -116,13 +156,13 @@ class StopWatchClock < TimeClock
     return "1min"
   end
 
-  def time_duration(start_time, end_time)
+  def time_duration(mytime, start_time, end_time)
     t = [(end_time - start_time).floor, @least_time_per_move].max
     return (t / @byoyomi) * @byoyomi
   end
 
   def timeout?(player, start_time, end_time)
-    t = time_duration(start_time, end_time)
+    t = time_duration(player.mytime, start_time, end_time)
 
     if (player.mytime <= t)
       return true
index a21726d..256a0cc 100644 (file)
@@ -13,6 +13,7 @@ require 'TC_floodgate_thread.rb'
 require 'TC_fork'
 require 'TC_functional'
 require 'TC_game'
+require 'TC_game_least_0'
 require 'TC_game_result'
 require 'TC_handicapped_boards'
 # This game has more thatn 256 moves.
index b788fd1..cfdf9e1 100644 (file)
@@ -16,8 +16,11 @@ class TestFork < BaseClient
   def test_wrong_game
     @admin = SocketPlayer.new "dummy", "admin", false
     @admin.connect
+    sleep 0.1
     @admin.reader
+    sleep 0.1
     @admin.login
+    sleep 0.1
 
     result, result2 = handshake do
       @admin.puts "%%FORK wronggame-900-0 buoy_WrongGame-900-0"
@@ -31,8 +34,11 @@ class TestFork < BaseClient
   def test_too_short_fork
     @admin = SocketPlayer.new "dummy", "admin", false
     @admin.connect
+    sleep 0.1
     @admin.reader
+    sleep 0.1
     @admin.login
+    sleep 0.1
 
     result, result2 = handshake do
       source_game = parse_game_name(@admin)
@@ -49,8 +55,12 @@ class TestFork < BaseClient
     
     @admin = SocketPlayer.new "dummy", "admin", "*"
     @admin.connect
+    sleep 0.1
     @admin.reader
+    sleep 0.1
     @admin.login
+    sleep 0.1
+
     assert buoy.is_new_game?("buoy_Fork-1500-0")
 
     result, result2 = handshake do
@@ -63,26 +73,34 @@ class TestFork < BaseClient
     @p1 = SocketPlayer.new "buoy_Fork", "p1", true
     @p2 = SocketPlayer.new "buoy_Fork", "p2", false
     @p1.connect
+    sleep 0.1
     @p2.connect
+    sleep 0.1
     @p1.reader
+    sleep 0.1
     @p2.reader
+    sleep 0.1
     @p1.login
+    sleep 0.1
     @p2.login
-    sleep 1
+    sleep 0.1
     @p1.game
+    sleep 0.1
     @p2.game
-    sleep 1
     @p1.agree
+    sleep 0.1
     @p2.agree
-    sleep 1
+    sleep 0.1
     assert /^Total_Time:1500/ =~ @p1.message
     assert /^Total_Time:1500/ =~ @p2.message
     @p2.move("-3334FU")
-    sleep 1
+    sleep 0.1
     @p1.toryo
-    sleep 1
+    sleep 0.1
     @p2.logout
+    sleep 0.1
     @p1.logout
+    sleep 0.1
 
     @admin.logout
   end
@@ -92,8 +110,11 @@ class TestFork < BaseClient
     
     @admin = SocketPlayer.new "dummy", "admin", "*"
     @admin.connect
+    sleep 0.1
     @admin.reader
+    sleep 0.1
     @admin.login
+    sleep 0.1
 
     result, result2 = handshake do
       source_game = parse_game_name(@admin)
@@ -106,26 +127,35 @@ class TestFork < BaseClient
     @p1 = SocketPlayer.new "buoy_TestFork_1", "p1", true
     @p2 = SocketPlayer.new "buoy_TestFork_1", "p2", false
     @p1.connect
+    sleep 0.1
     @p2.connect
+    sleep 0.1
     @p1.reader
+    sleep 0.1
     @p2.reader
+    sleep 0.1
     @p1.login
+    sleep 0.1
     @p2.login
-    sleep 1
+    sleep 0.1
     @p1.game
+    sleep 0.1
     @p2.game
-    sleep 1
+    sleep 0.1
     @p1.agree
+    sleep 0.1
     @p2.agree
-    sleep 1
+    sleep 0.1
     assert /^Total_Time:1500/ =~ @p1.message
     assert /^Total_Time:1500/ =~ @p2.message
     @p2.move("-3334FU")
-    sleep 1
+    sleep 0.1
     @p1.toryo
-    sleep 1
+    sleep 0.1
     @p2.logout
+    sleep 0.1
     @p1.logout
+    sleep 0.1
 
     @admin.logout
   end
index 8d7d493..08ffcf2 100644 (file)
@@ -5,6 +5,9 @@ require 'shogi_server/board'
 require 'shogi_server/game'
 require 'shogi_server/player'
 
+$options = {}
+$options["least-time-per-move"] = 1
+
 def log_message(str)
   $stderr.puts str
 end
@@ -52,7 +55,7 @@ BEGIN Time
 Time_Unit:1sec
 Total_Time:1500
 Byoyomi:0
-Least_Time_Per_Move:1
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
 END Time
 BEGIN Position
 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
@@ -86,7 +89,7 @@ BEGIN Time
 Time_Unit:1sec
 Total_Time:1500
 Byoyomi:0
-Least_Time_Per_Move:1
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
 END Time
 BEGIN Position
 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
@@ -154,7 +157,7 @@ BEGIN Time
 Time_Unit:1sec
 Total_Time:1500
 Byoyomi:0
-Least_Time_Per_Move:1
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
 END Time
 BEGIN Position
 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
@@ -189,7 +192,7 @@ BEGIN Time
 Time_Unit:1sec
 Total_Time:1500
 Byoyomi:0
-Least_Time_Per_Move:1
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
 END Time
 BEGIN Position
 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
@@ -261,7 +264,7 @@ BEGIN Time
 Time_Unit:1sec
 Total_Time:1500
 Byoyomi:0
-Least_Time_Per_Move:1
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
 END Time
 BEGIN Position
 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
@@ -297,7 +300,7 @@ BEGIN Time
 Time_Unit:1sec
 Total_Time:1500
 Byoyomi:0
-Least_Time_Per_Move:1
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
 END Time
 BEGIN Position
 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
diff --git a/test/TC_game_least_0.rb b/test/TC_game_least_0.rb
new file mode 100644 (file)
index 0000000..3429f07
--- /dev/null
@@ -0,0 +1,411 @@
+$:.unshift File.join(File.dirname(__FILE__), "..")
+require 'test/unit'
+require 'test/mock_player'
+require 'shogi_server/board'
+require 'shogi_server/game'
+require 'shogi_server/player'
+
+$options = {}
+$options["least-time-per-move"] = 0
+
+def log_message(str)
+  $stderr.puts str
+end
+
+def log_warning(str)
+  $stderr.puts str
+end
+
+def log_error(str)
+  $stderr.puts str
+end
+
+$league = ShogiServer::League.new(File.dirname(__FILE__))
+$league.event = "test"
+
+class TestGameWithLeastZero < Test::Unit::TestCase
+
+  def test_new
+    game_name = "hoge-1500-10"
+    board = ShogiServer::Board.new
+    board.initial
+    p1 = MockPlayer.new
+    p1.sente = true
+    p1.name  = "p1"
+    p2 = MockPlayer.new
+    p2.sente = false
+    p2.name  = "p2"
+    
+    game = ShogiServer::Game.new game_name, p1, p2, board 
+    assert_equal "", game.last_move
+
+    p1_out = <<EOF
+BEGIN Game_Summary
+Protocol_Version:1.1
+Protocol_Mode:Server
+Format:Shogi 1.0
+Declaration:Jishogi 1.1
+Game_ID:#{game.game_id}
+Name+:p1
+Name-:p2
+Your_Turn:+
+Rematch_On_Draw:NO
+To_Move:+
+BEGIN Time
+Time_Unit:1sec
+Total_Time:1500
+Byoyomi:10
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
+END Time
+BEGIN Position
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+END Position
+END Game_Summary
+EOF
+    assert_equal(p1_out, p1.out.first)
+
+    p2_out = <<EOF
+BEGIN Game_Summary
+Protocol_Version:1.1
+Protocol_Mode:Server
+Format:Shogi 1.0
+Declaration:Jishogi 1.1
+Game_ID:#{game.game_id}
+Name+:p1
+Name-:p2
+Your_Turn:-
+Rematch_On_Draw:NO
+To_Move:+
+BEGIN Time
+Time_Unit:1sec
+Total_Time:1500
+Byoyomi:10
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
+END Time
+BEGIN Position
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+END Position
+END Game_Summary
+EOF
+    assert_equal(p2_out, p2.out.first)
+
+    file = Pathname.new(game.logfile)
+    log = file.read
+    assert_equal(<<EOF, log.gsub(/^\$START_TIME.*?\n/,''))
+V2
+N+p1
+N-p2
+$EVENT:#{game.game_id}
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+EOF
+  end
+
+  def test_new_buoy_1_move
+    game_name = "buoyhoge-1500-10"
+    board = ShogiServer::Board.new
+    board.set_from_moves ["+7776FU"]
+    p1 = MockPlayer.new
+    p1.sente = true
+    p1.name  = "p1"
+    p2 = MockPlayer.new
+    p2.sente = false
+    p2.name  = "p2"
+    
+    game = ShogiServer::Game.new game_name, p1, p2, board 
+    assert_equal "+7776FU,T1", game.last_move
+
+    p1_out = <<EOF
+BEGIN Game_Summary
+Protocol_Version:1.1
+Protocol_Mode:Server
+Format:Shogi 1.0
+Declaration:Jishogi 1.1
+Game_ID:#{game.game_id}
+Name+:p1
+Name-:p2
+Your_Turn:+
+Rematch_On_Draw:NO
+To_Move:-
+BEGIN Time
+Time_Unit:1sec
+Total_Time:1500
+Byoyomi:10
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
+END Time
+BEGIN Position
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
++7776FU,T1
+END Position
+END Game_Summary
+EOF
+    assert_equal(p1_out, p1.out.first)
+
+    p2_out = <<EOF
+BEGIN Game_Summary
+Protocol_Version:1.1
+Protocol_Mode:Server
+Format:Shogi 1.0
+Declaration:Jishogi 1.1
+Game_ID:#{game.game_id}
+Name+:p1
+Name-:p2
+Your_Turn:-
+Rematch_On_Draw:NO
+To_Move:-
+BEGIN Time
+Time_Unit:1sec
+Total_Time:1500
+Byoyomi:10
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
+END Time
+BEGIN Position
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
++7776FU,T1
+END Position
+END Game_Summary
+EOF
+    assert_equal(p2_out, p2.out.first)
+
+    file = Pathname.new(game.logfile)
+    log = file.read
+    assert_equal(<<EOF, log.gsub(/^\$START_TIME.*?\n/,''))
+V2
+N+p1
+N-p2
+$EVENT:#{game.game_id}
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+'buoy game starting with 1 moves
++7776FU
+T1
+EOF
+  end
+
+  def test_new_buoy_2_moves
+    game_name = "buoyhoge-1500-10"
+    board = ShogiServer::Board.new
+    board.set_from_moves ["+7776FU", "-3334FU"]
+    p1 = MockPlayer.new
+    p1.sente = true
+    p1.name  = "p1"
+    p2 = MockPlayer.new
+    p2.sente = false
+    p2.name  = "p2"
+    
+    game = ShogiServer::Game.new game_name, p1, p2, board 
+    assert_equal "-3334FU,T1", game.last_move
+
+    p1_out = <<EOF
+BEGIN Game_Summary
+Protocol_Version:1.1
+Protocol_Mode:Server
+Format:Shogi 1.0
+Declaration:Jishogi 1.1
+Game_ID:#{game.game_id}
+Name+:p1
+Name-:p2
+Your_Turn:+
+Rematch_On_Draw:NO
+To_Move:+
+BEGIN Time
+Time_Unit:1sec
+Total_Time:1500
+Byoyomi:10
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
+END Time
+BEGIN Position
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
++7776FU,T1
+-3334FU,T1
+END Position
+END Game_Summary
+EOF
+    assert_equal(p1_out, p1.out.first)
+
+    p2_out = <<EOF
+BEGIN Game_Summary
+Protocol_Version:1.1
+Protocol_Mode:Server
+Format:Shogi 1.0
+Declaration:Jishogi 1.1
+Game_ID:#{game.game_id}
+Name+:p1
+Name-:p2
+Your_Turn:-
+Rematch_On_Draw:NO
+To_Move:+
+BEGIN Time
+Time_Unit:1sec
+Total_Time:1500
+Byoyomi:10
+Least_Time_Per_Move:#{$options["least-time-per-move"]}
+END Time
+BEGIN Position
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
++7776FU,T1
+-3334FU,T1
+END Position
+END Game_Summary
+EOF
+    assert_equal(p2_out, p2.out.first)
+
+    file = Pathname.new(game.logfile)
+    log = file.read
+    assert_equal(<<EOF, log.gsub(/^\$START_TIME.*?\n/,''))
+V2
+N+p1
+N-p2
+$EVENT:#{game.game_id}
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+'buoy game starting with 2 moves
++7776FU
+T1
+-3334FU
+T1
+EOF
+  end
+  
+  def test_monitor_add
+    game_name = "hoge-1500-10"
+    board = ShogiServer::Board.new
+    board.initial
+    p1 = MockPlayer.new
+    p1.sente = true
+    p1.name  = "p1"
+    p2 = MockPlayer.new
+    p2.sente = false
+    p2.name  = "p2"
+    
+    game = ShogiServer::Game.new game_name, p1, p2, board 
+    handler1 = ShogiServer::MonitorHandler1.new p1
+    handler2 = ShogiServer::MonitorHandler2.new p2
+
+    assert_equal(0, game.monitors.size)
+    game.monitoron(handler1)
+    assert_equal(1, game.monitors.size)
+    game.monitoron(handler2)
+    assert_equal(2, game.monitors.size)
+    game.monitoroff(handler1)
+    assert_equal(1, game.monitors.size)
+    assert_equal(handler2, game.monitors.last)
+    game.monitoroff(handler2)
+    assert_equal(0, game.monitors.size)
+  end
+
+  def test_decide_turns
+    p1 = MockPlayer.new
+    p1.name = "p1"
+    p2 = MockPlayer.new
+    p2.name = "p2"
+
+    p1.sente=nil; p2.sente=false
+    ShogiServer::Game::decide_turns(p1, "+", p2)
+    assert_equal true, p1.sente
+
+    p1.sente=nil; p2.sente=nil
+    ShogiServer::Game::decide_turns(p1, "+", p2)
+    assert_equal true, p1.sente
+
+    p1.sente=nil; p2.sente=true
+    ShogiServer::Game::decide_turns(p1, "-", p2)
+    assert_equal false, p1.sente
+
+    p1.sente=nil; p2.sente=nil
+    ShogiServer::Game::decide_turns(p1, "-", p2)
+    assert_equal false, p1.sente
+
+    p1.sente=nil; p2.sente=false
+    ShogiServer::Game::decide_turns(p1, "*", p2)
+    assert_equal true, p1.sente
+
+    p1.sente=nil; p2.sente=true
+    ShogiServer::Game::decide_turns(p1, "*", p2)
+    assert_equal false, p1.sente
+
+    p1.sente=nil; p2.sente=nil
+    ShogiServer::Game::decide_turns(p1, "*", p2)
+    assert (p1.sente == true  && p2.sente == false) ||
+           (p1.sente == false && p2.sente == true)
+  end
+end
+
index 151ba81..9ae5bef 100644 (file)
@@ -30,11 +30,11 @@ end
 class TestChessClock < Test::Unit::TestCase
   def test_time_duration
     tc = ShogiServer::ChessClock.new(1, 1500, 60)
-    assert_equal(1, tc.time_duration(100.1, 100.9))
-    assert_equal(1, tc.time_duration(100, 101))
-    assert_equal(1, tc.time_duration(100.1, 101.9))
-    assert_equal(2, tc.time_duration(100.1, 102.9))
-    assert_equal(2, tc.time_duration(100, 102))
+    assert_equal(1, tc.time_duration(nil, 100.1, 100.9))
+    assert_equal(1, tc.time_duration(nil, 100, 101))
+    assert_equal(1, tc.time_duration(nil, 100.1, 101.9))
+    assert_equal(2, tc.time_duration(nil, 100.1, 102.9))
+    assert_equal(2, tc.time_duration(nil, 100, 102))
   end
 
   def test_without_byoyomi
@@ -70,15 +70,56 @@ class TestChessClock < Test::Unit::TestCase
   end
 end
 
+class TestChessClockWithLeastZero < Test::Unit::TestCase
+  def test_time_duration_within_thinking_time
+    tc = ShogiServer::ChessClockWithLeastZero.new(0, 900, 10)
+    assert_equal(0, tc.time_duration(100, 100.1, 100.9))  # 0.8
+    assert_equal(1, tc.time_duration(100, 100, 101))      # 1
+    assert_equal(1, tc.time_duration(100, 100.1, 101.9))  # 1.8
+    assert_equal(1, tc.time_duration(1,    100,   101))    # 1
+    assert_equal(2, tc.time_duration(100, 100.1, 102.9))  # 2.8
+    assert_equal(2, tc.time_duration(100, 100, 102))      # 2
+  end
+
+  def test_time_duration_over_thinking_time
+    tc = ShogiServer::ChessClockWithLeastZero.new(0, 900, 10)
+    assert_equal(1, tc.time_duration(1,    100.1, 101.9))  # 1.8
+    assert_equal(2, tc.time_duration(2,    100.1, 102.9))  # 2.8
+  end
+
+  def test_with_byoyomi
+    tc = ShogiServer::ChessClockWithLeastZero.new(0, 900, 10)
+
+    p = DummyPlayer.new 100
+    assert(!tc.timeout?(p, 100, 101))    # 1
+    assert(!tc.timeout?(p, 100, 209))    # 109
+    assert(!tc.timeout?(p, 100, 209.9))  # 109.9
+    assert(tc.timeout?(p, 100, 210))     # 110
+    assert(tc.timeout?(p, 100, 210.1))   # 110.1
+    assert(tc.timeout?(p, 100, 211))     # 111
+  end
+
+  def test_with_byoyomi2
+    tc = ShogiServer::ChessClockWithLeastZero.new(0, 0, 10)
+
+    p = DummyPlayer.new 0
+    assert(!tc.timeout?(p, 100, 109))    # 9
+    assert(!tc.timeout?(p, 100, 109.9))  # 9.9
+    assert(tc.timeout?(p, 100, 110))     # 10
+    assert(tc.timeout?(p, 100, 110.1))   # 10.1
+    assert(tc.timeout?(p, 100, 110))     # 10.1
+  end
+end
+
 class TestStopWatchClock < Test::Unit::TestCase
   def test_time_duration
     tc = ShogiServer::StopWatchClock.new(1, 1500, 60)
-    assert_equal(0, tc.time_duration(100.1, 100.9))
-    assert_equal(0, tc.time_duration(100, 101))
-    assert_equal(0, tc.time_duration(100, 159.9))
-    assert_equal(60, tc.time_duration(100, 160))
-    assert_equal(60, tc.time_duration(100, 219))
-    assert_equal(120, tc.time_duration(100, 220))
+    assert_equal(0,   tc.time_duration(nil, 100.1, 100.9))
+    assert_equal(0,   tc.time_duration(nil, 100, 101))
+    assert_equal(0,   tc.time_duration(nil, 100, 159.9))
+    assert_equal(60,  tc.time_duration(nil, 100, 160))
+    assert_equal(60,  tc.time_duration(nil, 100, 219))
+    assert_equal(120, tc.time_duration(nil, 100, 220))
   end
 
   def test_with_byoyomi
index 39ad817..f45dc44 100644 (file)
@@ -178,20 +178,32 @@ class BaseClient < Test::Unit::TestCase
   end
 
   def login
+    sleep 0.1
     @p1.connect
+    sleep 0.1
     @p2.connect
+    sleep 0.1
     @p1.login
+    sleep 0.1
     @p2.login
+    sleep 0.1
     @p1.game
+    sleep 0.1
     @p2.game
+    sleep 0.1
     @p1.wait_game
+    sleep 0.1
     @p2.wait_game
   end
 
   def agree
+    sleep 0.1
     @p1.agree
+    sleep 0.1
     @p2.agree
+    sleep 0.1
     @p1.wait_agree
+    sleep 0.1
     @p2.wait_agree
   end