From 7f87ce92301841d191062b58b021d8eaa1ec948a Mon Sep 17 00:00:00 2001 From: Malaber <32635600+Malaber@users.noreply.github.com> Date: Wed, 3 Apr 2019 15:24:10 +0200 Subject: [PATCH] Add documentation for all services --- app/services/match_service.rb | 11 +++++++++++ app/services/playoff_stage_service.rb | 25 +++++++++++++++++++++++++ app/services/utils.rb | 12 ++++++++++++ 3 files changed, 48 insertions(+) diff --git a/app/services/match_service.rb b/app/services/match_service.rb index 25b590a..0eb9fa8 100644 --- a/app/services/match_service.rb +++ b/app/services/match_service.rb @@ -1,22 +1,32 @@ # frozen_string_literal: true 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) if teams.size < 2 # should be prevented by controller return 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 amount of teams is power of two, all matches are with two teams normal_games = teams.size / 2 needed_games = normal_games 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) needed_games = Utils.previous_power_of_two(teams.size) end matches = [] 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 match = Match.new state: :not_started, position: i, @@ -28,6 +38,7 @@ class MatchService end 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 match = Match.new state: :single_team, position: i, match_scores: [MatchScore.create(team: teams[i])] matches << match diff --git a/app/services/playoff_stage_service.rb b/app/services/playoff_stage_service.rb index 1251472..1168ff4 100644 --- a/app/services/playoff_stage_service.rb +++ b/app/services/playoff_stage_service.rb @@ -1,12 +1,18 @@ # frozen_string_literal: true 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) playoffs = [] 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_stage = Stage.new level: stage_count - 1, matches: initial_matches 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.each do |stage| playoffs << stage @@ -14,19 +20,33 @@ class PlayoffStageService playoffs 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) generate_playoff_stages tournament.teams 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) empty_stages = [] stage_count.times do |i| stage = Stage.new level: i, matches: generate_empty_matches(2**i) empty_stages << stage 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! 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) matches = [] amount.times do |i| @@ -36,10 +56,15 @@ class PlayoffStageService matches 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) if number_of_teams.zero? || number_of_teams == 1 0 else + # black voodoo magic stage_count = Math.log(Utils.previous_power_of_two(number_of_teams)) / Math.log(2) stage_count.to_int end diff --git a/app/services/utils.rb b/app/services/utils.rb index 68ea33c..9a53daf 100644 --- a/app/services/utils.rb +++ b/app/services/utils.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true 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) return 0 if number.zero? @@ -8,12 +12,20 @@ class Utils 2**exponent.floor 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) return 1 if number.zero? 2 * previous_power_of_two(number) 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) number.to_s(2).count('1') == 1 end