Why the spreadsheet breaks
It always starts the same way: one tab for the teams, another for the rounds and a cross-table where you tick off the fixtures already assigned. With six teams it works. With fourteen, split into two groups and half the league asking to play on Sunday, it stops working: two teams that meet twice in the first half and two others that never meet at all, a team with four home rounds in a row, matches dropped on a bank holiday weekend, and a round moved in the calendar but not in the standings.
The good news: sorting out a league is a problem that was solved more than a century ago. It is called the Berger system.
The Berger system: everyone against everyone, exactly once
Berger tables are the classic method for building a round-robin tournament. They have been used in chess since the 19th century and work just the same for a pelota, rowing or futsal league. With N teams you need N−1 rounds.
How the rotation works
The team in the last position of the list is the pivot: it does not rotate with the rest, it faces a different opponent every round and strictly alternates home and away. The other teams are paired by turning two indexes in opposite directions, one going up and one going down. That turn guarantees what is so hard to secure by hand: that no pairing is repeated and that nobody is left without a match.
With six teams (A, B, C, D, E and F, in that order) you get five rounds, home team on the left:
- Round 1: A–F · B–E · C–D
- Round 2: F–D · E–C · A–B
- Round 3: B–F · C–A · D–E
- Round 4: F–E · A–D · B–C
- Round 5: C–F · D–B · E–A
Fifteen matches: exactly the fifteen possible pairs of six teams, without a single repeat. And look at F, the pivot: away, home, away, home, away.
Odd number of teams, and return legs
Berger needs an even number of participants. If your league has seven teams, the generator adds a dummy bye team before calculating anything and treats it as one more: whoever is paired with that phantom participant rests that round. The bye rotates on its own, every team gets one and it is flagged as a bye, not as a match missing an opponent.
The return half is not calculated separately either: the first half is duplicated with home and away swapped. One checkbox turns five rounds into ten. And because the return is an exact mirror of the first leg, the system knows the reciprocal of every match; that matters when you swap venues.
Group stages and knockout brackets
If the competition has a group stage, you say how many groups you want and the system distributes the teams among them. Inside each group the same round-robin applies, with an automatic bye if the group is odd and with the option of a single or a double round.
Knockouts are built with standard seeding: 1 against 4 and 2 against 3, a pattern extended recursively to brackets of eight, sixteen or thirty-two, so the top two seeds can only meet in the final. And if you do not have exactly 8, 16 or 32 teams? That is the normal case, and it is solved: the system works out how many are left over against the lower power of two and creates a preliminary round just for them; the best placed teams go straight into the next round with a bye. Each round can also be configured separately as a single match or two legs.
Real-world constraints
A calendar that is flawless on paper is useless if it schedules round 12 at Christmas. This is what is applied when placing the dates:
- Holiday weeks: you mark the season's public holidays and the generator skips any week containing one in full; it does not move the match by two days, it discards the week.
- Match day of the week: you choose which weekday the round falls on, Saturday by default.
- The home team's time slot: every match takes the home day and kick-off time configured on the team that hosts. The club that plays on Sundays at 11:00 always hosts at that time.
The options used to build the calendar — return legs, match day, whether the pivot starts at home, holidays, number of groups — are stored in the season itself, not in the head of whoever pressed the button. Regenerating two years later reproduces the same calendar, and the public holidays can be copied from another competition, instead of marking them one by one all over again.
After generating, the calendar is still yours
No league survives the season exactly as it was generated. Everything is editable:
- Manual editing: add or remove rounds and matches, change the teams in a fixture and edit both the official and the visible date.
- Swap home and away: one click reverses the venue of a match and syncs the return leg, so both legs do not end up being played in the same place.
- Clash warning: if two teams from the same club end up at home on the same day and at the same time, the system warns you before you publish.
Postponement requests will arrive anyway, but instead of being settled over the phone they are submitted from the app, and the opposing club answers from the app too. The organisation sets the windows in advance: minimum notice in hours, final rounds that can no longer be postponed, and the maximum number of days a match may be moved.
Every team's calendar, as a PDF
Once the calendar is closed, each team downloads its own as a PDF: its matches, dates and times, with the logo and the name of the organisation publishing it. It is the document that goes on the noticeboard, and nobody has to lay it out.
Start with the list of teams
Generating a whole league calendar should not eat up your August. With the teams loaded, their time slots configured and the holidays marked, the full season — return legs, byes and real dates included — is one button away. If you want to see it with your own competition, try DMS Manager free for 14 days and generate your first season in an afternoon.