X-Git-Url: http://git.sourceforge.jp/view?p=shogi-server%2Fshogi-server.git;a=blobdiff_plain;f=shogi_server%2Fpairing.rb;h=c4819c74973231832a5325c7e8c3683eabad45a7;hp=992dbcb9e076c25aaca4dba6a1d51f208fc488c3;hb=893e6e974a00a76cab6a22bcfb45ef8bd0d1e05f;hpb=6165fe664fa1c71e5a6c16fe75fa30d913b32336 diff --git a/shogi_server/pairing.rb b/shogi_server/pairing.rb index 992dbcb..c4819c7 100644 --- a/shogi_server/pairing.rb +++ b/shogi_server/pairing.rb @@ -25,7 +25,7 @@ module ShogiServer class << self def default_factory - return sort_by_rate_with_randomness + return swiss_pairing end def sort_by_rate_with_randomness @@ -33,7 +33,7 @@ module ShogiServer ExcludeSacrificeGps500.new, MakeEven.new, SortByRateWithRandomness.new(1200, 2400), - StartGame.new] + StartGameWithoutHumans.new] end def random_pairing @@ -41,7 +41,15 @@ module ShogiServer ExcludeSacrificeGps500.new, MakeEven.new, Randomize.new, - StartGame.new] + StartGameWithoutHumans.new] + end + + def swiss_pairing + return [LogPlayers.new, + ExcludeSacrificeGps500.new, + MakeEven.new, + Swiss.new, + StartGameWithoutHumans.new] end def match(players) @@ -56,6 +64,7 @@ module ShogiServer def match(players) # to be implemented + log_message("Floodgate: %s" % [self.class.to_s]) end def include_newbie?(players) @@ -80,7 +89,7 @@ module ShogiServer end end if str_array.empty? - log_message("Floodgate: [Players] None is here.") + log_message("Floodgate: [Players] Nobody found.") else log_message("Floodgate: [Players] %s." % [str_array.join(", ")]) end @@ -90,35 +99,115 @@ module ShogiServer class LogPlayers < Pairing def match(players) + super log_players(players) end end - class StartGame < Pairing + class AbstractStartGame < Pairing + def start_game(p1, p2) + log_message("Floodgate: Starting a game: BLACK %s vs WHITE %s" % [p1.name, p2.name]) + p1.sente = true + p2.sente = false + board = Board.new + board.initial + Game.new(p1.game_name, p1, p2, board) + end + + def start_game_shuffle(pair) + pair.shuffle! + start_game(pair.first, pair.last) + end + end + + class StartGame < AbstractStartGame def match(players) super if players.size < 2 - log_warning("Floodgate: There should be more than one player: %d" % [players.size]) + log_warning("Floodgate: There should be more than one player (%d)." % [players.size]) return end if players.size.odd? - log_warning("Floodgate: There are odd players: %d. %s will not be matched." % + log_warning("Floodgate: There are odd players (%d). %s will not be matched." % [players.size, players.last.name]) end log_players(players) while (players.size >= 2) do pair = players.shift(2) - pair.shuffle! - start_game(pair.first, pair.last) + start_game_shuffle(pair) end end + end - def start_game(p1, p2) - log_message("Floodgate: BLACK %s; WHITE %s" % [p1.name, p2.name]) - p1.sente = true - p2.sente = false - Game.new(p1.game_name, p1, p2) + # This tries to avoid a human-human match + # + class StartGameWithoutHumans < AbstractStartGame + def match(players) + super + log_players(players) + if players.size < 2 + log_warning("Floodgate: There should be more than one player (%d)." % [players.size]) + return + elsif players.size == 2 + start_game_shuffle(players) + return + end + + loop do + humans = get_human_indexes(players) + log_message("Floodgate: There are (still) %d humans." % [humans.size]) + break if humans.size < 2 + + pairing_possible = false + for i in 0..(humans.size-2) # -2 + next if humans[i].odd? + if humans[i]+1 == humans[i+1] + pairing_possible = i + break + end + end + unless pairing_possible + log_message("Floodgate: No possible human-human match found") + break + end + + current_index = pairing_possible + j = (current_index == 0 ? current_index : current_index-1) + while j < players.size + break if players[j].is_computer? + j += 1 + end + + pairing_indexes = [] + if j == players.size + # no computer player found + pairing_indexes << current_index << current_index+1 + else + # a comupter player found + pairing_indexes << current_index << j + end + + pair = [] + pair << players.delete_at(pairing_indexes.max) + pair << players.delete_at(pairing_indexes.min) + start_game_shuffle(pair) + end # loop + + while (players.size >= 2) do + pair = players.shift(2) + start_game_shuffle(pair) + end + end + + private + + def get_human_indexes(players) + ret = [] + for i in 0..(players.size-1) + ret << i if players[i].is_human? + end + return ret end end @@ -148,13 +237,46 @@ module ShogiServer @rand1, @rand2 = rand1, rand2 end - def match(players) - super + def match(players, desc=false) + super(players) cur_rate = Hash.new players.each{|a| cur_rate[a] = a.rate ? a.rate + rand(@rand1) : rand(@rand2)} players.sort!{|a,b| cur_rate[a] <=> cur_rate[b]} + players.reverse! if desc log_players(players) do |one| - "%s %d (randomness %d)" % [one.name, one.rate, cur_rate[one] - one.rate] + "%s %d (+ randomness %d)" % [one.name, one.rate, cur_rate[one] - one.rate] + end + end + end + + class Swiss < Pairing + def match(players) + super + if players.size < 3 + log_message("Floodgate: players are small enough to skip Swiss pairing: %d" % [players.size]) + return + end + + path = ShogiServer::League::Floodgate.history_file_path(players.first.game_name) + history = ShogiServer::League::Floodgate::History.factory(path) + + winners = [] + if history + winners = players.find_all {|pl| history.last_win?(pl.player_id)} + end + rest = players - winners + + log_message("Floodgate: Ordering %d winners..." % [winners.size]) + sbrwr_winners = SortByRateWithRandomness.new(800, 2500) + sbrwr_winners.match(winners, true) + + log_message("Floodgate: Ordering the rest (%d)..." % [rest.size]) + sbrwr_losers = SortByRateWithRandomness.new(200, 400) + sbrwr_losers.match(rest, true) + + players.clear + [winners, rest].each do |group| + group.each {|pl| players << pl} end end end @@ -236,7 +358,7 @@ module ShogiServer def match(players) super return if players.size.even? - log_message("Floodgate: there are odd players: %d. Deleting one..." % + log_message("Floodgate: There are odd players (%d). Deleting one of them..." % [players.size]) DeletePlayerAtRandom.new.match(players) end