+2009-11-10 Daigo Moriwaki <daigo at debian dot org>
+
+ * [shogi-server]
+ - The server logs a result of each game to a file named '00LIST',
+ which will be used to generate players.yaml. If the file does
+ not exist, the server will create one automatically.
+ Instruction to use the game result list file:
+ 1. Make a list of game results from exisiting CSA files with
+ mk_game_results
+ % ./mk_game_results dir_of_csa_files > 00LIST
+ 2. Run the server. It appends a result of each game to
+ '00LIST' when the game finishes.
+ 3. From the list of game results, calcurate rating scores of
+ players.
+ % ./mk_rate 00LIST > players.yaml
+
2009-11-08 Daigo Moriwaki <daigo at debian dot org>
* [mk_rate]
- Split a pre-process collecting game results from csa files into
a new command, mk_game_results. Now, Generating players.yaml
requires two steps as follows:
- % ./mk_game_results dir_of_csa_files > 00list
- % ./mk_rate 00list > players.yaml
+ % ./mk_game_results dir_of_csa_files > 00LIST
+ % ./mk_rate 00LIST > players.yaml
or
% ./mk_game_results dir_of_csa_files | ./mk_rate > players.yaml
(Closes: #19454)
end
if (@current_player == killer)
- result = GameResultAbnormalWin.new(self, @next_player, @current_player)
- result.process
+ @result = GameResultAbnormalWin.new(self, @next_player, @current_player)
+ @result.process
finish
end
end
def finish
log_message(sprintf("game finished %s", @game_id))
- @fh.printf("'$END_TIME:%s\n", Time::new.strftime("%Y/%m/%d %H:%M:%S"))
+
+ # In a case where a player in agree_waiting or start_waiting status is
+ # rejected, a GameResult object is not yet instanciated.
+ # See test/TC_before_agree.rb.
+ end_time = @result ? @result.end_time : Time.now
+ @fh.printf("'$END_TIME:%s\n", end_time.strftime("%Y/%m/%d %H:%M:%S"))
@fh.close
@sente.game = nil
end
end
- result = nil
+ @result = nil
if (@next_player.status != "game") # rival is logout or disconnected
- result = GameResultAbnormalWin.new(self, @current_player, @next_player)
+ @result = GameResultAbnormalWin.new(self, @current_player, @next_player)
elsif (status == :timeout)
# current_player losed
- result = GameResultTimeoutWin.new(self, @next_player, @current_player)
+ @result = GameResultTimeoutWin.new(self, @next_player, @current_player)
elsif (move_status == :illegal)
- result = GameResultIllegalMoveWin.new(self, @next_player, @current_player)
+ @result = GameResultIllegalMoveWin.new(self, @next_player, @current_player)
elsif (move_status == :kachi_win)
- result = GameResultKachiWin.new(self, @current_player, @next_player)
+ @result = GameResultKachiWin.new(self, @current_player, @next_player)
elsif (move_status == :kachi_lose)
- result = GameResultIllegalKachiWin.new(self, @next_player, @current_player)
+ @result = GameResultIllegalKachiWin.new(self, @next_player, @current_player)
elsif (move_status == :toryo)
- result = GameResultToryoWin.new(self, @next_player, @current_player)
+ @result = GameResultToryoWin.new(self, @next_player, @current_player)
elsif (move_status == :outori)
# The current player captures the next player's king
- result = GameResultOutoriWin.new(self, @current_player, @next_player)
+ @result = GameResultOutoriWin.new(self, @current_player, @next_player)
elsif (move_status == :oute_sennichite_sente_lose)
- result = GameResultOuteSennichiteWin.new(self, @gote, @sente) # Sente is checking
+ @result = GameResultOuteSennichiteWin.new(self, @gote, @sente) # Sente is checking
elsif (move_status == :oute_sennichite_gote_lose)
- result = GameResultOuteSennichiteWin.new(self, @sente, @gote) # Gote is checking
+ @result = GameResultOuteSennichiteWin.new(self, @sente, @gote) # Gote is checking
elsif (move_status == :sennichite)
- result = GameResultSennichiteDraw.new(self, @current_player, @next_player)
+ @result = GameResultSennichiteDraw.new(self, @current_player, @next_player)
elsif (move_status == :uchifuzume)
# the current player losed
- result = GameResultUchifuzumeWin.new(self, @next_player, @current_player)
+ @result = GameResultUchifuzumeWin.new(self, @next_player, @current_player)
elsif (move_status == :oute_kaihimore)
# the current player losed
- result = GameResultOuteKaihiMoreWin.new(self, @next_player, @current_player)
+ @result = GameResultOuteKaihiMoreWin.new(self, @next_player, @current_player)
else
finish_flag = false
end
- result.process if result
+ @result.process if @result
finish() if finish_flag
@current_player, @next_player = @next_player, @current_player
@start_time = Time::new
end
end
-# Base class for a game result
+# LoggingObserver appends a result of each game to a log file, which will
+# be used to calculate rating scores of players.
+#
+class LoggingObserver
+ def initialize
+ @logfile = File.join($league.dir, "00LIST")
+ end
+
+ def update(game_result)
+ end_time_str = game_result.end_time.strftime("%Y/%m/%d %H:%M:%S")
+ black = game_result.black
+ white = game_result.white
+ black_name = black.rated? ? black.player_id : black.name
+ white_name = white.rated? ? white.player_id : white.name
+ msg = [end_time_str,
+ game_result.log_summary_type,
+ game_result.black_result,
+ black_name,
+ white_name,
+ game_result.white_result,
+ game_result.game.logfile]
+ begin
+ # Note that this is proccessed in the gian lock.
+ File.open(@logfile, "a") do |f|
+ f << msg.join("\t") << "\n"
+ end
+ rescue => e
+ # ignore
+ $stderr.puts "Failed to write to the game result file: #{@logfile}" if $DEBUG
+ end
+ end
+end
+
+# Base abstract class for a game result.
+# Imediate subclasses are GameResultWin and GameResultDraw.
#
class GameResult
include Observable
attr_reader :white
# Command to send monitors such as '%TORYO' etc...
attr_reader :result_type
+ # Result types to write the main log file such as 'toryo' etc...
+ attr_reader :log_summary_type
+ # Time when the game ends
+ attr_reader :end_time
def initialize(game, p1, p2)
@game = game
player.status = "connected"
end
@result_type = ""
-
+ @end_time = Time.now
regist_observers
end
def regist_observers
add_observer MonitorObserver.new
+ add_observer LoggingObserver.new
if League::Floodgate.game_name?(@game.game_name) &&
@game.sente.player_id &&
$options["floodgate-history"]
add_observer League::Floodgate::History.factory
end
-
end
def process
log(@game.board.to_s.gsub(/^/, "\'").chomp)
end
+ def black_result
+ return "Implemet me!"
+ end
+
+ def white_result
+ return "Implemet me!"
+ end
+
+ def log_summary
+ log_board
+ log("'summary:%s:%s %s:%s %s" % [@log_summary_type,
+ @black.name, black_result,
+ @white.name, white_result])
+ end
end
class GameResultWin < GameResult
@loser.last_game_win = false
end
- def log_summary(type)
- log_board
-
- black_result = white_result = ""
- if @black == @winner
- black_result = "win"
- white_result = "lose"
- else
- black_result = "lose"
- white_result = "win"
- end
- log("'summary:%s:%s %s:%s %s" % [type,
- @black.name, black_result,
- @white.name, white_result])
+ def black_result
+ return @black == @winner ? "win" : "lose"
+ end
+ def white_result
+ return @black == @winner ? "lose" : "win"
end
end
class GameResultAbnormalWin < GameResultWin
+ def initialize(game, winner, loser)
+ super
+ @log_summary_type = "abnormal"
+ @result_type = "%TORYO"
+ end
+
def process
@winner.write_safe("%TORYO\n#RESIGN\n#WIN\n")
@loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
- log("%TORYO")
- log_summary("abnormal")
- @result_type = "%TORYO"
+ log(@result_type)
+ log_summary
notify
end
end
class GameResultTimeoutWin < GameResultWin
+ def initialize(game, winner, loser)
+ super
+ @log_summary_type = "time up"
+ @result_type = "#TIME_UP"
+ end
+
def process
@winner.write_safe("#TIME_UP\n#WIN\n")
@loser.write_safe( "#TIME_UP\n#LOSE\n")
- log_summary("time up")
- @result_type = "#TIME_UP"
+ # no log
+ log_summary
notify
end
end
# A player declares (successful) Kachi
class GameResultKachiWin < GameResultWin
+ def initialize(game, winner, loser)
+ super
+ @log_summary_type = "kachi"
+ @result_type = "%KACHI"
+ end
+
def process
@winner.write_safe("%KACHI\n#JISHOGI\n#WIN\n")
@loser.write_safe( "%KACHI\n#JISHOGI\n#LOSE\n")
- log("%KACHI")
- log_summary("kachi")
- @result_type = "%KACHI"
+ log(@result_type)
+ log_summary
notify
end
end
# A player declares wrong Kachi
class GameResultIllegalKachiWin < GameResultWin
+ def initialize(game, winner, loser)
+ super
+ @log_summary_type = "illegal kachi"
+ @result_type = "%KACHI"
+ end
+
def process
@winner.write_safe("%KACHI\n#ILLEGAL_MOVE\n#WIN\n")
@loser.write_safe( "%KACHI\n#ILLEGAL_MOVE\n#LOSE\n")
- log("%KACHI")
- log_summary("illegal kachi")
- @result_type = "%KACHI"
+ log(@result_type)
+ log_summary
notify
end
end
class GameResultIllegalWin < GameResultWin
def initialize(game, winner, loser, cause)
super(game, winner, loser)
- @cause = cause
+ @log_summary_type = cause
+ @result_type = "#ILLEGAL_MOVE"
end
def process
@winner.write_safe("#ILLEGAL_MOVE\n#WIN\n")
@loser.write_safe( "#ILLEGAL_MOVE\n#LOSE\n")
- log_summary(@cause)
- @result_type = "#ILLEGAL_MOVE"
+ # no log
+ log_summary
notify
end
end
end
end
-class GameResultOutoriWin < GameResultWin
+# This won't happen, though.
+#
+class GameResultOutoriWin < GameResultIllegalWin
def initialize(game, winner, loser)
- super(game, winner, loser)
+ super(game, winner, loser, "outori")
end
end
class GameResultToryoWin < GameResultWin
+ def initialize(game, winner, loser)
+ super
+ @log_summary_type = "toryo"
+ @result_type = "%TORYO"
+ end
+
def process
@winner.write_safe("%TORYO\n#RESIGN\n#WIN\n")
@loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
- log("%TORYO")
- log_summary("toryo")
- @result_type = "%TORYO"
+ log(@result_type)
+ log_summary
notify
end
end
class GameResultOuteSennichiteWin < GameResultWin
+ def initialize(game, winner, loser)
+ super
+ @log_summary_type = "oute_sennichite"
+ @result_type = "#OUTE_SENNICHITE"
+ end
+
def process
@winner.write_safe("#OUTE_SENNICHITE\n#WIN\n")
@loser.write_safe( "#OUTE_SENNICHITE\n#LOSE\n")
- log_summary("oute_sennichite")
- @result_type = "#OUTE_SENNICHITE"
+ # no log
+ log_summary
notify
end
end
+# Draw
+#
class GameResultDraw < GameResult
def initialize(game, p1, p2)
super
p2.last_game_win = false
end
- def log_summary(type)
- log_board
- log("'summary:%s:%s draw:%s draw" % [type, @black.name, @white.name])
+ def black_result
+ return "draw"
+ end
+
+ def white_result
+ return "draw"
end
end
class GameResultSennichiteDraw < GameResultDraw
+ def initialize(game, winner, loser)
+ super
+ @log_summary_type = "sennichite"
+ @result_type = "#SENNICHITE"
+ end
+
def process
@players.each do |player|
player.write_safe("#SENNICHITE\n#DRAW\n")
end
- log_summary("sennichite")
- @result_type = "#SENNICHITE"
+ # no log
+ log_summary
notify
end
end
end # ShogiServer
-
require 'shogi_server'
require 'shogi_server/game'
+$league = ShogiServer::League.new(File.dirname(__FILE__))
+$league.event = "TC_game_result"
+
module ShogiServer
class BasicPlayer
attr_accessor :sente, :status
gr = ShogiServer::GameResultWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal(nil, gr.log_summary_type)
end
def test_game_result_abnormal_win
gr = ShogiServer::GameResultAbnormalWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal("abnormal", gr.log_summary_type)
end
def test_game_result_kachi_win
gr = ShogiServer::GameResultKachiWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal("kachi", gr.log_summary_type)
end
def test_game_result_illegal_kachi_win
gr = ShogiServer::GameResultIllegalKachiWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal("illegal kachi", gr.log_summary_type)
end
def test_game_result_illegal_move_win
gr = ShogiServer::GameResultIllegalMoveWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal("illegal move", gr.log_summary_type)
end
def test_game_result_uchifuzume_win
gr = ShogiServer::GameResultUchifuzumeWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal("uchifuzume", gr.log_summary_type)
end
def test_game_result_oute_kaihi_more_win
gr = ShogiServer::GameResultOuteKaihiMoreWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal("oute_kaihimore", gr.log_summary_type)
end
def test_game_result_outori_win
gr = ShogiServer::GameResultOutoriWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal("outori", gr.log_summary_type)
end
def test_game_result_toryo_win
gr = ShogiServer::GameResultToryoWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal("toryo", gr.log_summary_type)
end
def test_game_result_oute_sennichite_win
gr = ShogiServer::GameResultOuteSennichiteWin.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, true)
assert_equal(@p2.last_game_win, false)
+ assert_equal("oute_sennichite", gr.log_summary_type)
end
def test_game_result_draw
gr = ShogiServer::GameResultDraw.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, false)
assert_equal(@p2.last_game_win, false)
+ assert_equal(nil, gr.log_summary_type)
end
def test_game_result_sennichite_draw
gr = ShogiServer::GameResultSennichiteDraw.new(@game, @p1, @p2)
assert_equal(@p1.last_game_win, false)
assert_equal(@p2.last_game_win, false)
+ assert_equal("sennichite", gr.log_summary_type)
end
end