+ ntg = NextTimeGenerator.factory(@game_name)
+ if ntg
+ @next_time = ntg.call(Time.now)
+ @options[:pairing_factory] = ntg.pairing_factory
+ @options[:sacrifice] = ntg.sacrifice
+ else
+ @next_time = nil
+ end
+ end
+
+ # Returns an array of players who are allowed to participate in this
+ # Floodgate match
+ #
+ def select_players
+ players = @league.find_all_players do |pl|
+ pl.status == "game_waiting" &&
+ game_name?(pl.game_name) &&
+ pl.sente == nil &&
+ pl.rated? # Only players who have player ID can participate in Floodgate (rating match)
+ end
+ return players
+ end
+
+ def match_game
+ log_message("Starting Floodgate games...: %s, %s" % [@game_name, @options])
+ logics = Pairing.send(@options[:pairing_factory], @options)
+ Pairing.match(select_players(), logics)
+ end
+
+ #
+ #
+ class NextTimeGenerator
+ class << self
+ def factory(game_name)
+ ret = nil
+ conf_file_name = File.join($topdir, "#{game_name}.conf")
+
+ if $DEBUG
+ ret = NextTimeGenerator_Debug.new
+ elsif File.exists?(conf_file_name)
+ lines = IO.readlines(conf_file_name)
+ ret = NextTimeGeneratorConfig.new(lines)
+ elsif game_name == "floodgate-900-0"
+ ret = NextTimeGenerator_Floodgate_900_0.new
+ elsif game_name == "floodgate-3600-0"
+ ret = NextTimeGenerator_Floodgate_3600_0.new
+ end
+ return ret
+ end
+ end
+ end
+
+ class AbstructNextTimeGenerator
+
+ attr_reader :pairing_factory
+ attr_reader :sacrifice
+
+ # Constructor.
+ #
+ def initialize
+ @pairing_factory = "default_factory"
+ @sacrifice = "gps500+e293220e3f8a3e59f79f6b0efffaa931"
+ end
+ end
+
+ # Schedule the next time from configuration files.
+ #
+ # Line format:
+ # # This is a comment line
+ # set <parameter_name> <value>
+ # DoW Time
+ # ...
+ # where
+ # DoW := "Sun" | "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" |
+ # "Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" |
+ # "Friday" | "Saturday"
+ # Time := HH:MM
+ #
+ # For example,
+ # Sat 13:00
+ # Sat 22:00
+ # Sun 13:00
+ #
+ # Set parameters:
+ #
+ # * pairing_factory:
+ # Specifies a factory function name generating a pairing
+ # method which will be used in a specific Floodgate game.
+ # ex. set pairing_factory floodgate_zyunisen
+ # * sacrifice:
+ # Specifies a sacrificed player.
+ # ex. set sacrifice gps500+e293220e3f8a3e59f79f6b0efffaa931
+ #
+ class NextTimeGeneratorConfig < AbstructNextTimeGenerator
+
+ # Constructor.
+ # Read configuration contents.
+ #
+ def initialize(lines)
+ super()
+ @lines = lines
+ end
+
+ def call(now=Time.now)
+ if now.kind_of?(Time)
+ now = ::ShogiServer::time2datetime(now)
+ end
+ candidates = []
+ # now.cweek 1-53
+ # now.cwday 1(Monday)-7
+ @lines.each do |line|
+ case line
+ when %r!^\s*set\s+pairing_factory\s+(\w+)!
+ @pairing_factory = $1.chomp
+ when %r!^\s*set\s+sacrifice\s+(.*)!
+ @sacrifice = $1.chomp
+ when %r!^\s*(\w+)\s+(\d{1,2}):(\d{1,2})!
+ dow, hour, minute = $1, $2.to_i, $3.to_i
+ dow_index = ::ShogiServer::parse_dow(dow)
+ next if dow_index.nil?
+ next unless (0..23).include?(hour)
+ next unless (0..59).include?(minute)
+ time = DateTime::commercial(now.cwyear, now.cweek, dow_index, hour, minute) rescue next
+ time += 7 if time <= now
+ candidates << time
+ when %r!^\s*#!
+ # Skip comment line
+ when %r!^\s*$!
+ # Skip empty line
+ else
+ log_warning("Floodgate: Unsupported syntax in a next time generator config file: %s" % [line])
+ end
+ end
+ candidates.map! {|dt| ::ShogiServer::datetime2time(dt)}
+ return candidates.empty? ? nil : candidates.min
+ end
+ end
+
+ # Schedule the next time for floodgate-900-0: each 30 minutes
+ #
+ class NextTimeGenerator_Floodgate_900_0 < AbstructNextTimeGenerator
+
+ # Constructor.
+ #
+ def initialize
+ super
+ end
+
+ def call(now)