OSDN Git Service

18f0925bca6427d1dc5686879cd902d858002250
[shogi-server/shogi-server.git] / shogi_server / pairing.rb
1 ## $Id$
2
3 ## Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch)
4 ## Copyright (C) 2007-2008 Daigo Moriwaki (daigo at debian dot org)
5 ##
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 2 of the License, or
9 ## (at your option) any later version.
10 ##
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ## GNU General Public License for more details.
15 ##
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, write to the Free Software
18 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 module ShogiServer
21
22   class Pairing
23
24     class << self
25       def default_pairing
26         #return SwissPairing.new
27         return ExcludeSacrifice.new(SwissPairing.new)
28         #return RandomPairing.new
29         #return ExcludeSacrifice.new(RandomPairing.new)
30       end
31     end
32
33     def match(players)
34       if players.size < 2
35         log_message("Floodgate[%s]: too few players [%d]" % 
36                     [self.class, players.size])
37       else
38         log_message("Floodgate[%s]: found %d players. Pairing them..." % 
39                     [self.class, players.size])
40       end
41     end
42
43     def start_game(p1, p2)
44       p1.sente = true
45       p2.sente = false
46       Game.new(p1.game_name, p1, p2)
47     end
48
49     def include_newbie?(players)
50       return players.find{|a| a.rate == 0} == nil ? false : true
51     end
52
53     def delete_player_at_random(players)
54       return players.delete_at(rand(players.size))
55     end
56
57     def delete_player_at_random_except(players, a_player)
58       candidates = players - [a_player]
59       return delete_player_at_random(candidates)
60     end
61     
62     def delete_most_playing_player(players)
63       # TODO ??? undefined method `<=>' for nil:NilClass
64       max_player = players.max {|a,b| a.win + a.loss <=> b.win + b.loss}
65       return players.delete(max_player)
66     end
67
68     def delete_least_rate_player(players)
69       min_player = players.min {|a,b| a.rate <=> b.rate}
70       return players.delete(min_player)
71     end
72
73     def pairing_and_start_game(players)
74       return if players.size < 2
75       if players.size % 2 == 1
76         log_warning("#Players should be even: %d" % [players.size])
77         return
78       end
79       sorted = players.sort{ rand < 0.5 ? 1 : -1 }
80
81       pairs = [[sorted.shift]]
82       while !sorted.empty? do
83         if pairs.last.size < 2
84           pairs.last << sorted.shift
85         else
86           pairs << [sorted.shift]
87         end 
88       end
89       pairs.each do |pair|
90         start_game(pair.first, pair.last)
91       end
92     end
93   end # Pairing
94
95   class RandomPairing < Pairing
96     def match(players)
97       super
98       return if players.size < 2
99
100       if players.size % 2 == 1
101         delete_player_at_random(players)
102       end
103       pairing_and_start_game(players)
104     end
105   end # RadomPairing
106
107   class SwissPairing < Pairing
108     def match(players)
109       super
110       return if players.size < 2
111
112       win_players = players.find_all {|a| a.last_game_win?}
113       remains     = players - win_players
114       if win_players.size >= 2
115         if win_players.size % 2 == 1
116 #          if include_newbie?(win_players)
117             remains << delete_player_at_random(win_players)
118 #          else
119 #            remains << delete_least_rate_player(win_players)
120 #          end
121         end         
122         pairing_and_start_game(win_players)
123       else
124         remains.concat(win_players)
125       end
126       return if remains.size < 2
127       if remains.size % 2 == 1
128         delete_player_at_random(remains)
129         # delete_most_playing_player(remains)
130       end
131       pairing_and_start_game(remains)
132     end
133   end # SwissPairing
134
135   class ExcludeSacrifice
136     attr_accessor :sacrifice
137
138     def initialize(pairing)
139       @pairing  = pairing
140       @sacrifice = "gps500+e293220e3f8a3e59f79f6b0efffaa931"
141     end
142
143     def match(players)
144       if @sacrifice && 
145          players.size % 2 == 1 && 
146          players.find{|a| a.player_id == @sacrifice}
147         log_message("Floodgate: first, exclude %s" % [@sacrifice])
148         players.delete_if{|a| a.player_id == @sacrifice}
149       end
150       @pairing.match(players)
151     end
152
153     # Delegate to @pairing
154     def method_missing(message, *arg)
155       @pairing.send(message, *arg)
156     end
157   end # class ExcludeSacrifice
158 end # ShogiServer