OSDN Git Service

Improved %%FORK command. Thinking times of each move are also provided.
authorDaigo Moriwaki <beatles@users.sourceforge.jp>
Sun, 17 Mar 2013 05:49:40 +0000 (14:49 +0900)
committerDaigo Moriwaki <beatles@users.sourceforge.jp>
Sun, 17 Mar 2013 05:49:40 +0000 (14:49 +0900)
changelog
shogi_server/board.rb
shogi_server/buoy.rb
shogi_server/command.rb
shogi_server/game.rb
test/TC_buoy.rb
test/TC_command.rb
test/TC_fork.rb

index 75d31f0..1e4b366 100644 (file)
--- a/changelog
+++ b/changelog
@@ -13,9 +13,6 @@
            with this command you can restart a new buoy game from the
            previous stable position.
            ex. %%FORK server-denou-14400-60+p1+p2+20130223185013 buoy_denou-14400-60
-           - Note that this feature does not provice accurate thinking
-           time estimate. The thinking time of the last posision is carried
-           over to the new game regardless nth-move.
 
 2012-12-30  Daigo Moriwaki <daigo at debian dot org>
 
index d2b304b..c006176 100644 (file)
@@ -43,17 +43,42 @@ EOF
 
   # Split a moves line into an array of a move string.
   # If it fails to parse the moves, it raises WrongMoves.
-  # @param moves a moves line. Ex. "+776FU-3334Fu"
-  # @return an array of a move string. Ex. ["+7776FU", "-3334FU"]
+  # @param moves a moves line. Ex. "+776FU-3334FU" or
+  #              moves with times. Ex "+776FU,T2-3334FU,T5"
+  # @return an array of a move string. Ex. ["+7776FU", "-3334FU"] or
+  #         an array of arrays. Ex. [["+7776FU","T2"], ["-3334FU", "T5"]]
   #
   def Board.split_moves(moves)
     ret = []
 
-    rs = moves.gsub %r{[\+\-]\d{4}\w{2}} do |s|
-           ret << s
-           ""
-         end
-    raise WrongMoves, rs unless rs.empty?
+    i=0
+    tmp = ""
+    while i<moves.size
+      if moves[i,1] == "+" ||
+         moves[i,1] == "-" ||
+         i == moves.size - 1
+        if i == moves.size - 1
+          tmp << moves[i,1]
+        end
+        unless tmp.empty?
+          a = tmp.split(",")
+          if a[0].size != 7
+            raise WrongMoves, a[0]
+          end
+          if a.size == 1 # "+7776FU"
+            ret << a[0]
+          else           # "+7776FU,T2"
+            unless /^T\d+/ =~ a[1] 
+              raise WrongMoves, a[1]
+            end
+            ret << a
+          end
+          tmp = ""
+        end
+      end
+      tmp << moves[i,1]
+      i += 1
+    end
 
     return ret
   end
@@ -230,14 +255,21 @@ EOF
 
   # Set up a board starting with a position after the moves.
   # Failing to parse the moves raises an ArgumentError.
-  # @param moves an array of moves. ex. ["+7776FU", "-3334FU"]
+  # @param moves an array of moves. ex. ["+7776FU", "-3334FU"] or
+  #        an array of arrays. ex. [["+7776FU","T2"], ["-3334FU","T5"]]
   #
   def set_from_moves(moves)
     initial()
     return :normal if moves.empty?
     rt = nil
     moves.each do |move|
-      rt = handle_one_move(move, @teban)
+      rt = nil
+      case move
+      when Array
+        rt = handle_one_move(move[0], @teban)
+      when String
+        rt = handle_one_move(move, @teban)
+      end
       raise ArgumentError, "bad moves: #{moves}" unless rt == :normal
     end
     @initial_moves = moves.dup
index f189402..e2f88ee 100644 (file)
@@ -10,12 +10,10 @@ module ShogiServer
     attr_reader :moves
     attr_reader :owner
     attr_reader :count
-    attr_reader :sente_time
-    attr_reader :gote_time
 
-    def initialize(game_name, moves, owner, count, sente_time, gote_time)
+    def initialize(game_name, moves, owner, count)
       raise "owner name is required" if owner && !owner.instance_of?(String)
-      @game_name, @moves, @owner, @count, @sente_time, @gote_time = game_name, moves, owner, count, sente_time, gote_time
+      @game_name, @moves, @owner, @count = game_name, moves, owner, count
     end
 
     def decrement_count
