OSDN Git Service

Fix some lint warnings
[shogi-server/shogi-server.git] / shogi_server / league / floodgate.rb
index b7f26f5..da46a8a 100644 (file)
@@ -12,7 +12,7 @@ class League
       # ex. "floodgate-900-0"
       #
       def game_name?(str)
       # ex. "floodgate-900-0"
       #
       def game_name?(str)
-        return /^floodgate\-\d+\-\d+$/.match(str) ? true : false
+        return /^floodgate\-\d+\-\d+F?$/.match(str) ? true : false
       end
 
       def history_file_path(gamename)
       end
 
       def history_file_path(gamename)
@@ -27,11 +27,19 @@ class League
     #
     attr_reader :next_time
     attr_reader :league, :game_name
     #
     attr_reader :next_time
     attr_reader :league, :game_name
+    attr_reader :options
 
     def initialize(league, hash={})
       @league = league
 
     def initialize(league, hash={})
       @league = league
-      @next_time = hash[:next_time] || nil
-      @game_name = hash[:game_name] || "floodgate-900-0"
+      @next_time       = hash[:next_time] || nil
+      @game_name       = hash[:game_name] || "floodgate-900-0"
+      # Options will be updated by NextTimeGenerator and then passed to a
+      # pairing factory.
+      @options = {}
+      @options[:pairing_factory]     = hash[:pairing_factory] || "default_factory"
+      @options[:sacrifice]           = hash[:sacrifice] || "gps500+e293220e3f8a3e59f79f6b0efffaa931"
+      @options[:max_moves]           = hash[:max_moves] || Default_Max_Moves
+      @options[:least_time_per_move] = hash[:least_time_per_move] || Default_Least_Time_Per_Move
       charge if @next_time.nil?
     end
 
       charge if @next_time.nil?
     end
 
@@ -39,22 +47,52 @@ class League
       return Regexp.new(@game_name).match(str) ? true : false
     end
 
       return Regexp.new(@game_name).match(str) ? true : false
     end
 
+    def pairing_factory
+      return @options[:pairing_factory]
+    end
+
+    def sacrifice
+      return @options[:sacrifice]
+    end
+
+    def max_moves
+      return @options[:max_moves]
+    end
+
+    def least_time_per_move
+      return @options[:least_time_per_move]
+    end
+
     def charge
       ntg = NextTimeGenerator.factory(@game_name)
       if ntg
         @next_time = ntg.call(Time.now)
     def charge
       ntg = NextTimeGenerator.factory(@game_name)
       if ntg
         @next_time = ntg.call(Time.now)
+        @options[:pairing_factory]     = ntg.pairing_factory
+        @options[:sacrifice]           = ntg.sacrifice
+        @options[:max_moves]           = ntg.max_moves
+        @options[:least_time_per_move] = ntg.least_time_per_move
       else
         @next_time = nil
       end
     end
 
       else
         @next_time = nil
       end
     end
 
-    def match_game
+    # 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) &&
       players = @league.find_all_players do |pl|
         pl.status == "game_waiting" &&
         game_name?(pl.game_name) &&
-        pl.sente == nil
+        pl.sente == nil &&
+        pl.rated? # Only players who have player ID can participate in Floodgate (rating match)
       end
       end
-      Pairing.match(players)
+      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, @options)
     end
     
     #
     end
     
     #
@@ -80,10 +118,28 @@ class League
       end
     end
 
       end
     end
 
+    class AbstructNextTimeGenerator
+
+      attr_reader :pairing_factory
+      attr_reader :sacrifice
+      attr_reader :max_moves
+      attr_reader :least_time_per_move
+
+      # Constructor. 
+      #
+      def initialize
+        @pairing_factory     = "default_factory"
+        @sacrifice           = "gps500+e293220e3f8a3e59f79f6b0efffaa931"
+        @max_moves           = Default_Max_Moves
+        @least_time_per_move = Default_Least_Time_Per_Move
+      end
+    end
+
     # Schedule the next time from configuration files.
     #
     # Line format: 
     #   # This is a comment line
     # Schedule the next time from configuration files.
     #
     # Line format: 
     #   # This is a comment line
+    #   set <parameter_name> <value>
     #   DoW Time
     #   ...
     # where
     #   DoW Time
     #   ...
     # where
@@ -97,12 +153,29 @@ class League
     #   Sat 22:00
     #   Sun 13:00
     #
     #   Sat 22:00
     #   Sun 13:00
     #
-    class NextTimeGeneratorConfig
+    # 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
+    # * max_moves:
+    #   Sepcifies a number of max moves
+    #   ex. set max_moves 256
+    # * least_time_per_move:
+    #   Sepcifies a least time per move
+    #   ex. set least_time_per_move 0
+    #
+    class NextTimeGeneratorConfig < AbstructNextTimeGenerator
       
       # Constructor. 
       # Read configuration contents.
       #
       def initialize(lines)
       
       # Constructor. 
       # Read configuration contents.
       #
       def initialize(lines)
+        super()
         @lines = lines
       end
 
         @lines = lines
       end
 
@@ -114,7 +187,16 @@ class League
         # now.cweek 1-53
         # now.cwday 1(Monday)-7
         @lines.each do |line|
         # now.cweek 1-53
         # now.cwday 1(Monday)-7
         @lines.each do |line|
