OSDN Git Service

players.yaml is now read only. The server no longer writes last_game_win data to...
[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 require 'shogi_server/util'
21
22 module ShogiServer
23
24   class Pairing
25
26     class << self
27       def default_factory
28         return sort_by_rate_with_randomness
29       end
30
31       def sort_by_rate_with_randomness
32         return [LogPlayers.new,
33                 ExcludeSacrificeGps500.new,
34                 MakeEven.new,
35                 SortByRateWithRandomness.new(1200, 2400),
36                 StartGame.new]
37       end
38
39       def random_pairing
40         return [LogPlayers.new,
41                 ExcludeSacrificeGps500.new,
42                 MakeEven.new,
43                 Randomize.new,
44                 StartGame.new]
45       end
46
47       def match(players)
48         logics = default_factory
49         logics.inject(players) do |result, item|
50           item.match(result)
51           result
52         end
53       end
54     end # class << self
55
56
57     def match(players)
58       # to be implemented
59     end
60
61     def include_newbie?(players)
62       return players.find{|a| a.rate == 0} == nil ? false : true
63     end
64
65     def less_than_one?(players)
66       if players.size < 1
67         log_warning("Floodgate: There should be at least one player.")
68         return true
69       else
70         return false
71       end
72     end
73
74     def log_players(players)
75       str_array = players.map do |one|
76         if block_given?
77           yield one
78         else
79           one.name
80         end
81       end
82       if str_array.empty?
83         log_message("Floodgate: [Players] None is here.")
84       else
85         log_message("Floodgate: [Players] %s." % [str_array.join(", ")])
86       end
87     end
88   end # Pairing
89
90
91   class LogPlayers < Pairing
92     def match(players)
93       log_players(players)
94     end
95   end
96
97   class StartGame < Pairing
98     def match(players)
99       super
100       if players.size < 2
101         log_warning("Floodgate: There should be more than one player: %d" % [players.size])
102         return
103       end
104       if players.size.odd?
105         log_warning("Floodgate: There are odd players: %d. %s will not be matched." % 
106                     [players.size, players.last.name])
107       end
108
109       log_players(players)
110       while (players.size >= 2) do
111         pair = players.shift(2)
112         pair.shuffle!
113         start_game(pair.first, pair.last)
114       end
115     end
116
117     def start_game(p1, p2)
118       log_message("Floodgate: BLACK %s; WHITE %s" % [p1.name, p2.name])
119       p1.sente = true
120       p2.sente = false
121       Game.new(p1.game_name, p1, p2)
122     end
123   end
124
125   class Randomize < Pairing
126     def match(players)
127       super
128       log_message("Floodgate: Randomize... before")
129       log_players(players)
130       players.shuffle!
131       log_message("Floodgate: Randomized after")
132       log_players(players)
133     end
134   end # RadomPairing
135
136   class SortByRate < Pairing
137     def match(players)
138       super
139       log_message("Floodgate: Ordered by rate")
140       players.sort! {|a,b| a.rate <=> b.rate} # decendent order
141       log_players(players)
142     end
143   end
144
145   class SortByRateWithRandomness < Pairing
146     def initialize(rand1, rand2)
147       super()
148       @rand1, @rand2 = rand1, rand2
149     end
150
151     def match(players)
152       super
153       cur_rate = Hash.new
154       players.each{|a| cur_rate[a] = a.rate ? a.rate + rand(@rand1) : rand(@rand2)}
155       players.sort!{|a,b| cur_rate[a] <=> cur_rate[b]}
156       log_players(players) do |one|
157         "%s %d (randomness %d)" % [one.name, one.rate, cur_rate[one] - one.rate]
158       end
159     end
160   end
161
162   class DeletePlayerAtRandom < Pairing
163     def match(players)
164       super
165       return if less_than_one?(players)
166       one = players.choice
167       log_message("Floodgate: Deleted %s at random" % [one.name])
168       players.delete(one)
169       log_players(players)
170     end
171   end
172
173   class DeletePlayerAtRandomExcept < Pairing
174     def initialize(except)
175       super()
176       @except = except
177     end
178
179     def match(players)
180       super
181       log_message("Floodgate: Deleting a player at rondom except %s" % [@except.name])
182       players.delete(@except)
183       DeletePlayerAtRandom.new.match(players)
184       players.push(@except)
185     end
186   end
187   
188   class DeleteMostPlayingPlayer < Pairing
189     def match(players)
190       super
191       one = players.max_by {|a| a.win + a.loss}
192       log_message("Floodgate: Deleted the most playing player: %s (%d)" % [one.name, one.win + one.loss])
193       players.delete(one)
194       log_players(players)
195     end
196   end
197
198   class DeleteLeastRatePlayer < Pairing
199     def match(players)
200       super
201       one = players.min_by {|a| a.rate}
202       log_message("Floodgate: Deleted the least rate player %s (%d)" % [one.name, one.rate])
203       players.delete(one)
204       log_players(players)
205     end
206   end
207
208   class ExcludeSacrifice < Pairing
209     attr_reader :sacrifice
210
211     # @sacrifice a player id to be eliminated
212     def initialize(sacrifice)
213       super()
214       @sacrifice = sacrifice
215     end
216
217     def match(players)
218       super
219       if @sacrifice && 
220          players.size.odd? && 
221          players.find{|a| a.player_id == @sacrifice}
222          log_message("Floodgate: Deleting the sacrifice %s" % [@sacrifice])
223          players.delete_if{|a| a.player_id == @sacrifice}
224          log_players(players)
225       end
226     end
227   end # class ExcludeSacrifice
228
229   class ExcludeSacrificeGps500 < ExcludeSacrifice
230     def initialize
231       super("gps500+e293220e3f8a3e59f79f6b0efffaa931")
232     end
233   end
234
235   class MakeEven < Pairing
236     def match(players)
237       super
238       return if players.size.even?
239       log_message("Floodgate: there are odd players: %d. Deleting one..." % 
240                   [players.size])
241       DeletePlayerAtRandom.new.match(players)
242     end
243   end
244
245 end # ShogiServer