OSDN Git Service

Record Floodgate games' history
authorbeatles <beatles@b8c68f68-1e22-0410-b08e-880e1f8202b4>
Fri, 7 Nov 2008 07:14:46 +0000 (07:14 +0000)
committerbeatles <beatles@b8c68f68-1e22-0410-b08e-880e1f8202b4>
Fri, 7 Nov 2008 07:14:46 +0000 (07:14 +0000)
shogi_server/game.rb
shogi_server/league/floodgate.rb
showgame/controller/main.rb
test/TC_floodgate.rb
test/TC_functional.rb

index 09f0078..d266eb0 100644 (file)
 ## along with this program; if not, write to the Free Software
 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+require 'shogi_server/league/floodgate'
+require 'observer'
+
 module ShogiServer # for a namespace
 
+# MonitorObserver obserers GameResult to send messages to the monotors
+# watching the game
+#
+class MonitorObserver
+  def update(game_result)
+    game_result.game.each_monitor do |monitor|
+      monitor.write_safe("##[MONITOR][%s] %s\n" % [game_result.game.game_id, game_result.type])
+    end
+  end
+end
+
+# Base class for a game result
+#
 class GameResult
-  attr_reader :players, :black, :white
+  include Observable
+
+  # Game object
+  attr_reader :game
+  # Array of players
+  attr_reader :players
+  # Black player object
+  attr_reader :black
+  # White plyer object
+  attr_reader :white
+  # Command to send monitors such as '%%TORYO' etc...
+  attr_reader :result_type
 
   def initialize(game, p1, p2)
     @game = game
@@ -35,12 +62,31 @@ class GameResult
     @players.each do |player|
       player.status = "connected"
     end
+    @result_type = ""
+
+    regist_observers
+  end
+
+  def regist_observers
+    add_observer MonitorObserver.new
+
+    if League::Floodgate.game_name?(@game.game_id) &&
+       @game.sente.player_id &&
+       @game.gote.player_id &&
+       $options["floodgate-history"]
+      add_observer History.factory
+    end
   end
 
   def process
     raise "Implement me!"
   end
 
+  def notify
+    changed
+    notify_observers(self)
+  end
+
   def log(str)
     @game.log_game(str)
   end
@@ -49,11 +95,6 @@ class GameResult
     log(@game.board.to_s.gsub(/^/, "\'"))
   end
 
-  def notify_monitor(type)
-    @game.each_monitor do |monitor|
-      monitor.write_safe(sprintf("##[MONITOR][%s] %s\n", @game.game_id, type))
-    end
-  end
 end
 
 class GameResultWin < GameResult
@@ -90,7 +131,8 @@ class GameResultAbnormalWin < GameResultWin
     @loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
     log("%%TORYO\n")
     log_summary("abnormal")
-    notify_monitor("%%TORYO")
+    @result_type = "%%TORYO"
+    notify
   end
 end
 
@@ -99,7 +141,8 @@ class GameResultTimeoutWin < GameResultWin
     @winner.write_safe("#TIME_UP\n#WIN\n")
     @loser.write_safe( "#TIME_UP\n#LOSE\n")
     log_summary("time up")
-    notify_monitor("#TIME_UP")
+    @result_type = "#TIME_UP"
+    notify
   end
 end
 
@@ -110,7 +153,8 @@ class GameResultKachiWin < GameResultWin
     @loser.write_safe( "%KACHI\n#JISHOGI\n#LOSE\n")
     log("%%KACHI\n")
     log_summary("kachi")
-    notify_monitor("%%KACHI")
+    @result_type = "%%KACHI"
+    notify
   end
 end
 
@@ -121,7 +165,8 @@ class GameResultIllegalKachiWin < GameResultWin
     @loser.write_safe( "%KACHI\n#ILLEGAL_MOVE\n#LOSE\n")
     log("%%KACHI\n")
     log_summary("illegal kachi")
-    notify_monitor("%%KACHI")
+    @result_type = "%%KACHI"
+    notify
   end
 end
 
@@ -135,7 +180,8 @@ class GameResultIllegalWin < GameResultWin
     @winner.write_safe("#ILLEGAL_MOVE\n#WIN\n")
     @loser.write_safe( "#ILLEGAL_MOVE\n#LOSE\n")
     log_summary(@cause)
-    notify_monitor("#ILLEGAL_MOVE")
+    @result_type = "#ILLEGAL_MOVE"
+    notify
   end
 end
 
@@ -169,7 +215,8 @@ class GameReulstToryoWin < GameResultWin
     @loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
     log("%%TORYO\n")
     log_summary("toryo")
-    notify_monitor("%%TORYO")
+    @result_type = "%%TORYO"
+    notify
   end
 end
 
@@ -178,7 +225,8 @@ class GameResultOuteSennichiteWin < GameResultWin
     @winner.write_safe("#OUTE_SENNICHITE\n#WIN\n")
     @loser.write_safe( "#OUTE_SENNICHITE\n#LOSE\n")
     log_summary("oute_sennichite")
