OSDN Git Service

shogi_server/{game,time_clock}.rb: Adds variations of thinking time calculation:...
authorDaigo Moriwaki <beatles@users.sourceforge.jp>
Sun, 7 Apr 2013 15:39:32 +0000 (00:39 +0900)
committerDaigo Moriwaki <beatles@users.sourceforge.jp>
Sun, 7 Apr 2013 15:39:32 +0000 (00:39 +0900)
(current) and StopWatchClock (new).

StopWatchClock, which is usually used at official games of human
professional players, is a clock where thiking time less than a
miniute is regarded as zero.
To select StopWatchClock, use a special game name with "060"
byoyomi time. ex. "gamename_1500_060".

changelog
shogi_server/game.rb
shogi_server/time_clock.rb [new file with mode: 0644]
test/TC_ALL.rb
test/TC_command.rb
test/TC_time_clock.rb [new file with mode: 0644]

index eb0c731..428124a 100644 (file)
--- a/changelog
+++ b/changelog
@@ -1,3 +1,15 @@
+2013-04-07  Daigo Moriwaki <daigo at debian dot org>
+
+       * [shogi-server]
+         - shogi_server/{game,time_clock}.rb:
+           Adds variations of thinking time calculation: ChessClock
+           (current) and StopWatchClock (new).
+           StopWatchClock, which is usually used at official games of human
+           professional players, is a clock where thiking time less than a
+           miniute is regarded as zero.
+           To select StopWatchClock, use a special game name with "060"
+           byoyomi time. ex. "gamename_1500_060".
+
 2013-03-31  Daigo Moriwaki <daigo at debian dot org>
 
        * [shogi-server]
 2013-03-31  Daigo Moriwaki <daigo at debian dot org>
 
        * [shogi-server]
index afb5d08..1197896 100644 (file)
@@ -19,6 +19,7 @@
 
 require 'shogi_server/league/floodgate'
 require 'shogi_server/game_result'
 
 require 'shogi_server/league/floodgate'
 require 'shogi_server/game_result'
+require 'shogi_server/time_clock'
 require 'shogi_server/util'
 
 module ShogiServer # for a namespace
 require 'shogi_server/util'
 
 module ShogiServer # for a namespace
@@ -69,6 +70,8 @@ class Game
     if (@game_name =~ /-(\d+)-(\d+)$/)
       @total_time = $1.to_i
       @byoyomi = $2.to_i
     if (@game_name =~ /-(\d+)-(\d+)$/)
       @total_time = $1.to_i
       @byoyomi = $2.to_i
+
+      @time_clock = TimeClock::factory(Least_Time_Per_Move, @game_name)
     end
 
     if (player0.sente)
     end
 
     if (player0.sente)
@@ -119,6 +122,7 @@ class Game
     $league.games[@game_id] = self
 
     log_message(sprintf("game created %s", @game_id))
     $league.games[@game_id] = self
 
     log_message(sprintf("game created %s", @game_id))
+    log_message("    " + @time_clock.to_s)
 
     @start_time = nil
     @fh = open(@logfile, "w")
 
     @start_time = nil
     @fh = open(@logfile, "w")
@@ -127,7 +131,7 @@ class Game
 
     propose
   end
 
     propose
   end
-  attr_accessor :game_name, :total_time, :byoyomi, :sente, :gote, :game_id, :board, :current_player, :next_player, :fh, :monitors
+  attr_accessor :game_name, :total_time, :byoyomi, :sente, :gote, :game_id, :board, :current_player, :next_player, :fh, :monitors, :time_clock
   attr_accessor :last_move, :current_turn
   attr_reader   :result, :prepared_time
 
   attr_accessor :last_move, :current_turn
   attr_reader   :result, :prepared_time
 
@@ -227,22 +231,16 @@ class Game
       return nil
     end
 
       return nil
     end
 
-    finish_flag = true
     @end_time = end_time
     @end_time = end_time
-    t = [(@end_time - @start_time).floor, Least_Time_Per_Move].max
-    
+    finish_flag = true
     move_status = nil
     move_status = nil
-    if ((@current_player.mytime - t <= -@byoyomi) && 
-        ((@total_time > 0) || (@byoyomi > 0)))
+
+    if (@time_clock.timeout?(@current_player, @start_time, @end_time))
       status = :timeout
     elsif (str == :timeout)
       return false            # time isn't expired. players aren't swapped. continue game
     else
       status = :timeout
     elsif (str == :timeout)
       return false            # time isn't expired. players aren't swapped. continue game
     else
-      @current_player.mytime -= t
-      if (@current_player.mytime < 0)
-        @current_player.mytime = 0
-      end
-
+      t = @time_clock.process_time(@current_player, @start_time, @end_time)
       move_status = @board.handle_one_move(str, @sente == @current_player)
       # log_debug("move_status: %s for %s's %s" % [move_status, @sente == @current_player ? "BLACK" : "WHITE", str])
 
       move_status = @board.handle_one_move(str, @sente == @current_player)
       # log_debug("move_status: %s for %s's %s" % [move_status, @sente == @current_player ? "BLACK" : "WHITE", str])
 