@@ -23,18 +21,16 @@ module ShogiServer
     end
 
     def ==(rhs)
-      return (@game_name  == rhs.game_name &&
-              @moves      == rhs.moves &&
-              @owner      == rhs.owner &&
-              @count      == rhs.count &&
-              @sente_time == rhs.sente_time &&
-              @gote_time  == rhs.gote_time)
+      return (@game_name == rhs.game_name &&
+              @moves == rhs.moves &&
+              @owner == rhs.owner &&
+              @count == rhs.count)
     end
   end
 
   class NilBuoyGame < BuoyGame
     def initialize
-      super(nil, nil, nil, 0, nil, nil)
+      super(nil, nil, nil, 0)
     end
   end
 
@@ -66,11 +62,9 @@ module ShogiServer
         if @db.root?(buoy_game.game_name)
           # error
         else
-          hash = {'moves'      => buoy_game.moves,
-                  'owner'      => buoy_game.owner,
-                  'count'      => buoy_game.count,
-                  'sente_time' => buoy_game.sente_time,
-                  'gote_time'  => buoy_game.gote_time}
+          hash = {'moves' => buoy_game.moves,
+                  'owner' => buoy_game.owner,
+                  'count' => buoy_game.count}
           @db[buoy_game.game_name] = hash
         end
       end
@@ -79,11 +73,9 @@ module ShogiServer
     def update_game(buoy_game)
       @db.transaction do
         if @db.root?(buoy_game.game_name)
-          hash = {'moves'     => buoy_game.moves,
-                  'owner'     => buoy_game.owner,
-                  'count'     => buoy_game.count,
-                  'sene_time' => buoy_game.sente_time,
-                  'gote_time' => buoy_game.gote_time}
+          hash = {'moves' => buoy_game.moves,
+                  'owner' => buoy_game.owner,
+                  'count' => buoy_game.count}
           @db[buoy_game.game_name] = hash
         else
           # error
@@ -101,12 +93,10 @@ module ShogiServer
       @db.transaction(true) do
         hash = @db[game_name]
         if hash
-          moves      = hash['moves']
-          owner      = hash['owner']
-          count      = hash['count'].to_i
-          sente_time = hash['sente_time'] ? hash['sente_time'].to_i : nil
-          gote_time  = hash['gote_time']  ? hash['gote_time'].to_i  : nil
-          return BuoyGame.new(game_name, moves, owner, count, sente_time, gote_time)
+          moves = hash['moves']
+          owner = hash['owner']
+          count = hash['count'].to_i
+          return BuoyGame.new(game_name, moves, owner, count)
         else
           return NilBuoyGame.new
         end
index b8ffb9b..99c2b41 100644 (file)
@@ -540,9 +540,7 @@ module ShogiServer
               return :continue
             end
             buoy.decrement_count(buoy_game)
-            options = {:sente_time => buoy_game.sente_time,
-                       :gote_time  => buoy_game.gote_time}
-            Game::new(@player.game_name, @player, rival, board, options)
+            Game::new(@player.game_name, @player, rival, board)
           end
         else
           klass = Login.handicapped_game_name?(@game_name) || Board
@@ -702,16 +700,6 @@ module ShogiServer
       @game_name = game_name
       @moves     = moves
       @count     = count
-      @sente_time = nil
-      @gote_time  = nil
-    end
-
-    # ForkCommand may call this method to set remaining time of both
-    # players.
-    #
-    def set_initial_time(sente_time, gote_time)
-      @sente_time = sente_time
-      @gote_time  = gote_time
     end
 
     def call
@@ -741,7 +729,7 @@ module ShogiServer
         raise WrongMoves
       end
 
-      buoy_game = BuoyGame.new(@game_name, @moves, @player.name, @count, @sente_time, @gote_time)
+      buoy_game = BuoyGame.new(@game_name, @moves, @player.name, @count)
       buoy.add_game(buoy_game)
       @player.write_safe(sprintf("##[SETBUOY] +OK\n"))
       log_info("A buoy game was created: %s by %s" % [@game_name, @player.name])
@@ -769,11 +757,7 @@ module ShogiServer
       # found two players: p1 and p2
       log_info("Starting a buoy game: %s with %s and %s" % [@game_name, p1.name, p2.name])
       buoy.decrement_count(buoy_game)
