将赛季时间表分成几周而不重复球队比赛

Breaking Season Schedule into Weeks Without Repeating Teams Playing

我正在努力制定联赛时间表,但我坚持认为在任何给定的一周内,一支球队应该只打一场比赛。

到目前为止,我已经确定了正确的比赛次数,并且每支球队与他们的分区对手比赛 4 次,与他们的跨分区对手比赛 2 次。这是我的代码:

let easternConfTeams = [a, b, c, d, e, f];
let westernConfTeams = [g, h, i, j, k, l];

const teamPool = [...easternConfTeams, ...westernConfTeams];

let schedule = teamPool.reduce((a, v, i) => {
  for (let j = i + 1; j < teamPool.length; j++) {
    if (i < 6) {
      if (j < 6) {
        a.push(`${v} : ${teamPool[j]}`);
        a.push(`${v} : ${teamPool[j]}`);
        a.push(`${v} : ${teamPool[j]}`);
        a.push(`${v} : ${teamPool[j]}`);
      } else {
        a.push(`${v} : ${teamPool[j]}`);
        a.push(`${v} : ${teamPool[j]}`);
      }
    } else {
      if (j < 6) {
        a.push(`${v} : ${teamPool[j]}`);
        a.push(`${v} : ${teamPool[j]}`);
      } else {
        a.push(`${v} : ${teamPool[j]}`);
        a.push(`${v} : ${teamPool[j]}`);
        a.push(`${v} : ${teamPool[j]}`);
        a.push(`${v} : ${teamPool[j]}`);
      }
    }
  }
  return a;
}, []);

然后我 运行 通过洗牌功能:

shuffle = (schedule) => {
  let currentIndex = schedule.length,
    temporaryValue, randomIndex;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = schedule[currentIndex];
    schedule[currentIndex] = schedule[randomIndex];
    schedule[randomIndex] = temporaryValue;
  }

  return schedule;
};

然而,我坚持最后一步,即将这个比赛时间表变成单独的几周。由于有 12 支球队,因此本赛季每周应该有 6 场比赛——在这六场比赛中,任何一支球队都不应出场两次。换句话说,每支球队每周都应该参加比赛,但只能参加一次。

一共192场,需要分成32周,每周6场。

我如何确保这一点?

这是一种基于 round robin tournament scheduling algorithm.

的替代方法

循环赛算法将生成一系列比赛轮次,其中每支球队在每一轮比赛中与其他球队比赛一次,而不会在任何一轮比赛中重复比赛。然后,有一个步骤根据需要重复这些回合并交替比赛(模拟主场/客场交替)。生成所有团队之间的比赛回合并重复两次。然后生成、组合并重复 in-conference 场比赛的回合(因为 in-conference 队在上一步中已经有 2 场比赛)。

结果是 32 轮(周),每轮 6 场比赛,每队在所有回合结束后与 non-conference 对手比赛两次,并与 in-conference 对手比赛 4 次。

const zip = (a, b) => a.map((e, i) => [e, b[i]]);
const combine = (a, b) => zip(a, b).map((e) => [...e[0], ...e[1]]);
const alternate = (rounds, repeats) => {
  const alt = [];
  for (let i = 0; i < repeats; i++) {
    const next = i % 2 ? rounds.map((r) => r.map((m) => [m[1], m[0]])) : rounds;
    alt.push(...next);
  }
  
  return alt;
};

const roundrobin = (teams) => {
  const rounds = [];
  const mid = teams.length / 2;
  for (let i = 0; i < teams.length - 1; i++) {
    const t = i ? [teams[0], ...teams.slice(-i), ...teams.slice(1, -i)] : teams;
    const t1 = t.slice(0, mid);
    const t2 = t.slice(mid).reverse();
    rounds.push(zip(t1, t2));
  }
  
  return rounds;
};

const east = ['a','b','c','d','e','f'];
const west = ['g','h','i','j','k','l'];
const schedule = [
  ...alternate(roundrobin([...east, ...west]), 2),
  ...alternate(combine(roundrobin(east), roundrobin(west)), 2)
];

console.log(schedule);