From f36c7817d17d3eb003a15d24a19432904bb3019c Mon Sep 17 00:00:00 2001 From: Malaber Date: Wed, 12 Feb 2025 21:47:51 +0100 Subject: [PATCH 01/23] Add offset for second team to make rematches possible in finale only --- app/services/group_stage_service.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/services/group_stage_service.rb b/app/services/group_stage_service.rb index 7b2ac94..458198a 100644 --- a/app/services/group_stage_service.rb +++ b/app/services/group_stage_service.rb @@ -115,11 +115,13 @@ class GroupStageService tournament_teams_amount = group_stage.tournament.teams.size # special case for po2 teams in tournament and half of them advancing: - # we want to match first of first group with second of second group and so on + # we want to match firsts with seconds, but shift so that we space the teams out correctly so they only + # meet again in finale. This is done by taking the first team of the first group, + # then the second team of (groups_amount / 2) group and so on. if Utils.po2?(tournament_teams_amount) and advancing_teams_amount * 2 == tournament_teams_amount teams_per_group_ranked.each_with_index do |_group_teams, i| first = teams_per_group_ranked[i % teams_per_group_ranked.size][0] - second = teams_per_group_ranked[(i + 1) % teams_per_group_ranked.size][1] + second = teams_per_group_ranked[(i + teams_per_group_ranked.size / 2) % teams_per_group_ranked.size][1] advancing_teams << first advancing_teams << second end From b3c8ad0465fa0d5fee2e2aa2e863eecf590c590f Mon Sep 17 00:00:00 2001 From: Malaber Date: Wed, 12 Feb 2025 21:48:08 +0100 Subject: [PATCH 02/23] Add arm64-darwin-24 as PLATFORM --- Gemfile.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile.lock b/Gemfile.lock index d40cadd..0779004 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -328,6 +328,7 @@ PLATFORMS aarch64-linux-musl arm64-darwin-22 arm64-darwin-23 + arm64-darwin-24 x86_64-linux DEPENDENCIES From 92acbbc221c60cc25faf129b81a9c2221ca5a10c Mon Sep 17 00:00:00 2001 From: Malaber Date: Wed, 12 Feb 2025 21:52:23 +0100 Subject: [PATCH 03/23] Split up get_advancing_teams --- app/services/group_stage_service.rb | 77 +++++++++++++++++++---------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/app/services/group_stage_service.rb b/app/services/group_stage_service.rb index 458198a..db178ae 100644 --- a/app/services/group_stage_service.rb +++ b/app/services/group_stage_service.rb @@ -102,40 +102,65 @@ class GroupStageService # @param group_stage GroupStage the group stage to get all advancing teams from # @return [Array] the teams advancing from that group stage def get_advancing_teams(group_stage) - advancing_teams = [] teams_per_group_ranked = group_stage.groups.map(&method(:teams_sorted_by_group_scores)) - # teams_per_group_ranked is a 2D array - # [ - # [ group_a_first, group_a_second, ... ], - # [ group_b_first, group_b_second, ... ], - # ... - # ] - advancing_teams_amount = group_stage.tournament.instant_finalists_amount + - group_stage.tournament.intermediate_round_participants_amount + advancing_teams_amount = calculate_advancing_teams_amount(group_stage) tournament_teams_amount = group_stage.tournament.teams.size - # special case for po2 teams in tournament and half of them advancing: - # we want to match firsts with seconds, but shift so that we space the teams out correctly so they only - # meet again in finale. This is done by taking the first team of the first group, - # then the second team of (groups_amount / 2) group and so on. - if Utils.po2?(tournament_teams_amount) and advancing_teams_amount * 2 == tournament_teams_amount - teams_per_group_ranked.each_with_index do |_group_teams, i| - first = teams_per_group_ranked[i % teams_per_group_ranked.size][0] - second = teams_per_group_ranked[(i + teams_per_group_ranked.size / 2) % teams_per_group_ranked.size][1] - advancing_teams << first - advancing_teams << second - end - # default case + if special_case_for_po2?(tournament_teams_amount, advancing_teams_amount) + handle_special_case(teams_per_group_ranked) else - advancing_teams_amount.times do |i| - # we want to take the first team of the first group, then the first team of the second group, ... - advancing_teams << teams_per_group_ranked[i % group_stage.groups.size].shift - end + handle_default_case(teams_per_group_ranked, advancing_teams_amount, group_stage.groups.size) + end + end + + private + + # Calculates the total number of teams advancing to the playoff stage + # + # @param group_stage GroupStage the group stage to get the advancing teams amount from + # @return [Integer] the number of teams advancing from that group stage + def calculate_advancing_teams_amount(group_stage) + group_stage.tournament.instant_finalists_amount + + group_stage.tournament.intermediate_round_participants_amount + end + + # Checks if the special case for po2 teams in the tournament applies + # + # @param tournament_teams_amount [Integer] the total number of teams in the tournament + # @param advancing_teams_amount [Integer] the number of teams advancing to the playoff stage + # @return [Boolean] true if the special case applies, false otherwise + def special_case_for_po2?(tournament_teams_amount, advancing_teams_amount) + Utils.po2?(tournament_teams_amount) && advancing_teams_amount * 2 == tournament_teams_amount + end + + # Handles the special case for po2 teams in the tournament + # + # @param teams_per_group_ranked [Array] a 2D array of teams ranked by group scores + # @return [Array] the teams advancing from the group stage + def handle_special_case(teams_per_group_ranked) + advancing_teams = [] + teams_per_group_ranked.each_with_index do |_group_teams, i| + first = teams_per_group_ranked[i % teams_per_group_ranked.size][0] + second = teams_per_group_ranked[(i + teams_per_group_ranked.size / 2) % teams_per_group_ranked.size][1] + advancing_teams << first + advancing_teams << second end advancing_teams end - private + # Handles the default case for advancing teams + # + # @param teams_per_group_ranked [Array] a 2D array of teams ranked by group scores + # @param advancing_teams_amount [Integer] the number of teams advancing to the playoff stage + # @param groups_size [Integer] the number of groups in the group stage + # @return [Array] the teams advancing from the group stage + def handle_default_case(teams_per_group_ranked, advancing_teams_amount, groups_size) + advancing_teams = [] + advancing_teams_amount.times do |i| + advancing_teams << teams_per_group_ranked[i % groups_size].shift + end + advancing_teams + end def recalculate_position_of_group_scores!(group_scores) group_scores = group_scores.sort From 5a7ff67e738c3ee70a20928c91560fce783319d3 Mon Sep 17 00:00:00 2001 From: Tobias Huber Date: Sun, 2 Mar 2025 13:17:38 +0100 Subject: [PATCH 04/23] needs testing --- app/services/group_stage_service.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/services/group_stage_service.rb b/app/services/group_stage_service.rb index db178ae..72e8ece 100644 --- a/app/services/group_stage_service.rb +++ b/app/services/group_stage_service.rb @@ -138,14 +138,14 @@ class GroupStageService # @param teams_per_group_ranked [Array] a 2D array of teams ranked by group scores # @return [Array] the teams advancing from the group stage def handle_special_case(teams_per_group_ranked) - advancing_teams = [] - teams_per_group_ranked.each_with_index do |_group_teams, i| - first = teams_per_group_ranked[i % teams_per_group_ranked.size][0] - second = teams_per_group_ranked[(i + teams_per_group_ranked.size / 2) % teams_per_group_ranked.size][1] - advancing_teams << first - advancing_teams << second - end - advancing_teams + # transpose the array to group first and second places together + teams_per_group_ranked_transposed = teams_per_group_ranked.transpose + first_places = teams_per_group_ranked_transposed[0] + second_places = teams_per_group_ranked_transposed[1] + # split the second places in half and place the second half at the beginning + mid = second_places.length / 2 + second_places_new_order = second_places[mid..-1] + second_places[0..mid] + first_places.zip(second_places_new_order).flatten end # Handles the default case for advancing teams From fdfea9100dfb8d335d3dd74636437804de105dff Mon Sep 17 00:00:00 2001 From: Tobias Huber Date: Sun, 2 Mar 2025 13:22:20 +0100 Subject: [PATCH 05/23] comments --- app/services/group_stage_service.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/services/group_stage_service.rb b/app/services/group_stage_service.rb index 72e8ece..4a70f37 100644 --- a/app/services/group_stage_service.rb +++ b/app/services/group_stage_service.rb @@ -139,12 +139,16 @@ class GroupStageService # @return [Array] the teams advancing from the group stage def handle_special_case(teams_per_group_ranked) # transpose the array to group first and second places together + # e.g. [[1, 2, 3], [4, 5, 6]] to [[1, 4], [2, 5], [3, 6]] teams_per_group_ranked_transposed = teams_per_group_ranked.transpose first_places = teams_per_group_ranked_transposed[0] second_places = teams_per_group_ranked_transposed[1] # split the second places in half and place the second half at the beginning + # e.g. [1, 2, 3, 4, 5, 6] to [4, 5, 6, 1, 2, 3] mid = second_places.length / 2 second_places_new_order = second_places[mid..-1] + second_places[0..mid] + # zip the first and second places together + # e.g. [1, 2, 3], [a, b, c] to [1, a, 2, b, 3, c] first_places.zip(second_places_new_order).flatten end From e07d40e2d4453b333175b51c4873d799826f8bf8 Mon Sep 17 00:00:00 2001 From: Malaber Date: Sun, 2 Mar 2025 16:35:19 +0100 Subject: [PATCH 06/23] Add test for split_and_rotate --- overengineering.rb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 overengineering.rb diff --git a/overengineering.rb b/overengineering.rb new file mode 100644 index 0000000..b97d17a --- /dev/null +++ b/overengineering.rb @@ -0,0 +1,25 @@ +require 'benchmark' + +def split_and_rotate(arr) + midpoint = arr.length / 2 + arr[0...midpoint] + arr[midpoint..-1] +end + +# Create a large array with 1 million entries +large_array = (1..1_000_000).to_a + +# Run the operation 1000 times and calculate the average time +total_time = 0 +iterations = 1000 + +iterations.times do + time_taken = Benchmark.realtime do + split_and_rotate(large_array) + end + total_time += time_taken + # shuffle the array to avoid caching + large_array.shuffle! +end + +average_time = total_time / iterations +puts "Average time taken over #{iterations} iterations: #{average_time} seconds" From 2e56ed8bd0bc86f406150933793b339396d4d74e Mon Sep 17 00:00:00 2001 From: Malaber Date: Tue, 4 Mar 2025 21:33:57 +0100 Subject: [PATCH 07/23] Test each_slice --- overengineering.rb | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/overengineering.rb b/overengineering.rb index b97d17a..0c73620 100644 --- a/overengineering.rb +++ b/overengineering.rb @@ -1,16 +1,24 @@ require 'benchmark' -def split_and_rotate(arr) - midpoint = arr.length / 2 - arr[0...midpoint] + arr[midpoint..-1] +def split_and_rotate_with_each_slice(arr) + # Use each_slice to split the array into two parts + parts = arr.each_slice(arr.length/2).to_a + # Rotate the array by concatenating the back part with the front part + parts[1] + parts[0] end + + # Create a large array with 1 million entries large_array = (1..1_000_000).to_a +# test with small array 1, 2, 3, 4, 5, 6 +# expected output: 3, 4, 5, 6, 1, 2 +puts split_and_rotate_with_each_slice([1, 2, 3, 4, 5, 6]) + # Run the operation 1000 times and calculate the average time total_time = 0 -iterations = 1000 +iterations = 100 iterations.times do time_taken = Benchmark.realtime do @@ -23,3 +31,15 @@ end average_time = total_time / iterations puts "Average time taken over #{iterations} iterations: #{average_time} seconds" + +iterations.times do + time_taken = Benchmark.realtime do + split_and_rotate_with_each_slice(large_array) + end + total_time += time_taken + # shuffle the array to avoid caching + large_array.shuffle! +end + +average_time = total_time / iterations +puts "Average time taken with .each_slice over #{iterations} iterations: #{average_time} seconds" From 6fcdeac054e9c54614d7d5e56960af22d90610c8 Mon Sep 17 00:00:00 2001 From: Malaber Date: Tue, 4 Mar 2025 21:34:16 +0100 Subject: [PATCH 08/23] Move array splitting and rotating to utils --- app/services/group_stage_service.rb | 7 +++---- app/services/utils.rb | 13 +++++++++++++ spec/services/utils_spec.rb | 16 ++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/app/services/group_stage_service.rb b/app/services/group_stage_service.rb index 4a70f37..aa2b37e 100644 --- a/app/services/group_stage_service.rb +++ b/app/services/group_stage_service.rb @@ -143,10 +143,9 @@ class GroupStageService teams_per_group_ranked_transposed = teams_per_group_ranked.transpose first_places = teams_per_group_ranked_transposed[0] second_places = teams_per_group_ranked_transposed[1] - # split the second places in half and place the second half at the beginning - # e.g. [1, 2, 3, 4, 5, 6] to [4, 5, 6, 1, 2, 3] - mid = second_places.length / 2 - second_places_new_order = second_places[mid..-1] + second_places[0..mid] + + second_places_new_order = split_and_rotate(second_places) + # zip the first and second places together # e.g. [1, 2, 3], [a, b, c] to [1, a, 2, b, 3, c] first_places.zip(second_places_new_order).flatten diff --git a/app/services/utils.rb b/app/services/utils.rb index 9a53daf..ed52efe 100644 --- a/app/services/utils.rb +++ b/app/services/utils.rb @@ -29,4 +29,17 @@ class Utils def self.po2?(number) number.to_s(2).count('1') == 1 end + + # split the array in half and place the second half at the beginning + # e.g. [1, 2, 3, 4, 5, 6] to [4, 5, 6, 1, 2, 3] + def split_and_rotate(array) + # handle the case where the array has an odd number of elements + middle_element = [] + if array.length.odd? + # pop the last element and place it in the middle + middle_element = [array.pop] + end + mid = array.length / 2 + array[mid..-1] + middle_element + array[0..mid] + end end diff --git a/spec/services/utils_spec.rb b/spec/services/utils_spec.rb index f1c9ef5..1db709a 100644 --- a/spec/services/utils_spec.rb +++ b/spec/services/utils_spec.rb @@ -47,4 +47,20 @@ RSpec.describe Utils do expect(Utils.po2?(parameters[:test])).to eq(parameters[:result]) end end + + describe '#split_and_rotate' do + [ + { test: [1, 2, 3, 4, 5, 6], result: [4, 5, 6, 1, 2, 3] }, + { test: [1, 2, 3, 4, 5], result: [3, 4, 5, 1, 2] }, + { test: [1, 2, 3, 4], result: [3, 4, 1, 2] }, + { test: [1, 2, 3], result: [2, 3, 1] }, + { test: [1, 2], result: [2, 1] }, + { test: [1], result: [1] }, + { test: [], result: [] } + ].each do |parameters| + it "splits and rotates #{parameters[:test]} to #{parameters[:result]}" do + expect(Utils.new.split_and_rotate(parameters[:test])).to eq(parameters[:result]) + end + end + end end From cc04383191da4526da2f6bffa1b1541aae882513 Mon Sep 17 00:00:00 2001 From: Malaber Date: Tue, 4 Mar 2025 22:45:51 +0100 Subject: [PATCH 09/23] Fix split and rotate in utils --- app/services/utils.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/utils.rb b/app/services/utils.rb index ed52efe..9e06731 100644 --- a/app/services/utils.rb +++ b/app/services/utils.rb @@ -40,6 +40,6 @@ class Utils middle_element = [array.pop] end mid = array.length / 2 - array[mid..-1] + middle_element + array[0..mid] + array[mid..] + middle_element + array[0..(mid - 1)] end end From b0a8cd614eba3bfd81a1765517bdc7a29e024d99 Mon Sep 17 00:00:00 2001 From: Malaber Date: Sat, 8 Mar 2025 16:16:56 +0100 Subject: [PATCH 10/23] Remove coveralls --- Gemfile | 1 - Gemfile.lock | 18 ------------------ 2 files changed, 19 deletions(-) diff --git a/Gemfile b/Gemfile index 3c2f1db..0fc4715 100644 --- a/Gemfile +++ b/Gemfile @@ -43,7 +43,6 @@ gem 'active_model_serializers' gem 'mailgun-ruby' group :test, optional: true do - gem 'coveralls', require: false gem 'factory_bot_rails' gem 'faker' gem 'rspec-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 0779004..13f0829 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -91,12 +91,6 @@ GEM case_transform (0.2) activesupport concurrent-ruby (1.2.3) - coveralls (0.8.23) - json (>= 1.8, < 3) - simplecov (~> 0.16.1) - term-ansicolor (~> 1.3) - thor (>= 0.19.4, < 2.0) - tins (~> 1.6) crass (1.0.6) date (3.3.4) devise (4.9.3) @@ -106,7 +100,6 @@ GEM responders warden (~> 1.2.3) diff-lcs (1.5.1) - docile (1.4.0) domain_name (0.6.20240107) e2mmap (0.1.0) erubi (1.12.0) @@ -277,11 +270,6 @@ GEM ruby-progressbar (1.13.0) shoulda-matchers (6.2.0) activesupport (>= 5.2.0) - simplecov (0.16.1) - docile (~> 1.1) - json (>= 1.8, < 3) - simplecov-html (~> 0.10.0) - simplecov-html (0.10.2) solargraph (0.50.0) backport (~> 1.2) benchmark @@ -305,14 +293,9 @@ GEM sqlite3 (1.7.3-aarch64-linux) sqlite3 (1.7.3-arm64-darwin) sqlite3 (1.7.3-x86_64-linux) - sync (0.5.0) - term-ansicolor (1.7.2) - tins (~> 1.0) thor (1.3.1) tilt (2.3.0) timeout (0.4.1) - tins (1.32.1) - sync tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.5.0) @@ -334,7 +317,6 @@ PLATFORMS DEPENDENCIES active_model_serializers bootsnap - coveralls devise devise_token_auth! factory_bot_rails From dc1cf5aec5cd126f8c38764ad3bd904d69c94801 Mon Sep 17 00:00:00 2001 From: Malaber Date: Sat, 8 Mar 2025 16:20:37 +0100 Subject: [PATCH 11/23] Delete unused file --- overengineering.rb | 45 --------------------------------------------- 1 file changed, 45 deletions(-) delete mode 100644 overengineering.rb diff --git a/overengineering.rb b/overengineering.rb deleted file mode 100644 index 0c73620..0000000 --- a/overengineering.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'benchmark' - -def split_and_rotate_with_each_slice(arr) - # Use each_slice to split the array into two parts - parts = arr.each_slice(arr.length/2).to_a - # Rotate the array by concatenating the back part with the front part - parts[1] + parts[0] -end - - - -# Create a large array with 1 million entries -large_array = (1..1_000_000).to_a - -# test with small array 1, 2, 3, 4, 5, 6 -# expected output: 3, 4, 5, 6, 1, 2 -puts split_and_rotate_with_each_slice([1, 2, 3, 4, 5, 6]) - -# Run the operation 1000 times and calculate the average time -total_time = 0 -iterations = 100 - -iterations.times do - time_taken = Benchmark.realtime do - split_and_rotate(large_array) - end - total_time += time_taken - # shuffle the array to avoid caching - large_array.shuffle! -end - -average_time = total_time / iterations -puts "Average time taken over #{iterations} iterations: #{average_time} seconds" - -iterations.times do - time_taken = Benchmark.realtime do - split_and_rotate_with_each_slice(large_array) - end - total_time += time_taken - # shuffle the array to avoid caching - large_array.shuffle! -end - -average_time = total_time / iterations -puts "Average time taken with .each_slice over #{iterations} iterations: #{average_time} seconds" From 2ff726a0fe49b1ae909e178745321e3bb19e734c Mon Sep 17 00:00:00 2001 From: Malaber Date: Sat, 8 Mar 2025 16:22:00 +0100 Subject: [PATCH 12/23] Add todo --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9015644..5284cb3 100644 --- a/README.md +++ b/README.md @@ -72,3 +72,4 @@ $ rails diagram:all_with_engines - WICHTIG UND EZ: gruppenphase in der gleichen gruppe sollten erst finale gegeneinander spielen (dazu nicht aus der nächsten gruppe sondern einmal advancing teams von vorne und einmal von hinten, oder offset von hälfte der weiterkommenden teams) - beim eintragen einer runde direkt den nächsten tisch anzeigen - spiel um platz 3 +- edgecase wenn mehr als die hälfte der teams weiterkommen bedenken bzw zumindest abfangen From 7edf1c5140d3a76424d50b1975c98fe5d425507d Mon Sep 17 00:00:00 2001 From: Malaber Date: Tue, 4 Mar 2025 21:59:57 +0100 Subject: [PATCH 13/23] REVERT ME run only focused tests --- spec/rails_helper.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 3d93ab1..15c095d 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -38,6 +38,10 @@ RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" + # Run only focused tests + # TODO REVERT ME + config.filter_run_when_matching :focus + # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. From 322d1e2ed416e3647545c0afd74a35ff5c4f7e4d Mon Sep 17 00:00:00 2001 From: Malaber Date: Sat, 8 Mar 2025 17:39:27 +0100 Subject: [PATCH 14/23] Add dummy tournament with group stage --- spec/factories/tournaments.rb | 9 +++++++ spec/services/group_stage_service_spec.rb | 33 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/spec/factories/tournaments.rb b/spec/factories/tournaments.rb index 9a8a7d3..7b61999 100644 --- a/spec/factories/tournaments.rb +++ b/spec/factories/tournaments.rb @@ -52,6 +52,15 @@ FactoryBot.define do end end + factory :prepared_group_stage_tournament do + transient do + group_stage { create(:group_stage) } + end + after(:create) do |tournament, evaluator| + tournament.stages << evaluator.group_stage + end + end + factory :dummy_stage_tournament do transient do stage_count { 3 } diff --git a/spec/services/group_stage_service_spec.rb b/spec/services/group_stage_service_spec.rb index 8073ef2..61768c9 100644 --- a/spec/services/group_stage_service_spec.rb +++ b/spec/services/group_stage_service_spec.rb @@ -190,4 +190,37 @@ RSpec.describe GroupStageService do end end end + + describe '#get_advancing_teams', focus: true do + context 'when special case for po2 applies' do + before do + # Create some example teams + teams1 = [Team.new(name: 'Team 1'), Team.new(name: 'Team 2'), Team.new(name: 'Team 3'), Team.new(name: 'Team 4')] + teams2 = [Team.new(name: 'Team 5'), Team.new(name: 'Team 6'), Team.new(name: 'Team 7'), Team.new(name: 'Team 8')] + + # Group the teams + groups = [teams1, teams2] + + # Generate the group stage + @group_stage = GroupStageService.generate_group_stage(groups) + + @tournament = create(:prepared_group_stage_tournament, group_stage: @group_stage) + + # iterate over all groups and update the matches within to all be decided + @group_stage.groups.each do |group| + group.matches.each do |match| + match.match_scores.each do |ms| + # give the team the amount of points as in their name + ms.points = ms.team.name.split(' ').last.to_i + ms.save! + end + match.state = 'finished' + match.save! + end + gs = GroupStageService.update_group_scores(group) + gs.each(&:save!) + end + end + end + end end From 119cffc4ff5d1dc342047440da419d5fcc838562 Mon Sep 17 00:00:00 2001 From: Malaber Date: Sat, 8 Mar 2025 17:39:46 +0100 Subject: [PATCH 15/23] Remove coveralls for good --- spec/spec_helper.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 74a58f0..f38f403 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -require 'coveralls' -Coveralls.wear!('rails') - # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause From c5dd8beb001f233d28c26fb620e84b1bd450b355 Mon Sep 17 00:00:00 2001 From: Malaber Date: Sat, 8 Mar 2025 17:39:51 +0100 Subject: [PATCH 16/23] Typo --- app/controllers/tournaments_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/tournaments_controller.rb b/app/controllers/tournaments_controller.rb index a40c6bf..d2a2198 100644 --- a/app/controllers/tournaments_controller.rb +++ b/app/controllers/tournaments_controller.rb @@ -96,7 +96,7 @@ class TournamentsController < ApplicationController private def organize_teams_in_groups(teams) - # each team gets put into a array of teams depending on the group specified in team[:group] + # each team gets put into an array of teams depending on the group specified in team[:group] teams.group_by { |team| team['group'] }.values.map do |group| group.map do |team| find_or_create_team(team) From 5c6b683526d9f5cc3f3caa639c91d5aa265fe514 Mon Sep 17 00:00:00 2001 From: Malaber Date: Sat, 8 Mar 2025 17:46:55 +0100 Subject: [PATCH 17/23] All groups are created equal --- spec/services/group_stage_service_spec.rb | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/spec/services/group_stage_service_spec.rb b/spec/services/group_stage_service_spec.rb index 61768c9..55f8576 100644 --- a/spec/services/group_stage_service_spec.rb +++ b/spec/services/group_stage_service_spec.rb @@ -194,12 +194,18 @@ RSpec.describe GroupStageService do describe '#get_advancing_teams', focus: true do context 'when special case for po2 applies' do before do - # Create some example teams - teams1 = [Team.new(name: 'Team 1'), Team.new(name: 'Team 2'), Team.new(name: 'Team 3'), Team.new(name: 'Team 4')] - teams2 = [Team.new(name: 'Team 5'), Team.new(name: 'Team 6'), Team.new(name: 'Team 7'), Team.new(name: 'Team 8')] + teams = create_list(:team, 32) - # Group the teams - groups = [teams1, teams2] + # put the teams in groups of four + groups = teams.each_slice(4).to_a + + # iterate over all groups and number the teams in their name + groups.each do |group| + group.each_with_index do |team, i| + team.name = "#{team.name} #{i}" + team.save! + end + end # Generate the group stage @group_stage = GroupStageService.generate_group_stage(groups) @@ -210,8 +216,9 @@ RSpec.describe GroupStageService do @group_stage.groups.each do |group| group.matches.each do |match| match.match_scores.each do |ms| - # give the team the amount of points as in their name - ms.points = ms.team.name.split(' ').last.to_i + # give the team 10 points minus the number in their name + # this results in the team 1 always winning and getting to place 1 in the group etc. + ms.points = 10 - ms.team.name.split(' ').last.to_i ms.save! end match.state = 'finished' From bb61d514a0b69388ec2fccc36a4362fe2a80e841 Mon Sep 17 00:00:00 2001 From: Malaber Date: Sun, 9 Mar 2025 19:34:15 +0100 Subject: [PATCH 18/23] Fix Utils call --- app/services/group_stage_service.rb | 2 +- app/services/utils.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/group_stage_service.rb b/app/services/group_stage_service.rb index aa2b37e..c4b545c 100644 --- a/app/services/group_stage_service.rb +++ b/app/services/group_stage_service.rb @@ -144,7 +144,7 @@ class GroupStageService first_places = teams_per_group_ranked_transposed[0] second_places = teams_per_group_ranked_transposed[1] - second_places_new_order = split_and_rotate(second_places) + second_places_new_order = Utils.split_and_rotate(second_places) # zip the first and second places together # e.g. [1, 2, 3], [a, b, c] to [1, a, 2, b, 3, c] diff --git a/app/services/utils.rb b/app/services/utils.rb index 9e06731..e2e9a3f 100644 --- a/app/services/utils.rb +++ b/app/services/utils.rb @@ -32,7 +32,7 @@ class Utils # split the array in half and place the second half at the beginning # e.g. [1, 2, 3, 4, 5, 6] to [4, 5, 6, 1, 2, 3] - def split_and_rotate(array) + def self.split_and_rotate(array) # handle the case where the array has an odd number of elements middle_element = [] if array.length.odd? From a74236982d235b3b44829b1eee36df9edeb773aa Mon Sep 17 00:00:00 2001 From: Malaber Date: Sun, 9 Mar 2025 19:34:49 +0100 Subject: [PATCH 19/23] Generate a valid tournament like at bpwstr --- spec/factories/tournaments.rb | 20 +++++++++++++++----- spec/services/group_stage_service_spec.rb | 14 +++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/spec/factories/tournaments.rb b/spec/factories/tournaments.rb index 7b61999..4e2ab21 100644 --- a/spec/factories/tournaments.rb +++ b/spec/factories/tournaments.rb @@ -7,13 +7,23 @@ FactoryBot.define do user transient do teams_count { 8 } + teams { nil } + playoff_teams_amount { 4 } + instant_finalists_amount { 4 } + intermediate_round_participants_amount { 0 } end after(:create) do |tournament, evaluator| - tournament.teams = create_list(:team, evaluator.teams_count, tournament: tournament) - tournament.playoff_teams_amount = (tournament.teams.size / 2) - tournament.instant_finalists_amount = (tournament.playoff_teams_amount / 2) - tournament.intermediate_round_participants_amount = ((tournament.playoff_teams_amount - - tournament.instant_finalists_amount) * 2) + if evaluator.teams.present? + tournament.teams = evaluator.teams + else + tournament.teams = create_list(:team, evaluator.teams_count, tournament: tournament) + end + tournament.playoff_teams_amount = evaluator.playoff_teams_amount + tournament.instant_finalists_amount = evaluator.instant_finalists_amount + tournament.intermediate_round_participants_amount = evaluator.intermediate_round_participants_amount + if tournament.playoff_teams_amount != tournament.instant_finalists_amount + tournament.intermediate_round_participants_amount / 2 + raise 'playoff_teams_amount must be equal to instant_finalists_amount + intermediate_round_participants_amount / 2' + end tournament.save! end diff --git a/spec/services/group_stage_service_spec.rb b/spec/services/group_stage_service_spec.rb index 55f8576..fb093d8 100644 --- a/spec/services/group_stage_service_spec.rb +++ b/spec/services/group_stage_service_spec.rb @@ -200,9 +200,9 @@ RSpec.describe GroupStageService do groups = teams.each_slice(4).to_a # iterate over all groups and number the teams in their name - groups.each do |group| - group.each_with_index do |team, i| - team.name = "#{team.name} #{i}" + groups.each_with_index do |group, group_index| + group.each_with_index do |team, team_index| + team.name = "#{team.name} #{group_index} #{team_index}" team.save! end end @@ -210,8 +210,12 @@ RSpec.describe GroupStageService do # Generate the group stage @group_stage = GroupStageService.generate_group_stage(groups) - @tournament = create(:prepared_group_stage_tournament, group_stage: @group_stage) - + @tournament = create(:prepared_group_stage_tournament, + group_stage: @group_stage, + teams: teams, + playoff_teams_amount: 16, + instant_finalists_amount: 16, + intermediate_round_participants_amount: 0) # iterate over all groups and update the matches within to all be decided @group_stage.groups.each do |group| group.matches.each do |match| From 5786607b7fefc7a0e069d0990edd97390a141ec1 Mon Sep 17 00:00:00 2001 From: Malaber Date: Sun, 9 Mar 2025 20:04:00 +0100 Subject: [PATCH 20/23] Reload group_scores from database after update --- spec/services/group_stage_service_spec.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/services/group_stage_service_spec.rb b/spec/services/group_stage_service_spec.rb index fb093d8..37d3e67 100644 --- a/spec/services/group_stage_service_spec.rb +++ b/spec/services/group_stage_service_spec.rb @@ -221,15 +221,17 @@ RSpec.describe GroupStageService do group.matches.each do |match| match.match_scores.each do |ms| # give the team 10 points minus the number in their name - # this results in the team 1 always winning and getting to place 1 in the group etc. + # this results in the team 0 always winning and getting to place 1 in the group etc. ms.points = 10 - ms.team.name.split(' ').last.to_i ms.save! end match.state = 'finished' match.save! end - gs = GroupStageService.update_group_scores(group) - gs.each(&:save!) + group_scores = GroupStageService.update_group_scores(group) + group_scores.each(&:save!) + + group.group_scores.each(&:reload) end end end From cf3cc9cebb784826082925c6b0204a6c6b30087b Mon Sep 17 00:00:00 2001 From: Malaber Date: Sun, 9 Mar 2025 20:14:34 +0100 Subject: [PATCH 21/23] Add tests about the advancing teams --- spec/services/group_stage_service_spec.rb | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/spec/services/group_stage_service_spec.rb b/spec/services/group_stage_service_spec.rb index 37d3e67..f8eaf1d 100644 --- a/spec/services/group_stage_service_spec.rb +++ b/spec/services/group_stage_service_spec.rb @@ -234,6 +234,42 @@ RSpec.describe GroupStageService do group.group_scores.each(&:reload) end end + + it 'returns the correct amount of teams' do + advancing_teams = GroupStageService.get_advancing_teams(@group_stage) + expect(advancing_teams.size).to be(16) + end + + it 'returns the correct teams in the correct order' do + advancing_teams = GroupStageService.get_advancing_teams(@group_stage) + advancing_teams.each_with_index do |team, i| + # if index is even, the team should be of a first place; end in a 0 + # if index is odd, the team should be of a second place; end in a 1 + team_quality = team.name.split(' ').last.to_i + expect(team_quality % 2).to be(i % 2) + end + end + + it 'spaces groups apart, so you meet your group only in finale' do + group_first_matchups_expected = { + 0 => 4, + 1 => 5, + 2 => 6, + 3 => 7, + 4 => 0, + 5 => 1, + 6 => 2, + 7 => 3 + } + advancing_teams = GroupStageService.get_advancing_teams(@group_stage) + advancing_teams.each_slice(2).to_a.each do |matchup| + # this is the team that landed a first place in the group + first_place_team = matchup[0].name.split(' ')[1].to_i + # this is the team that landed a second place in the group + second_place_team = matchup[1].name.split(' ')[1].to_i + expect(group_first_matchups_expected[first_place_team]).to eq(second_place_team) + end + end end end end From 3bb8afeae69d9c27f7e744c3b01ab61bbfedc1d4 Mon Sep 17 00:00:00 2001 From: Malaber Date: Sun, 9 Mar 2025 21:14:18 +0100 Subject: [PATCH 22/23] Remove focused tests --- spec/services/group_stage_service_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/group_stage_service_spec.rb b/spec/services/group_stage_service_spec.rb index f8eaf1d..3b48d3f 100644 --- a/spec/services/group_stage_service_spec.rb +++ b/spec/services/group_stage_service_spec.rb @@ -191,7 +191,7 @@ RSpec.describe GroupStageService do end end - describe '#get_advancing_teams', focus: true do + describe '#get_advancing_teams' do context 'when special case for po2 applies' do before do teams = create_list(:team, 32) From d1c6ee2da51acd6f1413303710f8e0f360511ca6 Mon Sep 17 00:00:00 2001 From: Malaber Date: Thu, 13 Mar 2025 12:56:20 +0100 Subject: [PATCH 23/23] Fix split and rotate call --- spec/services/utils_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/utils_spec.rb b/spec/services/utils_spec.rb index 1db709a..ee84fd8 100644 --- a/spec/services/utils_spec.rb +++ b/spec/services/utils_spec.rb @@ -59,7 +59,7 @@ RSpec.describe Utils do { test: [], result: [] } ].each do |parameters| it "splits and rotates #{parameters[:test]} to #{parameters[:result]}" do - expect(Utils.new.split_and_rotate(parameters[:test])).to eq(parameters[:result]) + expect(Utils.split_and_rotate(parameters[:test])).to eq(parameters[:result]) end end end