## 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
@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
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
@loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
log("%%TORYO\n")
log_summary("abnormal")
- notify_monitor("%%TORYO")
+ @result_type = "%%TORYO"
+ notify
end
end
@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
@loser.write_safe( "%KACHI\n#JISHOGI\n#LOSE\n")
log("%%KACHI\n")
log_summary("kachi")
- notify_monitor("%%KACHI")
+ @result_type = "%%KACHI"
+ notify
end
end
@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
@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
@loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
log("%%TORYO\n")
log_summary("toryo")
- notify_monitor("%%TORYO")
+ @result_type = "%%TORYO"
+ notify
end
end
@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
player.write_safe("#SENNICHITE\n#DRAW\n")
end
log_summary("sennichite")
- notify_monitor("#SENNICHITE")
+ @result_type = "#SENNICHITE"
+ notify
end
end
+require 'thread'
+require 'ostruct'
+require 'pathname'
+
module ShogiServer
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