-
-      # remaining time for ForkCommand
-      options = {:sente_time => buoy_game.sente_time,
-                 :gote_time  => buoy_game.gote_time}
-      game = Game::new(@game_name, p1, p2, board, options)
+      game = Game::new(@game_name, p1, p2, board)
       return :continue
 
     rescue WrongMoves => e
@@ -855,17 +839,18 @@ module ShogiServer
         return :continue
       end
 
-      moves = game.read_moves
+      moves = game.read_moves # [["+7776FU","T2"],["-3334FU","T5"]]
       @nth_move = moves.size - 1 unless @nth_move
       if @nth_move > moves.size or @nth_move < 1
         @player.write_safe(sprintf("##[ERROR] number of moves to fork is out of range: %s.\n", moves.size))
         log_error "Number of moves to fork is out of range: %s [%s]" % [@nth_move, @player.name]
         return :continue
       end
-      new_moves = moves[0...@nth_move]
-
-      buoy_cmd = SetBuoyCommand.new(@str, @player, @new_buoy_game, new_moves.join(""), 1)
-      buoy_cmd.set_initial_time(game.sente.mytime, game.gote.mytime)
+      new_moves_str = ""
+      moves[0...@nth_move].each do |m|
+        new_moves_str << m.join(",")
+      end
+      buoy_cmd = SetBuoyCommand.new(@str, @player, @new_buoy_game, new_moves_str, 1)
       return buoy_cmd.call
     end
   end
index 4d246b9..afb5d08 100644 (file)
@@ -63,10 +63,7 @@ class Game
   end
 
 
-  # options may or may not have follwing keys: :sente_time, :gote_time; they
-  # are used for %%FORK command to set remaining times at the restart.
-  #
-  def initialize(game_name, player0, player1, board, options={})
+  def initialize(game_name, player0, player1, board)
     @monitors = Array::new # array of MonitorHandler*
     @game_name = game_name
     if (@game_name =~ /-(\d+)-(\d+)$/)
@@ -90,7 +87,16 @@ class Game
     @sente.game = self
     @gote.game  = self
 
-    @last_move = @board.initial_moves.empty? ? "" : "%s,T1" % [@board.initial_moves.last]
+    @last_move = ""
+    unless @board.initial_moves.empty?
+      last_move = @board.initial_moves.last
+      case last_move
+      when Array
+        @last_move = last_move.join(",")
+      when String
+        @last_move = "%s,T1" % [last_move]
+      end
+    end
     @current_turn = @board.initial_moves.size
 
     @sente.status = "agree_waiting"
@@ -119,8 +125,6 @@ class Game
     @fh.sync = true
     @result = nil
 
-    @options = options.dup
-
     propose
   end
   attr_accessor :game_name, :total_time, :byoyomi, :sente, :gote, :game_id, :board, :current_player, :next_player, :fh, :monitors
@@ -312,8 +316,8 @@ class Game
     @gote.status  = "game"
     @sente.write_safe(sprintf("START:%s\n", @game_id))
     @gote.write_safe(sprintf("START:%s\n", @game_id))
-    @sente.mytime = @options[:sente_time] || @total_time
-    @gote.mytime  = @options[:gote_time]  || @total_time
+    @sente.mytime = @total_time
+    @gote.mytime = @total_time
     @start_time = Time.now
   end
 
@@ -337,8 +341,14 @@ class Game
     unless @board.initial_moves.empty?
       @fh.puts "'buoy game starting with %d moves" % [@board.initial_moves.size]
       @board.initial_moves.each do |move|
-        @fh.puts move
-        @fh.puts "T1"
+        case move
+        when Array
+          @fh.puts move[0]
+          @fh.puts move[1]
+        when String
+          @fh.puts move
+          @fh.puts "T1"
+        end
       end
     end
   end
@@ -377,13 +387,6 @@ EOM
   end
 
   def propose_message(sg_flag)
-    time = @total_time
-    if @options[:sente_time] && sg_flag == "+"
-      time = @options[:sente_time]
-    elsif @options[:gote_time] && sg_flag == "-"
-      time = @options[:gote_time]
-    end
-
     str = <<EOM
 BEGIN Game_Summary
 Protocol_Version:1.1
