Add documentation for all services
This commit is contained in:
parent
9ac71e4c71
commit
7f87ce9230
|
|
@ -1,22 +1,32 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class MatchService
|
class MatchService
|
||||||
|
# Generates all necessary matches from a list of teams
|
||||||
|
#
|
||||||
|
# @param teams [Array] the teams to generate matches with
|
||||||
|
# @return [Array] the generated matches
|
||||||
def self.generate_matches(teams)
|
def self.generate_matches(teams)
|
||||||
if teams.size < 2
|
if teams.size < 2
|
||||||
# should be prevented by controller
|
# should be prevented by controller
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# normal_games = number of matches with two teams attending
|
||||||
|
# needed_games = number of matches to generate in total for the given number of teams
|
||||||
if Utils.po2?(teams.size)
|
if Utils.po2?(teams.size)
|
||||||
|
# if amount of teams is power of two, all matches are with two teams
|
||||||
normal_games = teams.size / 2
|
normal_games = teams.size / 2
|
||||||
needed_games = normal_games
|
needed_games = normal_games
|
||||||
else
|
else
|
||||||
|
# if amount of teams isn't a power of two we need to "kick out"
|
||||||
|
# enough teams to get to a power of two in the next round
|
||||||
normal_games = teams.size - Utils.previous_power_of_two(teams.size)
|
normal_games = teams.size - Utils.previous_power_of_two(teams.size)
|
||||||
needed_games = Utils.previous_power_of_two(teams.size)
|
needed_games = Utils.previous_power_of_two(teams.size)
|
||||||
end
|
end
|
||||||
|
|
||||||
matches = []
|
matches = []
|
||||||
while matches.size < normal_games
|
while matches.size < normal_games
|
||||||
|
# while we do not have as many matches with two teams as we need, create another one
|
||||||
i = matches.size
|
i = matches.size
|
||||||
match = Match.new state: :not_started,
|
match = Match.new state: :not_started,
|
||||||
position: i,
|
position: i,
|
||||||
|
|
@ -28,6 +38,7 @@ class MatchService
|
||||||
end
|
end
|
||||||
|
|
||||||
until matches.size >= needed_games
|
until matches.size >= needed_games
|
||||||
|
# while we do not have enough matches in general we need to fill the array with "single team" matches
|
||||||
i = matches.size
|
i = matches.size
|
||||||
match = Match.new state: :single_team, position: i, match_scores: [MatchScore.create(team: teams[i])]
|
match = Match.new state: :single_team, position: i, match_scores: [MatchScore.create(team: teams[i])]
|
||||||
matches << match
|
matches << match
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,18 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class PlayoffStageService
|
class PlayoffStageService
|
||||||
|
# Generates the playoff stage given the tournament
|
||||||
|
#
|
||||||
|
# @param teams [Array] The teams to generate the playoff stages with
|
||||||
|
# @return [Array] the generated playoff stages
|
||||||
def self.generate_playoff_stages(teams)
|
def self.generate_playoff_stages(teams)
|
||||||
playoffs = []
|
playoffs = []
|
||||||
stage_count = calculate_required_stage_count(teams.size)
|
stage_count = calculate_required_stage_count(teams.size)
|
||||||
|
# initial_matches are the matches in the first stage; this is the only stage filled with teams from the start on
|
||||||
initial_matches = MatchService.generate_matches(teams)
|
initial_matches = MatchService.generate_matches(teams)
|
||||||
initial_stage = Stage.new level: stage_count - 1, matches: initial_matches
|
initial_stage = Stage.new level: stage_count - 1, matches: initial_matches
|
||||||
playoffs << initial_stage
|
playoffs << initial_stage
|
||||||
|
# empty stages are the stages, the tournament is filled with to have the matches ready for later
|
||||||
empty_stages = generate_stages_with_empty_matches(stage_count - 1)
|
empty_stages = generate_stages_with_empty_matches(stage_count - 1)
|
||||||
empty_stages.each do |stage|
|
empty_stages.each do |stage|
|
||||||
playoffs << stage
|
playoffs << stage
|
||||||
|
|
@ -14,19 +20,33 @@ class PlayoffStageService
|
||||||
playoffs
|
playoffs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generates the playoff stage given the tournament
|
||||||
|
#
|
||||||
|
# @param tournament [Tournament] The tournament to generate the playoff stages from
|
||||||
|
# @return [Array] the generated playoff stages
|
||||||
def self.generate_playoff_stages_from_tournament(tournament)
|
def self.generate_playoff_stages_from_tournament(tournament)
|
||||||
generate_playoff_stages tournament.teams
|
generate_playoff_stages tournament.teams
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generates given number of empty stages
|
||||||
|
#
|
||||||
|
# @param stage_count [Integer] number of stages to generate
|
||||||
|
# @return [Array] the generated stages
|
||||||
def self.generate_stages_with_empty_matches(stage_count)
|
def self.generate_stages_with_empty_matches(stage_count)
|
||||||
empty_stages = []
|
empty_stages = []
|
||||||
stage_count.times do |i|
|
stage_count.times do |i|
|
||||||
stage = Stage.new level: i, matches: generate_empty_matches(2**i)
|
stage = Stage.new level: i, matches: generate_empty_matches(2**i)
|
||||||
empty_stages << stage
|
empty_stages << stage
|
||||||
end
|
end
|
||||||
|
# as we are generating the stages in the wrong order (starting with the lowest number of matches (which is
|
||||||
|
# the final stage)) they need to be reversed
|
||||||
empty_stages.reverse!
|
empty_stages.reverse!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Generates a number of empty matches to fill later stages
|
||||||
|
#
|
||||||
|
# @param amount [Integer] the amount of matches to generate
|
||||||
|
# @return [Array] the generated matches
|
||||||
def self.generate_empty_matches(amount)
|
def self.generate_empty_matches(amount)
|
||||||
matches = []
|
matches = []
|
||||||
amount.times do |i|
|
amount.times do |i|
|
||||||
|
|
@ -36,10 +56,15 @@ class PlayoffStageService
|
||||||
matches
|
matches
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Calculates how many stages are required for given number of teams
|
||||||
|
#
|
||||||
|
# @param number_of_teams [Integer] the teams number of teams to calculate amount of stages
|
||||||
|
# @return [Integer] amount of required stages
|
||||||
def self.calculate_required_stage_count(number_of_teams)
|
def self.calculate_required_stage_count(number_of_teams)
|
||||||
if number_of_teams.zero? || number_of_teams == 1
|
if number_of_teams.zero? || number_of_teams == 1
|
||||||
0
|
0
|
||||||
else
|
else
|
||||||
|
# black voodoo magic
|
||||||
stage_count = Math.log(Utils.previous_power_of_two(number_of_teams)) / Math.log(2)
|
stage_count = Math.log(Utils.previous_power_of_two(number_of_teams)) / Math.log(2)
|
||||||
stage_count.to_int
|
stage_count.to_int
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Utils
|
class Utils
|
||||||
|
# Calculates the previous power of 2 given a number
|
||||||
|
#
|
||||||
|
# @param number [Integer] the number to generate the previous power of 2 from
|
||||||
|
# @return [Array] previous power of two
|
||||||
def self.previous_power_of_two(number)
|
def self.previous_power_of_two(number)
|
||||||
return 0 if number.zero?
|
return 0 if number.zero?
|
||||||
|
|
||||||
|
|
@ -8,12 +12,20 @@ class Utils
|
||||||
2**exponent.floor
|
2**exponent.floor
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Calculates the next power of 2 given a number
|
||||||
|
#
|
||||||
|
# @param number [Integer] the number to generate the next power of 2 from
|
||||||
|
# @return [Array] next power of two
|
||||||
def self.next_power_of_two(number)
|
def self.next_power_of_two(number)
|
||||||
return 1 if number.zero?
|
return 1 if number.zero?
|
||||||
|
|
||||||
2 * previous_power_of_two(number)
|
2 * previous_power_of_two(number)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Calculates if a number is a power of 2
|
||||||
|
#
|
||||||
|
# @param number [Integer] the number to check
|
||||||
|
# @return [Boolean] is the number a power of 2?
|
||||||
def self.po2?(number)
|
def self.po2?(number)
|
||||||
number.to_s(2).count('1') == 1
|
number.to_s(2).count('1') == 1
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue