module TournamentSystem::Algorithm::SingleBracket

This module provides algorithms for dealing with single bracket elimination tournament systems.

Public Instance Methods

guess_round(teams_count, matches_count) click to toggle source

Guess the next round (starting at 0) for a single bracket tournament.

@param teams_count [Integer] the number of teams @param matches_count [Integer] the number of existing matches @return [Integer] next round number @raise [ArgumentError] when the number of matches does not add up

# File lib/tournament_system/algorithm/single_bracket.rb, line 33
def guess_round(teams_count, matches_count)
  rounds = total_rounds(teams_count)
  total_teams = max_teams(rounds)

  # Make sure we don't have too many matches
  raise ArgumentError, 'Too many matches' unless total_teams >= matches_count

  round = rounds - Math.log2(total_teams - matches_count)
  # Make sure we don't have some weird number of matches
  raise ArgumentError, 'Invalid number of matches' unless (round % 1).zero?

  round.to_i
end
max_teams(rounds) click to toggle source

Calculates the maximum number of teams that can play in a single bracket tournament with a given number of rounds.

@param rounds [Integer] the number of rounds @return [Integer] number of teams that could play

# File lib/tournament_system/algorithm/single_bracket.rb, line 23
def max_teams(rounds)
  2**rounds
end
padd_teams(teams) click to toggle source

@deprecated Please use {Util.padd_teams_pow2} instead.

# File lib/tournament_system/algorithm/single_bracket.rb, line 48
def padd_teams(teams)
  message = 'NOTE: padd_teams is now deprecated in favour of Util.padd_teams_even.'\
            'It will be removed in the next major version'\
            "SingleBracket.padd_teams called from #{Gem.location_of_caller.join(':')}"
  warn message unless Gem::Deprecate.skip

  Util.padd_teams_pow2(teams)
end
seed(teams) click to toggle source

Seed teams for a single bracket tournament.

Seed in a way that teams earlier in teams always win against later ones, the first team plays the second in the finals, the 3rd and 4th get nocked out in the semi-finals, etc.

Designed to be used with {GroupPairing#adjacent}.

@param teams [Array<Team>] @return [Array<team>] @raise [ArgumentError] when the number of teams is not a power of 2

# File lib/tournament_system/algorithm/single_bracket.rb, line 68
def seed(teams)
  raise ArgumentError, 'Need power-of-2 teams' unless (Math.log2(teams.length) % 1).zero?

  teams = teams.map.with_index do |team, index|
    OpenStruct.new(team: team, index: index)
  end
  seed_bracket(teams).map(&:team)
end
total_rounds(teams_count) click to toggle source

Calculates the total number of rounds needed for a single bracket tournament with a certain number of teams.

@param teams_count [Integer] the number of teams @return [Integer] number of rounds needed for round robin

# File lib/tournament_system/algorithm/single_bracket.rb, line 14
def total_rounds(teams_count)
  Math.log2(teams_count).ceil
end

Private Instance Methods

seed_bracket(teams) click to toggle source

Recursively seed the top half of the teams and match teams reversed by index to the bottom half.

# File lib/tournament_system/algorithm/single_bracket.rb, line 81
def seed_bracket(teams)
  return teams if teams.length <= 2

  top_half, bottom_half = teams.each_slice(teams.length / 2).to_a
  top_half = seed_bracket top_half

  top_half.map do |team|
    # match with the team appropriate team in the bottom half
    match = bottom_half[-team.index - 1]

    [team, match]
  end.flatten
end