@@ -398,13 +401,20 @@ Rematch_On_Draw:NO
 To_Move:#{@board.teban ? "+" : "-"}
 BEGIN Time
 Time_Unit:1sec
-Total_Time:#{time}
+Total_Time:#{@total_time}
 Byoyomi:#{@byoyomi}
 Least_Time_Per_Move:#{Least_Time_Per_Move}
 END Time
 BEGIN Position
 #{@board.initial_string.chomp}
-#{@board.initial_moves.collect {|m| m + ",T1"}.join("\n")}
+#{@board.initial_moves.collect do |m|
+  case m
+  when Array
+    m.join(",")
+  when String
+    m + ",T1"
+  end
+end.join("\n")}
 END Position
 END Game_Summary
 EOM
@@ -421,14 +431,16 @@ EOM
     return false
   end
 
-  # Read the .csa file and returns an array of moves.
-  # ex. ["+7776FU", "-3334FU"]
+  # Read the .csa file and returns an array of moves and times.
+  # ex. [["+7776FU","T2"], ["-3334FU","T5"]]
   #
   def read_moves
     ret = []
     IO.foreach(@logfile) do |line|
       if /^[\+\-]\d{4}[A-Z]{2}/ =~ line
-        ret << line.chomp
+        ret << [line.chomp]
+      elsif /^T\d*/ =~ line
+        ret[-1] << line.chomp
       end
     end
     return ret
index 8f6237f..1c15ca0 100644 (file)
@@ -9,32 +9,14 @@ require 'mock_log_message'
 
 class TestBuoyGame < Test::Unit::TestCase
   def test_equal
-    g1 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, nil, nil)
-    g2 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, nil, nil)
-    assert_equal g1, g2
-  end
-
-  def test_equal2
-    g1 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, 10, 20)
-    g2 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, 10, 20)
+    g1 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1)
+    g2 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1)
     assert_equal g1, g2
   end
 
   def test_not_equal
-    g1 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, nil, nil)
-    g2 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 2, nil, nil)
-    assert_not_equal g1, g2
-  end
-
-  def test_not_equal2
-    g1 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, 10, 20)
-    g2 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, 10, 200)
-    assert_not_equal g1, g2
-  end
-
-  def test_not_equal3
-    g1 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, 10, nil)
-    g2 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, 10, 200)
+    g1 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1)
+    g2 = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 2)
     assert_not_equal g1, g2
   end
 end
@@ -67,18 +49,7 @@ class TestBuoy < Test::Unit::TestCase
   end
 
   def test_add_game
-    game = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, nil, nil)
-    @buoy.add_game(game)
-    assert !@buoy.is_new_game?("buoy_1234-900-0")
-    game2 = @buoy.get_game(game.game_name)
-    assert_equal game, game2
-
-    @buoy.delete_game game
-    assert @buoy.is_new_game?("buoy_1234-900-0")
-  end
-
-  def test_add_game2
-    game = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1, 10, 20)
+    game = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 1)
     @buoy.add_game(game)
     assert !@buoy.is_new_game?("buoy_1234-900-0")
     game2 = @buoy.get_game(game.game_name)
@@ -89,9 +60,9 @@ class TestBuoy < Test::Unit::TestCase
   end
 
   def test_update_game
-    game = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 2, nil, nil)
+    game = ShogiServer::BuoyGame.new("buoy_1234-900-0", [], "p1", 2)
     @buoy.add_game(game)
-    g2 = ShogiServer::BuoyGame.new(game.game_name, game.moves, game.owner, game.count-1, nil, nil)
+    g2 = ShogiServer::BuoyGame.new(game.game_name, game.moves, game.owner, game.count-1)
     @buoy.update_game(g2)
     
     get = @buoy.get_game(g2.game_name)
index dc1c0f5..83d11a9 100644 (file)
@@ -842,7 +842,7 @@ class TestSetBuoyCommand < BaseTestBuoyCommand
     assert !$p1.out.empty?
     assert !$p2.out.empty?
     buoy_game2 = @buoy.get_game("buoy_hoge-1500-0")
-    assert_equal ShogiServer::BuoyGame.new("buoy_hoge-1500-0", "+7776FU", @p.name, 1, nil, nil), buoy_game2
+    assert_equal ShogiServer::BuoyGame.new("buoy_hoge-1500-0", "+7776FU", @p.name, 1), buoy_game2
   end
 
   def test_call_1