-    notify_monitor("#OUTE_SENNICHITE")
+    @result_type = "#OUTE_SENNICHITE"
+    notify
   end
 end
 
@@ -201,7 +249,8 @@ class GameResultSennichiteDraw < GameResultDraw
       player.write_safe("#SENNICHITE\n#DRAW\n")
     end
     log_summary("sennichite")
-    notify_monitor("#SENNICHITE")
+    @result_type = "#SENNICHITE"
+    notify
   end
 end
 
index 317dfe1..1d7d9e1 100644 (file)
@@ -1,3 +1,7 @@
+require 'thread'
+require 'ostruct'
+require 'pathname'
+
 module ShogiServer
 
 class League
@@ -43,7 +47,109 @@ class League
       end
       Pairing.match(players)
     end
+
+
+    #
+    #
+    class History
+      @@mutex = Mutex.new
+
+      class << self
+        def factory
+          file = Pathname.new $options["floodgate-history"]
+          return History.new file
+        end
+      end
+
+      # file_path_name is a Pathname object for this storage
+      #
+      def initialize(file_path_name)
+        @records = []
+        @max_records = 100
+        @file = file_path_name
+      end
+
+      # Return a hash describing the game_result
+      # :game_id: game id
+      # :black:   Black's player id
+      # :white:   White's player id
+      # :winner:  Winner's player id or nil for the game without a winner
+      # :loser:   Loser's player id or nil for the game without a loser
+      #
+      def make_record(game_result)
+        hash = Hash.new
+        hash[:game_id] = game_result.game.game_id
+        hash[:black]   = game_result.black.player_id
+        hash[:white]   = game_result.white.player_id
+        case game_result
+        when GameResultWin
+          hash[:winner] = game_result.winner.player_id
+          hash[:loser]  = game_result.loser.player_id
+        else
+          hash[:winner] = nil
+          hash[:loser]  = nil
+        end
+        return hash
+      end
+
+      def load
+        return unless @file.exist?
+
+        yaml = @file.open("r") {|f| f.read}
+        @records = YAML.load(yaml)
+      end
+
+      def save
+        begin
+          @file.open("w+") do |f| 
+            f << YAML.dump(@records)
+          end
+        rescue Errno::ENOSPC
+          # ignore
+        end
+      end
+
+      def update(game_result)
+        record = make_record(game_result)
+        @@mutex.synchronize do 
+          load
+          @records << record
+          while @records.size > @max_records
+            @records.shift
+          end
+          save
+        end
+      end
+      
+      def last_win?(player_id)
+        rc = last_valid_record(player_id)
+        return false unless rc
+        return rc[:winner] == player_id
+      end
+      
+      def last_lose?(player_id)
+        rc = last_valid_record(player_id)
+        return false unless rc
+        return rc[:loser] == player_id
+      end
+
+      def last_valid_record(player_id)
+        records = nil
+        @mutex.synchronize do
+          records = @records.reverse
+        end
+        rc = records.find do |rc|
+          rc[:winner] && 
+          rc[:loser]  && 
+          (rc[:black] == player_id || rc[:while] == player_id)
+        end
+        return rc
+      end
+    end # class History
+
+
   end # class Floodgate
 
+
 end # class League
 end # module ShogiServer
index c354a9a..cf32f34 100644 (file)
@@ -49,14 +49,11 @@ class MainController < Ramaze::Controller
   end
 
   def sfen(str)
-    sfen = "sfen %s" % [ShogiServer::Usi.unescape(str)]
-    Ramaze::Log.warn(sfen)
-
     transport = Thrift::BufferedTransport.new(Thrift::Socket.new('localhost', 9090))
     client    = ShogiGraphic::Client.new(Thrift::BinaryProtocol.new(transport))
 
     transport.open
-    result = client.usi2png(sfen)
+    result = client.usi2png(str)
     transport.close
 
     Ramaze::Log.warn("result fail") unless result
index 94b7a60..f9ac2a7 100644 (file)
@@ -363,3 +363,22 @@ class TestExcludeSacrifice < Test::Unit::TestCase
   end
 end
 
+
+class TestFloodgateHistory < Test::Unit::TestCase
+  def setup
+    @file = Pathname.new(File.join(File.dirname(__FILE__), "floodgate_history.yaml"))
+    @history = ShogiServer::League::Floodgate::History.new @file
+  end
+
+  def teaup
+    @file.delete if @file.exist?
+  end
+
+  def test_new
+    file = Pathname.new(File.join(File.dirname(__FILE__), "hoge.yaml"))
+    history = ShogiServer::League::Floodgate::History.new file
+    history.save
+    assert file.exist?
+    file.delete if file.exist?
+  end
+end
index 41558a1..ee5ef41 100644 (file)
@@ -240,3 +240,12 @@ class TestDuplicatedMoves < BaseClient
   end
 end
 
+class TestChatCommand < BaseClient
+  def test_chat
+    cmd "%%CHAT Hello"
+    sleep 1
+    str = read(@socket2)
+    puts str   
+    assert("", str)
+  end
+end