Spec — Tournament Calculator: team tournament format¶
Status: not started. Nothing here is built. Written 2026-10-05 against
sage-match-control.github.io/tools/tournament-calculator.htmlas of that date and the first team event, PickleDrive Club One Year Celebration. The rules and the model are settled by that event; §9 lists the few questions that are the owner's.
Add a third option to the calculator's Format menu, Team tournament, so a team event can be planned the way a standard tournament or a dual meet already is: how many matchups and matches, how many time slots, and what time the last match finishes, before the draw is final.
Independent of the Team Tournament Master: this changes only the calculator. It does give that generator an input to read later (§6), but the calculator ships first without it.
0. How to use this spec¶
- One file changes:
sage-match-control.github.io/tools/tournament-calculator.html(a single self-contained page: inline<style>, inline<script>, no build step, 2-space indent, single quotes, classic script). Docs change insage-docs. - Find code by function name, not line number. The functions named below exist
today:
calcAll,calcCategory,calcDualCategory,catTime,readForm,recalc,buildPlanCsv,importCSVText,exportXLSX,toggleDualClubsVisibility,splitEven,GENERATOR_MASTERS. - Don't touch the standard or dual-meet maths, their CSV columns, or
sw.js(§7). - Working copies use CRLF line endings in some repos; keep each file's.
1. Why the model differs from the other two formats¶
| Standard | Dual meet | Team | |
|---|---|---|---|
| Unit that plays | a pair, in a category | a pair vs a pair from the other club | a team meets a team: one matchup of several matches |
| Categories | many, each with its own count and brackets | many | none: one competition |
| Group stage | round robin inside a bracket, one match per pairing | cross-club, one match per pairing | round robin inside a group, one matchup per pairing, each P matches |
| Playoffs | per category, from the top k of each group | per category | one shared playoff: QF, SF, Bronze, Final, each a matchup |
| What limits the clock | courts, and that a pair can't be on two courts | the same | courts, and that a team plays one matchup at a time, which takes several slots |
So the calculator treats the team format as one competition with no category cards, and works in matchups rather than matches.
2. The model¶
Inputs (all new, team format only):
| Input | Default | Meaning |
|---|---|---|
| Teams, T | 15 | |
| Groups, G | 3 | Teams are split as evenly as possible with splitEven(T, G), bigger groups first (15 into 3 is 5, 5, 5) |
| Matches per matchup, P | 4 | One match per pair of the two teams |
| Matches on court at once, S | 2 | How many of a matchup's matches share a slot. PickleDrive played pairs 1–2 in one slot and pairs 3–4 in the next |
| Teams to the playoffs, A | 8 | 2, 4 or 8 (§9 Q2). The organiser chooses who; the calculator only counts |
| Break before the playoffs (min) | 0 | Idle time between the last group match and the first playoff slot |
The existing Courts, Match duration, Buffer and Start time inputs are reused as they are.
Derived:
- Group matchups = Σ over groups of n(n−1)/2. Group matches = that × P.
- Playoff matchups by A: 8 → QF 4 + SF 2 + Bronze 1 + Final 1; 4 → SF 2 + Bronze 1 + Final 1; 2 → Final 1. Playoff matches = matchups × P.
- Total matchups and total matches are the sums.
- Block =
ceil(P / S)slots: how long one matchup occupies the courts it uses. Lanes =floor(courts / S): how many matchups can run at once. (Courts below S is an error: §5.)
Time, in slots (each slot is dur + buf minutes):
- Group stage =
max( ceil(groupMatchups / lanes), R ) × block, where R is the biggest group's round count (n − 1 if n is even, n if odd, since a team can't be in two matchups at once, so a group can't finish faster than its rounds). Groups run side by side. - Each playoff round =
ceil(matchupsInRound / lanes) × block. Bronze and Final are one round, played at the same time. Rounds run one after another. - Total slots = group slots + Σ playoff round slots.
- Play time =
slots × (dur + buf) − buf + break. Projected finish = start + play time, formatted by the existingfmtTime, with its "+1d" handling.
Worked examples (the verification targets)¶
15 teams, 3 groups, P 4, S 2, 10 courts, 25-minute matches, no buffer, 9:00 AM
start. Lanes 5, block 2, R 5, 30 group matchups = 120 matches, group stage
max(6, 5) × 2 = 12 slots.
| Plan | Matchups | Matches | Playoff slots | Slots | Break | Finish |
|---|---|---|---|---|---|---|
| A 4 (PickleDrive's published plan: SF, Bronze, Final) | 34 | 136 | 4 | 16 | 25 min | 4:05 PM |
| A 8 (the quarterfinal plan the workbook was changed to) | 38 | 152 | 6 | 18 | 0 | 4:30 PM |
| A 8 | 38 | 152 | 6 | 18 | 25 min | 4:55 PM |
The first row matches the event's published schedule: the group stage ends at 2:00 PM, the semifinals start at 2:25 PM after a one-slot break, and the Final and Bronze start at 3:15 PM and 3:40 PM. The counts (136 and 152 matches, 34 and 38 matchups) are the event's own.
3. UI¶
- Format gains
<option value="team">Team tournament</option>. Standard and dual are unchanged. - Team format shows: the existing Tournament details panel (title, date, start, courts, duration, buffer), plus a new Team tournament panel with the six inputs of §2, in place of the Categories panel. The club-name fields stay hidden, as for standard. Category data is kept in memory, so switching format and back loses nothing.
- Results keep the big clock, the stat grid (matches, slots, play time) and the alerts. The breakdown table becomes Breakdown with one row per stage:
| Stage | Matchups | Matches | Slots | Time |
|---|---|---|---|---|
| Group stage (3 groups: 5-5-5) | 30 | 120 | 12 | 5h |
| Quarterfinals | 4 | 16 | 2 | 50m |
| Semifinals | 2 | 8 | 2 | 50m |
| Bronze + Final | 2 | 8 | 2 | 50m |
| Total | 38 | 152 | 18 | 7h 30m |
The header text under the clock reads Team tournament, with the date, start
and courts as the other formats do.
- Playoff chips (like the other formats' tagchips) list the rounds and their
matchup counts. No bracket chart in this version.
- Everything recalculates on input, as now, and persists through save() under
the same ttc-config key, with the team inputs added to the saved object. A saved
object without them loads with the defaults.
4. Export and import¶
CSV keeps one file format and adds a value to it. For format = team:
- the
settingrows are the existing ones (title,date,start,courts,duration_min,buffer_min,format) plus six more:teams,groups,matches_per_matchup,matches_per_slot,advance_teams,break_min; - there are no
categoryrows; club_aandclub_bare written empty.
The header line is unchanged. importCSVText reads these keys when format is
team, clamps them like the form does, switches the format menu, and shows the
team panel. A file that is not team imports exactly as today. A team file
imported by an older copy of the page is not supported and says "no categories
found", which is acceptable.
.xlsx (exportXLSX) gets a team layout: the header lines, then the stage
table of §3 with its totals row, on the same Breakdown sheet. The existing
fileBase naming is unchanged.
5. Alerts¶
Reuse the three existing finish-time alerts (over midnight, after 10 PM, comfortable) unchanged, and add warnings, which are notes and never block:
| Condition | Message |
|---|---|
courts < S |
Needs at least S courts: a matchup puts S of its matches on court at once. Results show 0 until fixed |
T < 2, or G > floor(T / 2) |
Each group needs at least 2 teams: using G′ groups |
A > T |
Only T teams: the playoff is capped at the largest of 2, 4, 8 that fits |
A < G |
Fewer playoff places than groups: not every group can send its winner |
| a group of size 2 | A 2-team group plays one matchup |
| R × block > group slots from capacity | The biggest group's rounds, not the courts, set the length of the group stage |
lanes > a playoff round's matchups |
Courts idle during the round name |
A not 2, 4 or 8 |
Not supported: choose 2, 4 or 8 |
Plus one fixed note, always shown: Who advances is the organiser's choice: the calculator only counts the places.
6. Generator handoff¶
GENERATOR_MASTERS has dual and standard. For team there is no master yet
(Team Tournament Master builds it), so:
- while
GENERATOR_MASTERS.teamis absent, the Build the event workbook box is hidden for the team format, rather than offering a master that cannot read the plan; - when the Master exists, add its entry (file ID and help text) and the box returns;
nothing else in the handoff changes, because
buildPlanCsv()already feeds both the file and the clipboard.
Both existing generators already refuse a plan whose format is not theirs, with
a message naming the format they found, so a team CSV pasted into the wrong master
fails clearly with no change to either script.
7. What does not change¶
The standard and dual-meet calculations and their cards, PRESETS, buildChart,
the CSV columns, GENERATOR_MASTERS' existing entries, and sw.js. The service
worker serves the document network-first and its cached set does not change, so
CACHE is not bumped (PWA spec).
Nothing in sage-tools-api changes.
8. Acceptance checklist¶
Serve the site and open the calculator (a git-ignored tournament-calculator-beta.html
copy is enough while building).
- Format lists three options. Standard and Dual meet behave exactly as before (compare a saved plan's numbers before and after).
- Team format shows the team panel and no category cards; switching back restores the categories.
- The three rows of §2's table reproduce: 136 / 34 / 16 slots / 4:05 PM; 152 / 38 / 18 slots / 4:30 PM; 4:55 PM with the break.
- Stage table totals equal the stat grid (matches and slots).
- Each alert of §5 appears for a plan that triggers it and is absent otherwise.
- Export CSV, then Import CSV (and Reset first): the team plan round-trips with the
same numbers. Open the file: no
categoryrows, the six newsettingrows. - Export
.xlsxopens and shows the stage table and totals. - A saved team plan survives a reload; an old saved plan (no team keys) loads.
- The handoff box is hidden for the team format and back for the others.
- No console errors, at desktop and 375 px, and the page still installs and works offline (the PWA).
9. Decisions for the owner¶
| # | Question | Leaning |
|---|---|---|
| Q1 | Default for break before the playoffs: 0, or 25 minutes as PickleDrive ran? | 0: it is the organiser's choice, and the worked examples show both |
| Q2 | Which playoff sizes: 2, 4, 8 only, or any number? | 2, 4, 8, which covers the quarterfinal-semifinal-final shape the format has used |
| Q3 | Should S default to 2, or be derived from the roster (men and women needed per pair)? | 2, as an input: roster rules are the Master's concern |
| Q4 | Include a playoff bracket chart for teams? | Not in this version |
| Q5 | Should the calculator take team names, so the CSV can carry them to the generator? | No: names and rosters are the generator's inputs (Master Q4) |
10. Out of scope¶
- Building the schedule (which matchup is on which court in which slot) and any tiebreak or ranking rule: those belong to the workbook.
- Team names and rosters.
- Categories inside a team event.
- A team calculator for formats other than group stage then QF/SF/Bronze/Final.
- Any change to the Team Tournament Master, the event-site template or
sage-tools-api.
Related: Tournament Calculator usage · technical · Team Tournament Master · Team tournament event-site template