From: beatles Date: Sun, 15 Nov 2009 13:24:44 +0000 (+0000) Subject: Added a new feature, LoggingObserver, which logs a result of each game. X-Git-Tag: 20170902~148 X-Git-Url: http://git.osdn.net/view?p=shogi-server%2Fshogi-server.git;a=commitdiff_plain;h=bd654851220da09d022b6aa3bb9217ddfe6170e0 Added a new feature, LoggingObserver, which logs a result of each game. --- diff --git a/changelog b/changelog index 3ef2cd9..fe7794b 100644 --- a/changelog +++ b/changelog @@ -1,11 +1,27 @@ +2009-11-10 Daigo Moriwaki + + * [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 * [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) diff --git a/shogi_server/game.rb b/shogi_server/game.rb index 4aeaf3a..532c552 100644 --- a/shogi_server/game.rb +++ b/shogi_server/game.rb @@ -138,15 +138,20 @@ class Game 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 @@ -218,39 +223,39 @@ class Game 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 diff --git a/shogi_server/game_result.rb b/shogi_server/game_result.rb index 39a9c62..81a438a 100644 --- a/shogi_server/game_result.rb +++ b/shogi_server/game_result.rb @@ -32,7 +32,41 @@ class MonitorObserver 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 @@ -47,6 +81,10 @@ class GameResult 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 @@ -62,12 +100,13 @@ class GameResult 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 && @@ -75,7 +114,6 @@ class GameResult $options["floodgate-history"] add_observer League::Floodgate::History.factory end - end def process @@ -95,6 +133,20 @@ class GameResult 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 @@ -107,65 +159,77 @@ 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 @@ -173,14 +237,15 @@ 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 @@ -203,33 +268,48 @@ class GameResultOuteKaihiMoreWin < GameResultIllegalWin 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 @@ -237,22 +317,30 @@ class GameResultDraw < GameResult 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 - diff --git a/test/TC_game_result.rb b/test/TC_game_result.rb index f7c3d78..277ec11 100644 --- a/test/TC_game_result.rb +++ b/test/TC_game_result.rb @@ -4,6 +4,9 @@ require 'test/unit' 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 @@ -27,72 +30,84 @@ class TestGameResult < Test::Unit::TestCase 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