@@ -867,7 +867,7 @@ class TestSetBuoyCommand < BaseTestBuoyCommand
 
   def test_call_error_duplicated_game_name
     assert @buoy.is_new_game?("buoy_duplicated-1500-0")
-    bg = ShogiServer::BuoyGame.new("buoy_duplicated-1500-0", ["+7776FU"], @p.name, 1, nil, nil)
+    bg = ShogiServer::BuoyGame.new("buoy_duplicated-1500-0", ["+7776FU"], @p.name, 1)
     @buoy.add_game bg
     assert !@buoy.is_new_game?("buoy_duplicated-1500-0")
     
@@ -905,7 +905,7 @@ end
 #
 class TestDeleteBuoyCommand < BaseTestBuoyCommand
   def test_call
-    buoy_game = ShogiServer::BuoyGame.new("buoy_testdeletebuoy-1500-0", "+7776FU", @p.name, 1, nil, nil)
+    buoy_game = ShogiServer::BuoyGame.new("buoy_testdeletebuoy-1500-0", "+7776FU", @p.name, 1)
     assert @buoy.is_new_game?(buoy_game.game_name)
     @buoy.add_game buoy_game
     assert !@buoy.is_new_game?(buoy_game.game_name)
@@ -918,7 +918,7 @@ class TestDeleteBuoyCommand < BaseTestBuoyCommand
   end
 
   def test_call_not_exist
-    buoy_game = ShogiServer::BuoyGame.new("buoy_notexist-1500-0", "+7776FU", @p.name, 1, nil, nil)
+    buoy_game = ShogiServer::BuoyGame.new("buoy_notexist-1500-0", "+7776FU", @p.name, 1)
     assert @buoy.is_new_game?(buoy_game.game_name)
     cmd = ShogiServer::DeleteBuoyCommand.new "%%DELETEBUOY", @p, buoy_game.game_name
     rt = cmd.call
@@ -929,7 +929,7 @@ class TestDeleteBuoyCommand < BaseTestBuoyCommand
   end
 
   def test_call_another_player
-    buoy_game = ShogiServer::BuoyGame.new("buoy_anotherplayer-1500-0", "+7776FU", "another_player", 1, nil, nil)
+    buoy_game = ShogiServer::BuoyGame.new("buoy_anotherplayer-1500-0", "+7776FU", "another_player", 1)
     assert @buoy.is_new_game?(buoy_game.game_name)
     @buoy.add_game(buoy_game)
     assert !@buoy.is_new_game?(buoy_game.game_name)
@@ -946,7 +946,7 @@ end
 #
 class TestGetBuoyCountCommand < BaseTestBuoyCommand
   def test_call
-    buoy_game = ShogiServer::BuoyGame.new("buoy_testdeletebuoy-1500-0", "+7776FU", @p.name, 1, nil, nil)
+    buoy_game = ShogiServer::BuoyGame.new("buoy_testdeletebuoy-1500-0", "+7776FU", @p.name, 1)
     assert @buoy.is_new_game?(buoy_game.game_name)
     @buoy.add_game buoy_game
     assert !@buoy.is_new_game?(buoy_game.game_name)
@@ -957,7 +957,7 @@ class TestGetBuoyCountCommand < BaseTestBuoyCommand
   end
 
   def test_call_not_exist
-    buoy_game = ShogiServer::BuoyGame.new("buoy_notexist-1500-0", "+7776FU", @p.name, 1, nil, nil)
+    buoy_game = ShogiServer::BuoyGame.new("buoy_notexist-1500-0", "+7776FU", @p.name, 1)
     assert @buoy.is_new_game?(buoy_game.game_name)
     cmd = ShogiServer::GetBuoyCountCommand.new "%%GETBUOYCOUNT", @p, buoy_game.game_name
     rt = cmd.call
index 4cd5e58..64f5f0c 100644 (file)
@@ -74,8 +74,8 @@ class TestFork < BaseClient
     @p1.agree
     @p2.agree
     sleep 1
-    assert /^Total_Time:1499/ =~ @p1.message
-    assert /^Total_Time:1499/ =~ @p2.message
+    assert /^Total_Time:1500/ =~ @p1.message
+    assert /^Total_Time:1500/ =~ @p2.message
     @p2.move("-3334FU")
     sleep 1
     @p1.toryo