3 ## Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch)
4 ## Copyright (C) 2007-2008 Daigo Moriwaki (daigo at debian dot org)
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.
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.
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
20 require 'shogi_server/util'
31 def sort_by_rate_with_randomness
32 return [LogPlayers.new,
33 ExcludeSacrificeGps500.new,
35 SortByRateWithRandomness.new(1200, 2400),
36 StartGameWithoutHumans.new]
40 return [LogPlayers.new,
41 ExcludeSacrificeGps500.new,
44 StartGameWithoutHumans.new]
48 return [LogPlayers.new,
49 ExcludeSacrificeGps500.new,
52 StartGameWithoutHumans.new]
56 logics = default_factory
57 logics.inject(players) do |result, item|
67 log_message("Floodgate: %s" % [self.class.to_s])
70 def include_newbie?(players)
71 return players.find{|a| a.rate == 0} == nil ? false : true
74 def less_than_one?(players)
76 log_warning("Floodgate: There should be at least one player.")
83 def log_players(players)
84 str_array = players.map do |one|
92 log_message("Floodgate: [Players] Nobody found.")
94 log_message("Floodgate: [Players] %s." % [str_array.join(", ")])
100 class LogPlayers < Pairing
107 class AbstractStartGame < Pairing
108 def start_game(p1, p2)
109 log_message("Floodgate: Starting a game: BLACK %s vs WHITE %s" % [p1.name, p2.name])
114 Game.new(p1.game_name, p1, p2, board)
117 def start_game_shuffle(pair)
119 start_game(pair.first, pair.last)
123 class StartGame < AbstractStartGame
127 log_warning("Floodgate: There should be more than one player (%d)." % [players.size])
131 log_warning("Floodgate: There are odd players (%d). %s will not be matched." %
132 [players.size, players.last.name])
136 while (players.size >= 2) do
137 pair = players.shift(2)
138 start_game_shuffle(pair)
143 # This tries to avoid a human-human match
145 class StartGameWithoutHumans < AbstractStartGame
150 log_warning("Floodgate: There should be more than one player (%d)." % [players.size])
152 elsif players.size == 2
153 start_game_shuffle(players)
158 humans = get_human_indexes(players)
159 log_message("Floodgate: There are (still) %d humans." % [humans.size])
160 break if humans.size < 2
162 pairing_possible = false
163 for i in 0..(humans.size-2) # -2
164 next if humans[i].odd?
165 if humans[i]+1 == humans[i+1]
166 pairing_possible = humans[i]
170 unless pairing_possible
171 log_message("Floodgate: No possible human-human match found")
175 current_index = pairing_possible
176 j = [0, current_index - 2].max
177 while j < players.size
178 break if players[j].is_computer?
184 # no computer player found
185 pairing_indexes << current_index << current_index+1
187 # a comupter player found
188 pairing_indexes << current_index << j
192 pair << players.delete_at(pairing_indexes.max)
193 pair << players.delete_at(pairing_indexes.min)
194 start_game_shuffle(pair)
197 while (players.size >= 2) do
198 pair = players.shift(2)
199 start_game_shuffle(pair)
205 def get_human_indexes(players)
207 for i in 0..(players.size-1)
208 ret << i if players[i].is_human?
214 class Randomize < Pairing
217 log_message("Floodgate: Randomize... before")
220 log_message("Floodgate: Randomized after")
225 class SortByRate < Pairing
228 log_message("Floodgate: Ordered by rate")
229 players.sort! {|a,b| a.rate <=> b.rate} # decendent order
234 class SortByRateWithRandomness < Pairing
235 def initialize(rand1, rand2)
237 @rand1, @rand2 = rand1, rand2
240 def match(players, desc=false)
243 players.each{|a| cur_rate[a] = a.rate ? a.rate + rand(@rand1) : rand(@rand2)}
244 players.sort!{|a,b| cur_rate[a] <=> cur_rate[b]}
245 players.reverse! if desc
246 log_players(players) do |one|
247 "%s %d (+ randomness %d)" % [one.name, one.rate, cur_rate[one] - one.rate]
252 class Swiss < Pairing
256 log_message("Floodgate: players are small enough to skip Swiss pairing: %d" % [players.size])
260 path = ShogiServer::League::Floodgate.history_file_path(players.first.game_name)
261 history = ShogiServer::League::Floodgate::History.factory(path)
265 winners = players.find_all {|pl| history.last_win?(pl.player_id)}
267 rest = players - winners
269 log_message("Floodgate: Ordering %d winners..." % [winners.size])
270 sbrwr_winners = SortByRateWithRandomness.new(800, 2500)
271 sbrwr_winners.match(winners, true)
273 log_message("Floodgate: Ordering the rest (%d)..." % [rest.size])
274 sbrwr_losers = SortByRateWithRandomness.new(200, 400)
275 sbrwr_losers.match(rest, true)
278 [winners, rest].each do |group|
279 group.each {|pl| players << pl}
284 class DeletePlayerAtRandom < Pairing
287 return if less_than_one?(players)
289 log_message("Floodgate: Deleted %s at random" % [one.name])
295 class DeletePlayerAtRandomExcept < Pairing
296 def initialize(except)
303 log_message("Floodgate: Deleting a player at rondom except %s" % [@except.name])
304 players.delete(@except)
305 DeletePlayerAtRandom.new.match(players)
306 players.push(@except)
310 class DeleteMostPlayingPlayer < Pairing
313 one = players.max_by {|a| a.win + a.loss}
314 log_message("Floodgate: Deleted the most playing player: %s (%d)" % [one.name, one.win + one.loss])
320 class DeleteLeastRatePlayer < Pairing
323 one = players.min_by {|a| a.rate}
324 log_message("Floodgate: Deleted the least rate player %s (%d)" % [one.name, one.rate])
330 class ExcludeSacrifice < Pairing
331 attr_reader :sacrifice
333 # @sacrifice a player id to be eliminated
334 def initialize(sacrifice)
336 @sacrifice = sacrifice
343 players.find{|a| a.player_id == @sacrifice}
344 log_message("Floodgate: Deleting the sacrifice %s" % [@sacrifice])
345 players.delete_if{|a| a.player_id == @sacrifice}
349 end # class ExcludeSacrifice
351 class ExcludeSacrificeGps500 < ExcludeSacrifice
353 super("gps500+e293220e3f8a3e59f79f6b0efffaa931")
357 class MakeEven < Pairing
360 return if players.size.even?
361 log_message("Floodgate: There are odd players (%d). Deleting one of them..." %
363 DeletePlayerAtRandom.new.match(players)