class Pairing
class << self
- def default_factory
- return least_diff_pairing
+ def default_factory(options)
+ return least_diff_pairing(options)
end
- def sort_by_rate_with_randomness
+ def sort_by_rate_with_randomness(options)
return [LogPlayers.new,
- ExcludeSacrificeGps500.new,
+ ExcludeSacrifice.new(options[:sacrifice]),
MakeEven.new,
SortByRateWithRandomness.new(1200, 2400),
StartGameWithoutHumans.new]
end
- def random_pairing
+ def random_pairing(options)
return [LogPlayers.new,
- ExcludeSacrificeGps500.new,
+ ExcludeSacrifice.new(options[:sacrifice]),
MakeEven.new,
Randomize.new,
StartGameWithoutHumans.new]
end
- def swiss_pairing
+ def swiss_pairing(options)
return [LogPlayers.new,
- ExcludeSacrificeGps500.new,
+ ExcludeSacrifice.new(options[:sacrifice]),
MakeEven.new,
Swiss.new,
StartGameWithoutHumans.new]
end
- def least_diff_pairing
+ def least_diff_pairing(options)
return [LogPlayers.new,
- ExcludeSacrificeGps500.new,
+ ExcludeSacrifice.new(options[:sacrifice]),
MakeEven.new,
LeastDiff.new,
StartGameWithoutHumans.new]
end
- def match(players)
- logics = default_factory
+ def floodgate_zyunisen(options)
+ return [LogPlayers.new,
+ ExcludeUnratedPlayers.new,
+ ExcludeSacrifice.new(options[:sacrifice]),
+ MakeEven.new,
+ LeastDiff.new,
+ StartGameWithoutHumans.new]
+ end
+
+ def match(players, logics, options)
logics.inject(players) do |result, item|
+ item.set_options(options)
item.match(result)
result
end
end
end # class << self
+ def initialize
+ @options = {}
+ end
+
+ def set_options(options)
+ @options.merge!(options)
+ end
# Make matches among players.
# @param players an array of players, which should be updated destructively
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 = Board.new(@options)
board.initial
Game.new(p1.game_name, p1, p2, board)
end
def match(players)
super
if players.size < 2
- log_warning("Floodgate: There should be more than one player (%d)." % [players.size])
+ log_message("Floodgate: There are less than two players: %d" % [players.size])
return
end
if players.size.odd?
super
log_players(players)
if players.size < 2
- log_warning("Floodgate: There should be more than one player (%d)." % [players.size])
+ log_message("Floodgate: There are less than two players: %d" % [players.size])
return
elsif players.size == 2
start_game_shuffle(players)
# @sacrifice a player id to be eliminated
def initialize(sacrifice)
super()
- @sacrifice = sacrifice
+ @sacrifice = sacrifice || "gps500+e293220e3f8a3e59f79f6b0efffaa931"
end
def match(players)
players.shuffle
end
- # Returns a player's rate value.
+ # Update estimated rate of a player.
# 1. If it has a valid rate, return the rate.
- # 2. If it has no valid rate, return average of the following values:
- # a. For games it won, the opponent's rate + 100
- # b. For games it lost, the opponent's rate - 100
- # (if the opponent has no valid rate, count out the game)
- # (if there are not such games, return 2150 (default value)
+ # 2. If it has no valid rate, return:
+ # a. If it won the last game, the opponent's rate + 200
+ # b. If it lost the last game, the opponent's rate - 200
+ # c. otherwise, return 2150 (default value)
#
- def get_player_rate(player, history)
- return player.rate if player.rate != 0
- return 2150 unless history
-
- count = 0
- sum = 0
-
- history.win_games(player.player_id).each do |g|
- next unless g[:loser]
- name = g[:loser].split("+")[0]
- p = $league.find(name)
- if p && p.rate != 0
- count += 1
- sum += p.rate + 100
- end
+ def estimate_rate(player, history)
+ player.estimated_rate = 2150 # default value
+
+ unless history
+ log_message("Floodgate: Without game history, estimated %s's rate: %d" % [player.name, player.estimated_rate])
+ return
end
- history.loss_games(player.player_id).each do |g|
- next unless g[:winner]
- name = g[:winner].split("+")[0]
- p = $league.find(name)
- if p && p.rate != 0
- count += 1
- sum += p.rate - 100
- end
+
+ g = history.last_valid_game(player.player_id)
+ unless g
+ log_message("Floodgate: Without any valid games in history, estimated %s's rate: %d" % [player.name, player.estimated_rate])
+ return
+ end
+
+ opponent_id = nil
+ win = true
+ case player.player_id
+ when g[:winner]
+ opponent_id = g[:loser]
+ win = true
+ when g[:loser]
+ opponent_id = g[:winner]
+ win = false
+ else
+ log_warning("Floodgate: The last valid game is invalid for %s!" % [player.name])
+ log_message("Floodgate: Estimated %s's rate: %d" % [player.name, player.estimated_rate])
+ return
+ end
+
+ opponent_name = opponent_id.split("+")[0]
+ p = $league.find(opponent_name)
+ unless p
+ log_message("Floodgate: No active opponent found. Estimated %s's rate: %d" % [player.name, player.estimated_rate])
+ return
end
- estimate = (count == 0 ? 2150 : sum/count)
- log_message("Floodgate: Estimated rate of %s is %d" % [player.name, estimate])
- return estimate
+ opponent_rate = 0
+ if p.rate != 0
+ opponent_rate = p.rate
+ elsif p.estimated_rate != 0
+ opponent_rate = p.estimated_rate
+ end
+
+ if opponent_rate != 0
+ player.estimated_rate = opponent_rate + (win ? 200 : -200)
+ end
+
+ log_message("Floodgate: Estimated %s's rate: %d" % [player.name, player.estimated_rate])
+ end
+
+ # Return a player's rate based on its actual rate or estimated rate.
+ #
+ def get_player_rate(player, history)
+ if player.rate != 0
+ return player.rate
+ elsif player.estimated_rate != 0
+ return player.estimated_rate
+ else
+ estimate_rate(player, history)
+ return player.estimated_rate
+ end
end
def calculate_diff_with_penalty(players, history)
if p1.is_human? && p2.is_human?
ret += 800
end
+
+ # 2.3 a match with likely kin players
+ if (p1.player_id[0..6] == p2.player_id[0..6])
+ ret += 800
+ elsif (p1.player_id[0..3] == p2.player_id[0..3])
+ ret += 400
+ end
end
ret
return players
end
+ # Reset estimated rate
+ players.each {|p| p.estimated_rate = 0}
+
# 10 trials
matches = []
scores = []
min_score = s
end
end
- log_message("Floodgate: the least score %d (%d per player) [%s]" % [min_score, min_score/players.size, scores.join(" ")])
+ log_message("Floodgate: the least score %d (%d per game) [%s]" % [min_score, min_score/players.size*2, scores.join(" ")])
players.replace(matches[min_index])
end
end
+ # This pairing method excludes unrated players
+ #
+ class ExcludeUnratedPlayers < Pairing
+
+ def match(players)
+ super
+
+ log_message("Floodgate: Deleting unrated players...")
+ players.delete_if{|a| a.rate == 0}
+ log_players(players)
+ end
+ end # class ExcludeUnratedPlayers
+
end # ShogiServer