Pairing & season format
How we pair you in Phase 1 and how the season is structured. Each section has a short summary; expand the sections below when you want the full detail (algorithms, tiebreaker maths, and why we do it this way).
Season in one sentence
Phase 1 (Weeks 1–5): One big pool, Swiss pairing — you're matched with teams on a similar record, we avoid rematches, and byes are fair. Phase 2 (Weeks 6–12): You're in a division (A, B, or C) and play everyone in that division once. Playoffs: Top 4 per division, Best-of-3 for the Grand Final.
Phase 1: How you get paired
- Week 1: Everyone is 0–0. Pairings are effectively random (no seeding). If there's an odd number of teams, one team gets a bye that round.
- Weeks 2–5: You're paired with teams on the same or closest record (e.g. 2–1 vs 2–1). We avoid rematches when we can. We also try to avoid stacking "pairdowns" on one team: if you already had to play someone on a different record once, we prefer to give you a same-record opponent next.
- Odd number of teams: One team gets a bye that round. The bye goes to the lowest-ranked team that hasn't had a bye yet. A bye is worth 3 points (same as a win) and doesn't count in your "opponent strength" (resistance) for tiebreakers.
Algorithm & how we pair (cost, Blossom, round by round)
What we're trying to achieve
We want Phase 1 pairings to be fair, predictable, and free of obvious abuses:
- Same record when possible. If you're 2–1, you play another 2–1 team. Keeps the competition meaningful and avoids lucky/unlucky draws.
- No rematches. You already played that team in Phase 1; we avoid pairing you again unless the field forces it (e.g. small pool, many rounds).
- No stacking "pairdowns" on one team. When we have to pair across records (e.g. 2–1 vs 1–2), we track how often each team has had that and spread it — we don't give the same team repeated pairdowns.
- Fair byes. Odd number of teams → one bye. It goes to the lowest-ranked team that hasn't had a bye yet. Bye = 3 points (like a win) and doesn't count in resistance, so you're not penalised.
We don't use four separate rules that might conflict. One algorithm each round: encode all of the above as costs on a graph, then find the set of pairings (and who gets the bye) that minimises total cost. Whole round optimised together.
The idea: minimise "cost" across the whole field
We build a graph each round: teams = nodes, possible pairings = edges. Each edge gets a cost:
- Same record (e.g. both 2–1) → low cost (algorithm prefers these).
- Different record (e.g. 2–1 vs 1–2) → higher cost (score difference cubed; big gaps strongly discouraged).
- Rematch (already played in Phase 1) → very high cost (only chosen if there’s no other way).
- Repeated pairdown → extra cost if that team has already had a pairdown; we spread the pain.
BYE when odd teams: We add a BYE node. Any team can be paired with BYE (they get the bye). Cost is set so:
- Better-ranked teams → higher cost for bye (we don’t want the top team sitting out).
- Worse-ranked teams → lower cost for bye.
- Already had a bye → penalty so byes are spread fairly.
So the bye goes to the lowest-ranked team that hasn’t had a bye yet.
Once every edge (and the BYE option) has a cost, we run a minimum-weight perfect matching algorithm. In short:
- Goal: Choose a set of pairings so every team is in exactly one "pair" (with another team or with BYE).
- Objective: Minimise the sum of the costs of the chosen edges.
- Algorithm: We use the Blossom algorithm (Edmonds' algorithm for minimum-weight maximum matching) — a well-known, efficient way to solve exactly this problem.
- Result: One pairing per match and, when relevant, which team gets the bye. That set minimises total cost, so we get same-record where possible, no rematches where possible, and fair bye assignment.
One round: possible pairings and their cost
Teams are nodes; each possible pairing is an edge with a cost. We pick the perfect matching that minimises total cost (Blossom).
Green edges = chosen matching (lowest total cost). BYE used when odd teams.
Round-by-round flow (Rounds 2–5)
Each round we build a cost graph from standings and history, then run minimum-cost perfect matching (Blossom/Edmonds).
Points (wins/losses)
per team this phase
Previous opponents
who already played whom
Mispairing count
how often paired up/down
Cost graph + Blossom
Weights: same record = low; diff record = score diff³; rematch = very high. Minimum-weight perfect matching (Blossom/Edmonds).
Pairings + bye
One match per pair; one team gets BYE if odd count.
Round by round
Round 1
Everyone 0–0
We still build the graph and assign costs, but with identical records "same" vs "different" doesn’t differ — so pairings are effectively random (plus bye rule if odd teams). No seeding; fresh start.
Rounds 2–5
Inputs → Blossom → pairings
We feed current points, previous opponents, and mispairing count into the algorithm. It outputs one pairing per match and who gets the bye — whole round solved in one go.
Round 1: Everyone is 0–0. We still build the graph and assign costs, but since everyone has the same record, "same record" and "different record" don't differ — so pairings are effectively random (subject to the odd/bye rule if the number of teams is odd). We don't seed; Week 1 is a fresh start.
Rounds 2–5: For each round we feed the algorithm:
- Current points (wins × 3 + byes × 3) for every team, so we know who is "same record" and who is "different record."
- Previous opponents: who has already played whom, so we can assign very high cost to rematches.
- Mispairing count: how many times each team has already been paired with a different-record opponent in earlier rounds, so we can add extra cost to giving them another pairdown.
Output:
- One pairing per match (Team A vs Team B).
- If the number of teams is odd, which team gets the bye.
- We don't "pair the first two same-record teams" and then the next two — the whole round is solved in one go, so the global result is as fair as possible.
After Phase 1: Divisions and tiebreakers
Where you finish in the Phase 1 standings decides your division (e.g. top 8 → Division A, next 8 → Division B). Tiebreakers order teams when they're tied on points (e.g. 8th vs 9th for the last Division A spot):
- Points (wins × 3 + byes × 3)
- Opponent win % (only opponents you actually played; bye rounds don't count)
- Soul differential
- Total souls
Tiebreakers in detail
When two or more teams have the same number of points, we need a consistent way to order them — for standings, for division cutoffs (e.g. who gets 8th and Division A vs 9th and Division B), and for display. We use a fixed tiebreaker order. We compare teams on the first criterion; if tied, we move to the second; and so on until we have an order.
1. Points. First we look at raw points: wins × 3, plus bye × 3 if you had a bye. This is already the main ranking, but when points are equal we need more.
2. Resistance (opponent win %). We look at the average win rate of the opponents you actually played. Only opponents you played count — bye rounds do not add a "ghost" opponent. So if you had a bye in Round 3, that round simply doesn't appear in your resistance calculation; we don't give you a 0% or 50% fake opponent. That way having a bye doesn't unfairly hurt or help your tiebreaker. Resistance rewards playing stronger schedules: if you're 3–1 and your opponents have a high combined win rate, you're ranked above another 3–1 team whose opponents were weaker.
3. Soul differential. If points and resistance are still tied, we use souls for minus souls against (across your Phase 1 matches). Higher differential ranks higher.
4. Total souls. If we're still tied (rare), we use total souls scored. Higher total ranks higher.
We don't use head-to-head as a tiebreaker for Phase 1 standings (it can create circular ties and is harder to explain). The order above is simple and deterministic.
Division boundaries: Sometimes two teams are tied exactly at a cutoff (e.g. 8th vs 9th for the last Division A spot). Per-season policy may allow a playoff match in the break week between Phase 1 and Phase 2: one game (or short series) to decide who gets the higher division. Winner gets Division A; loser gets Division B. This is optional and announced at the start of the season if we're using it. Platform note: OCElock does not auto-create or schedule that tiebreaker match yet — staff run it out-of-band if the season rules call for it, then use Admin → Rounds division preview / adjustments before confirming transition.
Tiebreaker order (first criterion wins)
Points
Wins and losses in the phase
Resistance
Strength of schedule (opponents’ win rates)
Soul differential
Souls for minus souls against
Total souls
Souls scored (if still tied)
Byes: who gets them and how they affect you
When the number of teams is odd, exactly one team gets a bye that round. The bye goes to the lowest-ranked team that hasn't had a bye yet (rank by points, then tiebreakers). 3 points per bye (same as a win). For tiebreakers, bye rounds do not add a "ghost" opponent — your resistance is based only on opponents you actually played, so you're not penalised for having had a bye.
Who gets the bye
Odd teams → one bye per round
Bye goes to the lowest-ranked team that hasn't had a bye yet. Rank order:
- 1Points
- 2Resistance
- 3Soul diff
- 4Total souls
If everyone's already had a bye → fewest byes so far, then lowest rank.
Points & resistance
No penalty for having a bye
- 3 points— same as a win. You're not penalised in points.
- Resistance = only opponents you actually played. Bye rounds do notadd a ghost opponent — that round isn't counted, so you're not disadvantaged at division cutoffs.
Byes: full detail
Who gets the bye: When the number of teams is odd, exactly one team gets a bye that round. We don't pick at random. The bye goes to the lowest-ranked team that hasn't had a bye yet. Rank is determined by the same order we use for standings: points first, then resistance (opponent win %), then soul differential, then total souls. So the team at the bottom of the current standings, among those who haven't had a bye, gets the bye. If everyone has already had a bye (e.g. 5 rounds with 6 teams — everyone gets one bye), we use fewest byes so far and then lowest rank. That way we spread byes as fairly as possible and don't give the same team two byes while someone else has had none.
Points: A bye is worth 3 points, the same as a win. So in the standings it's like you "won" that round. You're not penalised in points for having sat out.
Resistance (tiebreakers): Your "strength of schedule" for tiebreakers is based only on opponents you actually played. Bye rounds do not add a ghost opponent. We don't give you a 0% or 50% fake opponent for that round — we simply don't count that round in your resistance. So having had a bye doesn't dilute your resistance (e.g. by adding a weak fake opponent) and doesn't skew it either. When we're deciding 8th vs 9th for Division A, you're not disadvantaged for having had a bye; your resistance is computed only from the teams you actually faced.
Forfeits (no-show or can't field)
If a team doesn't show or can't field a full team by the agreed match time, the other captain marks a forfeit in the app. That's not a bye — it's a win for the team that showed (3 points) and a loss for the forfeiting team (0 points). The forfeiting team is not added to the showing team's "opponent strength" (resistance) for tiebreakers. Full forfeit rules (disputes, repeated forfeits) are in Match rules.
Forfeit is not a bye — it's a win and a loss
Team that showed
Gets the win
- Win + 3 points
- Resistance: forfeiting team is notadded — that round doesn't count in opponent win %. No free strength bump.
Forfeiting team
Gets the loss
- Loss + 0 points
- Normal W/L for points and record; the only special rule is resistance for the team that showed (above).
Forfeits: resistance and record
A forfeit is when one team doesn't show or can't field a full team by the agreed match time. The other captain (the one whose team showed) marks the match as a forfeit in the app. That's not a "bye" — it's a win for the team that showed and a loss for the team that forfeited.
Team that showed: They get a win and 3 points, exactly as if they had won a played match. For tiebreakers, the forfeiting team is not added to their resistance. We don't treat the forfeiting team as a 0% opponent (which would unfairly inflate the showing team's strength of schedule). So the showing team gets the W and the points, but that match doesn't count in their "opponent win %" — it's as if that round didn't exist for resistance purposes. That keeps things fair: you don't get a free strength bump from someone no-showing.
Forfeiting team: They get a loss and 0 points. So the result is a normal win/loss for both sides in terms of points and record; the only special rule is how we handle resistance for the team that showed. Full forfeit rules — including disputes, repeated forfeits, and how we handle multiple no-shows — are in Match rules.
If a team drops out
If a team withdraws from the season during Phase 1, all their past results stay. For any round they were scheduled but don't play, their opponent gets a bye (3 points, one bye). Teams who already played them keep that opponent in their strength-of-schedule — we don't change the past.
We don't rewrite history — here's exactly what happens
Past results stay
We don't rewrite history
- All completed matches involving the dropped team stand.
- Wins and losses against them stay as-is.
- Standings for already-played rounds stay as they are.
Future rounds
Their opponent gets a bye
- For every round they were scheduled but don't play, their opponent gets a bye.
- 3 points + one bye (same as a normal bye).
- Bye rules apply — not a "free win" without points.
Resistance
Strength-of-schedule unchanged
- Teams who already played the dropped team keep that opponent in their resistance.
- Dropped team's final record counts like any other opponent for "opponent win %".
- We don't remove them from your strength-of-schedule; resistance stays consistent.
Dropouts: full detail
If a team withdraws from the season during Phase 1 (drops out), we don't rewrite history. Here's exactly what happens.
Past results: All completed matches involving the dropped team stand. If they beat you in Week 2, that's still a loss for you. If you beat them in Week 1, that's still a win. We don't retroactively remove their results or pretend those matches didn't happen. Standings and records for everyone else stay as they are for the rounds already played.
Future rounds: For every round in which the dropped team was scheduled to play but no longer plays (because they've withdrawn), their opponent gets a bye. So that opponent gets 3 points and one bye for that round — same as a normal bye. We don't leave them without a match or give them a "free win" without points; we treat it as a bye so the bye rules (points, resistance exclusion) apply consistently.
Resistance: Teams who already played the dropped team keep that opponent in their resistance. The dropped team's final record (at the time they left, or however we close it out) counts like any other opponent when we compute "opponent win %." We don't remove them from your strength-of-schedule just because they dropped. So your resistance stays consistent with the matches that were actually played.
What we prevent (and why)
What we avoid and how
Uneven pairing
Global cost minimisation (Blossom); whole round optimised together.
Rematches
Very high cost (or disallowed); only if no other way.
Repeated pairdowns
Mispairing count + penalty so algorithm prefers to spread pairdowns.
Unfair bye
Bye in same optimisation; cost set so lowest-ranked without bye gets it.
Bye hurting tiebreakers
Resistance = real opponents only; bye rounds don't count.
Problems we avoid and how
We designed the pairing and scoring rules to avoid several common problems. Here’s what we avoid and how.
Uneven pairing. If we just "paired the first two same-record teams" and then the next two, we might leave awkward leftovers (e.g. one 2–1 stuck with a 0–3). We use global cost minimisation (Blossom-style): the whole round is optimised in one go. So the set of pairings is as good as possible across the entire field — same record where possible, and when we have to pair across records, the algorithm chooses who gets the pairdown in a way that minimises total cost (and spreads pairdowns when we track mispairing count).
Rematches. We don't want you playing the same team twice in Phase 1. Pairings that already happened are either disallowed or given very high cost, so the algorithm only chooses a rematch when there is no other valid way to pair everyone (e.g. small field, many rounds). So you only play the same team again if it's unavoidable.
Repeated pairdowns. If we didn't track "how often has this team been paired with a different record," we might give one team two or three pairdowns in a row while others get none. We track how many times each team has already been paired with a different-record opponent and add a penalty so the algorithm prefers to give them a same-record opponent next. That spreads the pairdowns across teams instead of stacking them on one.
Unfair bye. The bye isn't assigned by a separate rule that might conflict with pairing. The bye is a node in the same graph and the same optimisation. The cost of giving the bye to a team is set so that the lowest-ranked team that hasn't had a bye yet gets it. So the bye assignment is fair and consistent with the rest of the round.
Bye hurting tiebreakers. Some leagues count a bye as a "ghost" opponent (e.g. 0% or 50%), which can skew strength-of-schedule. We don't. Resistance uses only real opponents; bye rounds do not count in your opponent win %. So you're not penalised at division cutoffs for having had a bye — your resistance is based only on the teams you actually played.
| Problem | What we do |
|---|---|
| Uneven pairing | Global cost minimisation (Blossom); whole round optimised together. |
| Rematches | Very high cost (or disallowed); only if no other way. |
| Repeated pairdowns | Mispairing count + penalty so algorithm prefers to spread pairdowns. |
| Unfair bye | Bye in same optimisation; cost set so lowest-ranked without bye gets it. |
| Bye hurting tiebreakers | Resistance = real opponents only; bye rounds don't count. |
Summary
| Question | Answer |
|---|---|
| Round 1? | Random pairings (everyone 0–0). |
| Rounds 2–5? | Same record when possible; no rematches when possible; we avoid repeated pairdowns. |
| Bye? | One per round when odd; goes to lowest-ranked without a bye yet; 3 points; doesn't count in resistance. |
| Tiebreakers? | Points → opponent win % (real opponents only) → soul diff → total souls. |
| Forfeit? | No-show / can't field → other team gets a win (3 pts), you get a loss (0 pts). Not a bye. |
| Dropout? | Past results stand; their future opponents get a bye. |