Рубиновая комбинаторика - PullRequest
3 голосов
/ 20 ноября 2011

Я хочу создать футбольный матч со списком клубов. Каждая игра проводится по воскресеньям в случайное время, включенное в массив match_starts_at. Каждый клуб играет только одну игру каждое воскресенье.

Пример:

Наличие этих клубов:

Club Atlético All Boys
Asociación Atlética Argentinos Juniors
Arsenal Fútbol Club
Club Atlético Banfield
Club Atlético Belgrano
Club Atlético Boca Juniors
Club Atlético Colón
Club Estudiantes de La Plata
Club Deportivo Godoy Cruz Antonio Tomba
Asociación Mutual Social y Deportiva Atlético de Rafaela
Club Atlético Independiente
Club Atlético Lanús
Club Atlético Newell's Old Boys
Club Olimpo
Racing Club
Club Atlético San Martín
Club Atlético San Lorenzo de Almagro
Club Atlético Tigre
Club Atlético Unión
Club Atlético Vélez Sarsfield

Результат должен быть похож на то, что видно здесь: http://www.afa.org.ar/index.php?option=com_content&view=article&id=16780%3Afixture-del-torneo-de-primera-division&Itemid=100

Пример структуры клуба:

=> # Club @id=1 @name="Example Name"

=> # Club @id=2 @name="Example2 Name"

Пример конструкции приспособления:

=> # Fixture @id=1 @datetime='2011-11-19 19:12:49' @home_id=1 @away_id=2

Объекту Fixture требуется следующее для сохранения в базе данных:

a home club (:home)    
an away club (:away)    
and the time of the match (:datetime)

Каждый клуб должен играть только один раз с другими клубами, и все клубы должны играть один матч дома, другой в гостях, другой дома и т. Д. В дате должно быть 10 матчей. Как я могу создать список матчей?

Это то, что я сделал до сих пор.

  competition = Competition.get(1)
  clubs = Club.all #20 clubs
  @time = Time.now
  @count = 0
  until @time.sunday? do
     @time += (24*60*60) # add 1 day until it's sunday
  end
  @first_time = @time
  @fixture = {1 => []}
  clubs.combination(2).each_with_index do |(club1, club2), idx|
    Fixture.create(
      :home => idx.even? ? club1 : club2,
      :away => idx.even? ? club2 : club1,
      :datetime =>  available_fixture_date(club1,club2)
    ).save
  end

  def getFecha(club1, club2)
    @fixture.keys.each do |fecha|
      if (!@fixture[fecha].include?(club1.name) && !@fixture[fecha].include?(club2.name))
        @fixture[fecha] << club1.name
        @fixture[fecha] << club2.name
        @fixture[@fixture.keys.last + 1] = []
        return fecha
      end
    end
  end

  def available_fixture_date(club1, club2)
    fecha = getFecha(club1, club2)
    match_starts_at = ['16:00', '17:30', '18:10', '22:00']
    match_time = match_starts_at.shuffle.first
    @time  = @first_time + (24*60*60) * fecha * 7
    Time.new(@time.year, @time.month, @time.day, match_time[0,2], match_time[3,2])
 end

С моим кодом я получаю более 19 дат, и я должен получить 19 дат с 10 совпадениями в день.

Ответы [ 3 ]

2 голосов
/ 20 ноября 2011

Вы не получите хорошую однострочку для этого, как вы это делали для объединения команд, так как для этого нужно обратиться к существующим данным, чтобы выяснить, какие даты уже приняты.Но это должно работать нормально.Обратите внимание, что я использовал временные помощники ActiveSupport, но вы можете использовать что-то вроде Chronic, если у вас нет ActiveSupport и вы не хотите его включать.

def available_fixture_date(club1, club2)
  last_played = (club1.fixtures | club2.fixtures).max(:datetime)
  last_played.nil? ? DateTime.now.sunday : last_played + 1.week
end

def create_fixtures(clubs)
  clubs.combination(2).each_with_index do |(club1, club2), idx|
    Fixture.create(
      :home     => idx.even? ? club1 : club2,
      :away     => idx.even? ? club2 : club1,
      :datetime =>  available_fixture_date(club1, club2)
    )
  end
end
1 голос
/ 26 февраля 2013

Я полагаю, что общий алгоритм, который вы ищете здесь, это Round-Robin . Следующее дает мне правильные даты, в итоге получается всего 19 дат, 10 матчей за дату:

DAY = 24 * 60 * 60
MATCH_START_TIMES = ['16:00', '17:30', '18:10', '22:00']

def fixture_date(fecha)
  # select a random start time
  match_time = MATCH_START_TIMES.sample

  @time = @first_time + DAY * fecha * 7
  Time.new(@time.year, @time.month, @time.day, match_time[0,2].to_i, match_time[3,2].to_i)
end

# uses round-robin indexing algorithm described on
# http://en.wikipedia.org/wiki/Round-robin%5Ftournament#Scheduling_algorithm
def round_robin(n, round)
  arr = [*0...n]
  arr.insert 1, *arr.pop(round)
  [arr.slice(0, n/2), arr.slice(n/2, n).reverse]
end

def find_club_combination(clubs, round, pair)
  indexes = round_robin(clubs.size, round)
  index_a, index_b = indexes.first[pair], indexes.last[pair]
  [clubs[index_a], clubs[index_b]]
end

competition = Competition.get(1)
clubs = Club.all #20 clubs
@time = Time.now
@count = 0
@time += DAY until @time.sunday?

@first_time = @time

num_rounds = clubs.size - 1
matches_per_day = clubs.size / 2
(0...num_rounds).collect do |round|
  matches_per_day.times do |pair|
    club1, club2 = find_club_combination(clubs, round, pair)
    Fixture.create(
      :home => round.even? ? club1 : club2,
      :away => round.even? ? club2 : club1,
      :datetime => fixture_date(round)
    ).save
  end
end
1 голос
/ 20 ноября 2011

Не уверен, что именно вы здесь, но вы, кажется, хотите более простой способ для расчета времени?

Если это так, Хронический довольно круто.

Chronic.parse('next sunday at 4pm')
#=> Sun Nov 20 16:00:00 -0800 2011

match_starts_at = ['16:00', '17:30', '18:10', '22:00']
Chronic.parse("sunday at #{match_starts_at[0]}")
#=> Sun Nov 20 16:00:00 -0800 2011
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...