[Math] Schedule 8 teams for 6 events. Each team plays each event twice.

combinatorics

I have a seemingly simple question. I'm holding a Beer Olympics at my house tomorrow. There are 8 teams competing in 6 different events (Beer Pong, Beerball, Can Jam, Corn Hole, Beersbee, and Flip Cup). Each event is seeing two teams compete. Is there a way to arrange the schedule so that each team plays each event twice? I understand teams will play some teams twice in different events, but I don't want them to play the same event against the same opponent they played the first time (example – team 1 plays team 2 in cornhole, later on team 1 plays a different team in cornhole).

Round 1: All 6 events can happen at the same time (Obviously only 4 of the games will be played at once due to 8 teams)
Round 2: All 6 events happen at the same time.
And so on until Round 12.

Best Answer

Using the constraint solver MiniZinc, I arrived at the following:

            1   2   3   4   5   6   7   8   9   10  11  12  
Beer Pong                   AC  BF  AE  BD  CG  DH  EG  FH
Beerball                    EG  DH  CG  FH  AE  BF  AC  BD
Can Jam     BG  AH  EF  CH  BD              DF  CE      AG
Corn Hole   CF  DE  AB  DG  FH                  AG  BH  CE
Beersbee    EH  FG  CD  BE      AG  DF  AC  BH          
Flip Cup    AD  BC  GH  AF      CE  BH  EG          DF  

As a bonus beyond what was asked for in the question, I added constraints to prevent that teams have to play the same event in the next round. Also, additional constraints make sure that each team plays against all other teams, but the teams do not play against the same team in the next round.

The solution is found in under two seconds.

The MiniZinc code:

% number of rounds depends on the number of events and the
% number of times events are played by every team
int: NoOfEvents = 6;
int: EventPlaysPerTeam = 4;
int: NoOfRounds = EventPlaysPerTeam * NoOfEvents;
int: NoOfTeams = 8;
set of int: Teams = 1..NoOfTeams;  
set of int: Events = 1..NoOfEvents;
set of int: Rounds = 1..NoOfRounds;

array[Events] of string: 
    eventName = ["Beer Pong", "Beerball", "Can Jam", "Corn Hole", "Beersbee", "Flip Cup"];
array[Teams] of string:
    teamName = ["A", "B", "C", "D", "E", "F", "G", "H"];

%  decision variables
array[Rounds, Events, Teams] of var bool: ret;

%  in every round, every team plays exactly one event
constraint
    forall(r in Rounds, t in Teams)(
      1 == sum([ret[r,e,t] | e in Events])
    );

%  in every round, no or two teams participate in a given event
constraint
    forall(r in Rounds, e in Events)(
       let { var int: occ = sum([ret[r,e,t]| t in Teams]) } in (occ == 0) \/ (occ == 2)
    );

%  every team plays every event a certain number of times
constraint
     forall(e in Events, t in Teams)(
        EventPlaysPerTeam == sum([ret[r,e,t] | r in Rounds])
     );

%  no repetitions
constraint
    forall(e in Events, r1 in Rounds, r2 in Rounds, t1 in Teams, t2 in Teams where (r2 > r1) /\ (t2 > t1))(
      (ret[r1,e,t1] /\ ret[r1,e,t2]) -> (not (ret[r2,e,t1] /\ ret[r2,e,t2]))
    );

%  play every other team at least once
constraint
    forall(t1 in Teams, t2 in Teams where t2 > t1) (
      exists(r in Rounds, e in Events)(ret[r,e,t1] /\ ret[r,e,t2])
    );

%  different event in subsequent round
constraint
    forall(e in Events, t in Teams, r in Rounds where r < NoOfRounds)(
       ret[r,e,t] -> not ret[r+1,e,t]
    );

%  different team pair in subsequent round
constraint
    forall(e1 in Events, e2 in Events, t1 in Teams, t2 in Teams, r in Rounds where (e1 != e2) /\ (t1 < t2) /\ (r < NoOfRounds))(
       (ret[r,e1,t1] /\ ret[r,e1,t2]) -> not (ret[r+1,e2,t1] /\ ret[r+1,e2,t2])
    );

solve satisfy;

output 
[ if r == 1 then "\t\t" else "" endif ++ show(r) ++ "\t" | r in Rounds] ++
[ if r*t==1 then "\n    " ++ eventName[e] ++ "" else "" endif ++ 
  if t == 1 then "\t" else "" endif ++
  if fix(ret[r,e,t]) then teamName[t] else "" endif
  | e in Events, r in Rounds, t in Teams ];