diff --git a/shogi_server/time_clock.rb b/shogi_server/time_clock.rb
new file mode 100644 (file)
index 0000000..08258de
--- /dev/null
@@ -0,0 +1,129 @@
+## $Id$
+
+## Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch)
+## Copyright (C) 2007-2008 Daigo Moriwaki (daigo at debian dot org)
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+module ShogiServer # for a namespace
+
+# Abstract class to caclulate thinking time.
+#
+class TimeClock
+
+  def TimeClock.factory(least_time_per_move, game_name)
+    total_time_str = nil
+    byoyomi_str = nil
+    if (game_name =~ /-(\d+)-(\d+)$/)
+      total_time_str = $1
+      byoyomi_str    = $2
+    end
+    total_time = total_time_str.to_i
+    byoyomi    = byoyomi_str.to_i
+    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)
+    end
+  end
+
+  def initialize(least_time_per_move, total_time, byoyomi)
+    @least_time_per_move = least_time_per_move
+    @total_time = total_time
+    @byoyomi     = byoyomi
+  end
+
+  # Returns thinking time duration
+  #
+  def time_duration(start_time, end_time)
+    # implement this
+    return 9999999
+  end
+
+  # If thinking time runs out, returns true; false otherwise.
+  #
+  def timeout?(player, start_time, end_time)
+    # implement this
+    return true
+  end
+
+  # 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)
+    
+    player.mytime -= t
+    if (player.mytime < 0)
+      player.mytime = 0
+    end
+
+    return t
+  end
+end
+
+# Calculates thinking time with chess clock.
+#
+class ChessClock < TimeClock
+  def initialize(least_time_per_move, total_time, byoyomi)
+    super
+  end
+
+  def time_duration(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)
+
+    if ((player.mytime - t <= -@byoyomi) && 
+        ((@total_time > 0) || (@byoyomi > 0)))
+      return true
+    else
+      return false
+    end
+  end
+
+  def to_s
+    return "ChessClock: 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
+  end
+
+  def time_duration(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)
+
+    if (player.mytime <= t)
+      return true
+    else
+      return false
+    end
+  end
+
+  def to_s
+    return "StopWatchClock: LeastTimePerMove %d; TotalTime %d; Byoyomi %d" % [@least_time_per_move, @total_time, @byoyomi]
+  end
+end
+
+end
index f93033f..ca66262 100644 (file)
@@ -24,6 +24,7 @@ require 'TC_oute_sennichite'
 require 'TC_pairing'
 require 'TC_player'
 require 'TC_rating'
 require 'TC_pairing'
 require 'TC_player'
 require 'TC_rating'
+require 'TC_time_clock'
 require 'TC_uchifuzume'
 require 'TC_usi'
 require 'TC_util'
 require 'TC_uchifuzume'
 require 'TC_usi'
 require 'TC_util'
index 012ec82..3cb3795 100644 (file)
@@ -960,6 +960,13 @@ class TestForkCommand < Test::Unit::TestCase
     c.decide_new_buoy_game_name
     assert_equal "buoy_denou_13-14400-60", c.new_buoy_game
   end
     c.decide_new_buoy_game_name
     assert_equal "buoy_denou_13-14400-60", c.new_buoy_game
   end
+
+  def test_new_buoy_game_name2
+    src = "%%FORK server+denou-14400-060+p1+p2+20130223185013"
+    c = ShogiServer::ForkCommand.new src, @player, "server+denou-14400-060+p1+p2+20130223185013", nil, 13
+    c.decide_new_buoy_game_name
+    assert_equal "buoy_denou_13-14400-060", c.new_buoy_game
+  end
 end
 
 #
 end
 
 #
diff --git a/test/TC_time_clock.rb b/test/TC_time_clock.rb
new file mode 100644 (file)
index 0000000..151ba81
--- /dev/null
@@ -0,0 +1,92 @@
+$:.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'
+
+class DummyPlayer
+  def initialize(mytime)
+    @mytime = mytime
+  end
+  attr_reader :mytime
+end
+
+class TestTimeClockFactor < Test::Unit::TestCase
+  def test_chess_clock
+    c = ShogiServer::TimeClock::factory(1, "hoge-900-0")
+    assert_instance_of(ShogiServer::ChessClock, c)
+
+    c = ShogiServer::TimeClock::factory(1, "hoge-1500-60")
+    assert_instance_of(ShogiServer::ChessClock, c)
+  end
+
+  def test_stop_watch_clock
+    c = ShogiServer::TimeClock::factory(1, "hoge-1500-060")
+    assert_instance_of(ShogiServer::StopWatchClock, c)
+  end
+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))
+  end
+
+  def test_without_byoyomi
+    tc = ShogiServer::ChessClock.new(1, 1500, 0)
+
+    p = DummyPlayer.new 100
+    assert(!tc.timeout?(p, 100, 101))
+    assert(!tc.timeout?(p, 100, 199))
+    assert(tc.timeout?(p, 100, 200))
+    assert(tc.timeout?(p, 100, 201))
+  end
+
+  def test_with_byoyomi
+    tc = ShogiServer::ChessClock.new(1, 1500, 60)
+
+    p = DummyPlayer.new 100
+    assert(!tc.timeout?(p, 100, 101))
+    assert(!tc.timeout?(p, 100, 259))
+    assert(tc.timeout?(p, 100, 260))
+    assert(tc.timeout?(p, 100, 261))
+
+    p = DummyPlayer.new 30
+    assert(!tc.timeout?(p, 100, 189))
+    assert(tc.timeout?(p, 100, 190))
+  end
+
+  def test_with_byoyomi2
+    tc = ShogiServer::ChessClock.new(1, 0, 60)
+
+    p = DummyPlayer.new 0
+    assert(!tc.timeout?(p, 100, 159))
+    assert(tc.timeout?(p, 100, 160))
+  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))
+  end
+
+  def test_with_byoyomi
+    tc = ShogiServer::StopWatchClock.new(1, 600, 60)
+
+    p = DummyPlayer.new 60
+    assert(!tc.timeout?(p, 100, 159))
+    assert(tc.timeout?(p, 100, 160))
+  end
+end
+