-          if %r!^\s*(\w+)\s+(\d{1,2}):(\d{1,2})! =~ 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*set\s+max_moves\s+(\d+)!
+            @max_moves = $1.chomp.to_i
+          when %r!^\s*set\s+least_time_per_move\s+(\d+)!
+            @least_time_per_move = $1.chomp.to_i
+          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?
             dow, hour, minute = $1, $2.to_i, $3.to_i
             dow_index = ::ShogiServer::parse_dow(dow)
             next if dow_index.nil?
@@ -123,6 +205,12 @@ class League
             time = DateTime::commercial(now.cwyear, now.cweek, dow_index, hour, minute) rescue next
             time += 7 if time <= now 
             candidates << time
             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)}
           end
         end
         candidates.map! {|dt| ::ShogiServer::datetime2time(dt)}
@@ -132,7 +220,14 @@ class League
 
     # Schedule the next time for floodgate-900-0: each 30 minutes
     #
 
     # Schedule the next time for floodgate-900-0: each 30 minutes
     #
-    class NextTimeGenerator_Floodgate_900_0
+    class NextTimeGenerator_Floodgate_900_0 < AbstructNextTimeGenerator
+
+      # Constructor. 
+      #
+      def initialize
+        super
+      end
+
       def call(now)
         if now.min < 30
           return Time.mktime(now.year, now.month, now.day, now.hour, 30)
       def call(now)
         if now.min < 30
           return Time.mktime(now.year, now.month, now.day, now.hour, 30)
@@ -144,7 +239,14 @@ class League
 
     # Schedule the next time for floodgate-3600-0: each 2 hours (odd hour)
     #
 
     # Schedule the next time for floodgate-3600-0: each 2 hours (odd hour)
     #
-    class NextTimeGenerator_Floodgate_3600_0
+    class NextTimeGenerator_Floodgate_3600_0 < AbstructNextTimeGenerator
+
+      # Constructor. 
+      #
+      def initialize
+        super
+      end
+
       def call(now)
         return Time.mktime(now.year, now.month, now.day, now.hour) + ((now.hour%2)+1)*3600
       end
       def call(now)
         return Time.mktime(now.year, now.month, now.day, now.hour) + ((now.hour%2)+1)*3600
       end
@@ -152,7 +254,14 @@ class League
 
     # Schedule the next time for debug: each 30 seconds.
     #
 
     # Schedule the next time for debug: each 30 seconds.
     #
-    class NextTimeGenerator_Debug
+    class NextTimeGenerator_Debug < AbstructNextTimeGenerator
+
+      # Constructor. 
+      #
+      def initialize
+        super
+      end
+
       def call(now)
         if now.sec < 30
           return Time.mktime(now.year, now.month, now.day, now.hour, now.min, 30)
       def call(now)
         if now.sec < 30
           return Time.mktime(now.year, now.month, now.day, now.hour, now.min, 30)
@@ -216,8 +325,13 @@ class League
       def load
         return unless @file.exist?
 
       def load
         return unless @file.exist?
 
-        @records = YAML.load_file(@file)
-        unless @records && @records.instance_of?(Array)
+        begin
+          @records = YAML.load_file(@file)
+          unless @records && @records.instance_of?(Array)
+            $logger.error "%s is not a valid yaml file. Instead, an empty array will be used and updated." % [@file]
+            @records = []
+          end
+        rescue
           $logger.error "%s is not a valid yaml file. Instead, an empty array will be used and updated." % [@file]
           @records = []
         end
           $logger.error "%s is not a valid yaml file. Instead, an empty array will be used and updated." % [@file]
           @records = []
         end
@@ -274,12 +388,12 @@ class League
         @@mutex.synchronize do
           records = @records.reverse
         end
         @@mutex.synchronize do
           records = @records.reverse
         end
-        rc = records.find do |rc|
+        ret = records.find do |rc|
           rc[:winner] && 
           rc[:loser]  && 
           (rc[:black] == player_id || rc[:white] == player_id)
         end
           rc[:winner] && 
           rc[:loser]  && 
           (rc[:black] == player_id || rc[:white] == player_id)
         end
-        return rc
+        return ret
       end
 
       def win_games(player_id)
       end
 
       def win_games(player_id)
@@ -287,10 +401,10 @@ class League
         @@mutex.synchronize do
           records = @records.reverse
         end
         @@mutex.synchronize do
           records = @records.reverse
         end
-        rc = records.find_all do |rc|
+        ret = records.find_all do |rc|
           rc[:winner] == player_id && rc[:loser]
         end
           rc[:winner] == player_id && rc[:loser]
         end
-        return rc
+        return ret
       end
 
       def loss_games(player_id)
       end
 
       def loss_games(player_id)
@@ -298,10 +412,10 @@ class League
         @@mutex.synchronize do
           records = @records.reverse
         end
         @@mutex.synchronize do
           records = @records.reverse
         end
-        rc = records.find_all do |rc|
+        ret = records.find_all do |rc|
           rc[:winner] && rc[:loser] == player_id
         end
           rc[:winner] && rc[:loser] == player_id
         end
-        return rc
+        return ret
       end
     end # class History
 
       end
     end # class History