module Tournaments class GenerateGroupMatches def self.call(tournament:, start_at: nil) new(tournament, start_at: start_at).call end def initialize(tournament, start_at: nil) @tournament = tournament @start_at = start_at end def call Tournaments::Entitlements.new(@tournament.club).assert_writable! created = [] cursor = @start_at || Time.zone.local(@tournament.starts_on.year, @tournament.starts_on.month, @tournament.starts_on.day, 9, 0, 0) courts = @tournament.court_list court_idx = 0 @tournament.groups.includes(:participants).order(:position).each do |group| pairs = round_robin(group.participants.to_a) pairs.each do |home, away| next if home.blank? || away.blank? match = Tournaments::ScheduleMatch.call( tournament: @tournament, attrs: { home_participant_id: home.id, away_participant_id: away.id, tournament_group_id: group.id, court: courts[court_idx % courts.size], scheduled_at: cursor } ) created << match court_idx += 1 if (court_idx % courts.size).zero? cursor += 1.hour cursor = next_day_morning(cursor) if cursor.to_date > @tournament.ends_on end end end created end private def round_robin(participants) list = participants.dup return [] if list.size < 2 list << nil if list.size.odd? n = list.size rounds = n - 1 pairs = [] rounds.times do (n / 2).times do |i| a = list[i] b = list[n - 1 - i] pairs << [a, b] if a && b end list = [list[0]] + [list[-1]] + list[1..-2] end pairs end def next_day_morning(time) nxt = time.to_date + 1.day nxt = @tournament.starts_on if nxt > @tournament.ends_on Time.zone.local(nxt.year, nxt.month, nxt.day, 9, 0, 0) end end end