OSDN Git Service

Pairing classes are located in a separate source file.
authorbeatles <beatles@b8c68f68-1e22-0410-b08e-880e1f8202b4>
Fri, 15 Feb 2008 13:00:18 +0000 (13:00 +0000)
committerbeatles <beatles@b8c68f68-1e22-0410-b08e-880e1f8202b4>
Fri, 15 Feb 2008 13:00:18 +0000 (13:00 +0000)
Added ExcludeSacrifice.

changelog
pairing.rb [new file with mode: 0644]
shogi-server

index 039fbcc..c19a1e6 100644 (file)
--- a/changelog
+++ b/changelog
@@ -5,6 +5,12 @@
            prevous games (his/her point of view, not the server's) are 
            paired first, and then the others are matched. This is the 
            default option now.
+         - Pairing classes are located in a separate source file,
+           pairing.rb. The file is 'load'ed each time to be used,
+           meaning that modifying the code will be applied to a running
+           server.
+         - You can specify a single player who will be out of pairing
+           when there are odd players waiting for Floodgate.
 
 2008-02-13 Daigo Moriwaki <daigo at debian dot org>
 
diff --git a/pairing.rb b/pairing.rb
new file mode 100644 (file)
index 0000000..4cca5ab
--- /dev/null
@@ -0,0 +1,129 @@
+module ShogiServer
+
+  class Pairing
+
+    class << self
+      def default_pairing
+        return SwissPairing.new
+        #return ExcludeSacrifice.new(SwissPairing.new)
+        #return RandomPairing.new
+        #return ExcludeSacrifice.new(RandomPairing.new)
+      end
+    end
+
+    def match(players)
+      if players.size < 2
+        log_message("Floodgate[%s]: too few players [%d]" % 
+                    [self.class, players.size])
+      else
+        log_message("Floodgate[%s]: found %d players. Pairing them..." % 
+                    [self.class, players.size])
+      end
+    end
+
+    def start_game(p1, p2)
+      p1.sente = true
+      p2.sente = false
+      Game.new(p1.game_name, p1, p2)
+    end
+
+    def delete_player_at_random(players)
+      return players.delete_at(rand(players.size))
+    end
+
+    def delete_player_at_random_except(players, a_player)
+      candidates = players - [a_player]
+      return delete_player_at_random(candidates)
+    end
+    
+    def delete_most_playing_player(players)
+      max_player = players.max {|a,b| a.win + a.loss <=> b.win + b.loss}
+      return players.delete(max_player)
+    end
+
+    def delete_least_rate_player(players)
+      min_player = players.min {|a,b| a.rate <=> b.rate}
+      return players.delete(min_player)
+    end
+
+    def pairing_and_start_game(players)
+      return if players.size < 2
+      if players.size % 2 == 1
+        log_warning("#Players should be even: %d" % [players.size])
+        return
+      end
+      sorted = players.sort{ rand < 0.5 ? 1 : -1 }
+
+      pairs = [[sorted.shift]]
+      while !sorted.empty? do
+        if pairs.last.size < 2
+          pairs.last << sorted.shift
+        else
+          pairs << [sorted.shift]
+        end 
+      end
+      pairs.each do |pair|
+        start_game(pair.first, pair.last)
+      end
+    end
+  end # Pairing
+
+  class RandomPairing < Pairing
+    def match(players)
+      super
+      return if players.size < 2
+
+      if players.size % 2 == 1
+        delete_most_playing_player(players)
+      end
+      pairing_and_start_game(players)
+    end
+  end # RadomPairing
+
+  class SwissPairing < Pairing
+    def match(players)
+      super
+      return if players.size < 2
+
+      win_players = players.find_all {|a| a.last_game_win?}
+      remains     = players - win_players
+      if win_players.size >= 2
+        if win_players.size % 2 == 1
+          remains << delete_least_rate_player(win_players)
+        end         
+        pairing_and_start_game(win_players)
+      else
+        remains.concat(win_players)
+      end
+      return if remains.size < 2
+      if remains.size % 2 == 1
+        delete_most_playing_player(remains)
+      end
+      pairing_and_start_game(remains)
+    end
+  end # SwissPairing
+
+  class ExcludeSacrifice
+    attr_accessor :sacrifice
+
+    def initialize(pairing)
+      @pairing  = pairing
+      @sacrifice = "gps500+e293220e3f8a3e59f79f6b0efffaa931"
+    end
+
+    def match(players)
+      if @sacrifice && 
+         players.size % 2 == 1 && 
+         players.find{|a| a.id == @sacrifice}
+        log_message("Floodgate: first, exclude %s" % [@sacrifice])
+        players.delete_if{|a| a.id == @sacrifice}
+      end
+      @pairing.match(players)
+    end
+
+    # Delegate to @pairing
+    def method_missing(message, *arg)
+      @pairing.send(message, *arg)
+    end
+  end # class ExcludeSacrifice
+end # ShogiServer
index 24f7ff1..e753b4c 100755 (executable)
@@ -89,88 +89,6 @@ Revision = "$Revision$".gsub(/[^\.\d]/, '')
 
 class League
 
-  class Pairing
-    def match(players)
-      if players.size < 2
-        log_message("Floodgate: too few players [%d]" % [players.size])
-      else
-        log_message("Floodgate: found %d players. Making games..." % [players.size])
-      end
-    end
-
-    def start_game(p1, p2)
-      p1.sente = true
-      p2.sente = false
-      Game.new(p1.game_name, p1, p2)
-    end
-    
-    def delete_most_playing_player(players)
-      max_player = players.max {|a,b| a.win + a.loss <=> b.win + b.loss}
-      return players.delete(max_player)
-    end
-
-    def delete_least_rate_player(players)
-      min_player = players.min {|a,b| a.rate <=> b.rate}
-      return players.delete(min_player)
-    end
-
-    def pairing_and_start_game(players)
-      return if players.size < 2
-      if players.size % 2 == 1
-        log_warning("#Players should be even: %d" % [players.size])
-        return
-      end
-      sorted = players.sort{ rand < 0.5 ? 1 : -1 }
-
-      pairs = [[sorted.shift]]
-      while !sorted.empty? do
-        if pairs.last.size < 2
-          pairs.last << sorted.shift
-        else
-          pairs << [sorted.shift]
-        end 
-      end
-      pairs.each do |pair|
-        start_game(pair.first, pair.last)
-      end
-    end
-  end # Pairing
-
-  class RandomPairing < Pairing
-    def match(players)
-      super
-      return if players.size < 2
-
-      if players.size % 2 == 1
-        delete_most_playing_player(players)
-      end
-      pairing_and_start_game(players)
-    end
-  end # RadomPairing
-
-  class SwissPairing < Pairing
-    def match(players)
-      super
-      return if players.size < 2
-
-      win_players = players.find_all {|a| a.last_game_win?}
-      remains     = players - win_players
-      if win_players.size >= 2
-        if win_players.size % 2 == 1
-          remains << delete_least_rate_player(win_players)
-        end         
-        pairing_and_start_game(win_players)
-      else
-        remains.concat(win_players)
-      end
-      return if remains.size < 2
-      if remains.size % 2 == 1
-        delete_most_playing_player(remains)
-      end
-      pairing_and_start_game(remains)
-    end
-  end # SwissPairing
-
   class Floodgate
     class << self
       def game_name?(str)
@@ -181,7 +99,6 @@ class League
     def initialize(league)
       @league = league
       @next_time = nil
-      @pairing = SwissPairing.new
       charge
     end
 
@@ -230,7 +147,8 @@ class League
         Floodgate.game_name?(pl.game_name) &&
         pl.sente == nil
       end
-      @pairing.match(players)
+      load File.join(File.dirname(__FILE__), "pairing.rb")
+      Pairing.default_pairing.match(players)
     end
   end # class Floodgate