From 8cb7dc7ca103f849814073a96da7ae739d94746d Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 24 May 2026 14:21:45 +0300 Subject: [PATCH] In league mode, have all matches be unlocked from the start Closes #2861 --- .../tournament-bracket/core/Tournament.ts | 1 + app/modules/brackets-manager/create.ts | 6 ++++- .../brackets-manager/test/round-robin.test.ts | 26 +++++++++++++++++++ app/modules/brackets-model/input.ts | 10 +++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/app/features/tournament-bracket/core/Tournament.ts b/app/features/tournament-bracket/core/Tournament.ts index 4f0ed86f4..b143b4bb5 100644 --- a/app/features/tournament-bracket/core/Tournament.ts +++ b/app/features/tournament-bracket/core/Tournament.ts @@ -558,6 +558,7 @@ export class Tournament { groupCount: Math.ceil(participantsCount / teamsPerGroup), seedOrdering: ["groups.seed_optimized"], hasAbDivisions: selectedSettings?.hasAbDivisions ?? false, + ...(this.isLeagueDivision ? { independentRounds: true } : {}), }; } case "swiss": { diff --git a/app/modules/brackets-manager/create.ts b/app/modules/brackets-manager/create.ts index f4ab402e7..c599faa21 100644 --- a/app/modules/brackets-manager/create.ts +++ b/app/modules/brackets-manager/create.ts @@ -469,7 +469,11 @@ class Create { // In round-robin, only the first round is ready to play at the beginning. // other matches have teams set but they are busy playing the first round. - if (this.stage.type === "round_robin" && roundNumber > 1) { + if ( + this.stage.type === "round_robin" && + roundNumber > 1 && + !this.stage.settings?.independentRounds + ) { status = Status.Locked; } diff --git a/app/modules/brackets-manager/test/round-robin.test.ts b/app/modules/brackets-manager/test/round-robin.test.ts index 879e56886..d5147395e 100644 --- a/app/modules/brackets-manager/test/round-robin.test.ts +++ b/app/modules/brackets-manager/test/round-robin.test.ts @@ -368,6 +368,32 @@ describe("Update scores in a round-robin stage", () => { expect(round2Match2After.status).toBe(2); // Ready }); + test("should leave every match Ready when independentRounds is set", () => { + storage.reset(); + manager.create({ + name: "Independent rounds", + tournamentId: 0, + type: "round_robin", + seeding: [1, 2, 3, 4], + settings: { groupCount: 1, independentRounds: true }, + }); + + const matches = storage.select("match")!; + for (const match of matches) { + expect(match.status).toBe(2); + } + + // reporting a round-2 match before round-1 finishes must not throw + const round2Match = storage.select("match", 2)!; + expect(() => + manager.update.match({ + id: round2Match.id, + opponent1: { score: 16, result: "win" }, + opponent2: { score: 4 }, + }), + ).not.toThrow(); + }); + test("should unlock next round matches with BYE participants", () => { storage.reset(); // Create a round robin with 3 teams (odd number creates rounds where one team doesn't play) diff --git a/app/modules/brackets-model/input.ts b/app/modules/brackets-model/input.ts index 6651eff3d..205fed8dd 100644 --- a/app/modules/brackets-model/input.ts +++ b/app/modules/brackets-model/input.ts @@ -110,6 +110,16 @@ export interface StageSettings { */ manualOrdering?: number[][]; + /** + * Whether matches in a round-robin stage are playable independently of each other. + * + * - If `false` (default), only round 1 matches start `Ready`; later rounds start + * `Locked` and unlock as both opponents complete the previous round. + * - If `true`, every match with two opponents starts `Ready` (used by league + * formats where weeks are scheduled independently and may be played out of order). + */ + independentRounds?: boolean; + /** * Optional final between semi-final losers. */