From 6dc2dd29867cc7bf1beeb864e1119feefa3c4bf7 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 26 Jul 2026 12:40:43 +0300 Subject: [PATCH] Bracket engine refactor (#3249) --- .../match-page/MatchActionTab.module.css | 2 +- app/components/match-page/MatchActionTab.tsx | 31 +- app/components/match-page/MatchTimeline.tsx | 17 +- app/db/seed/index.ts | 8 +- app/db/tables.ts | 39 +- .../routes/sendouq.match.$matchId.ts | 2 +- .../api-public/routes/tournament-match.$id.ts | 27 +- app/features/api-public/schema.ts | 13 +- .../bracket-test/routes/bracket-test.tsx | 65 +- .../calendar/actions/calendar.$id.server.ts | 9 +- app/features/calendar/calendar-schemas.ts | 2 +- .../components/BracketProgressionSelector.tsx | 2 +- app/features/core/streams/streams.server.ts | 15 +- .../LeaderboardRepository.server.test.ts | 1 - .../routes/match-page-test.tsx | 4 +- .../scrims/components/ScrimMatchActionTab.tsx | 2 +- .../components/SendouQMatchActionTab.tsx | 2 +- .../actions/to.$id.admin.brackets.server.ts | 3 +- .../actions/to.$id.admin.index.server.ts | 26 +- .../BracketRepository.server.ts | 379 ++ .../actions/to.$id.brackets.server.ts | 103 +- .../Bracket/Bracket.browser.test.tsx | 562 ++- .../components/Bracket/Elimination.tsx | 24 +- .../components/Bracket/Match.tsx | 22 +- .../components/Bracket/PlacementsTable.tsx | 13 +- .../components/Bracket/RoundRobin.tsx | 21 +- .../components/Bracket/Swiss.tsx | 25 +- .../components/BracketMapListDialog.tsx | 27 +- .../tournament-bracket/core/Bracket.test.ts | 318 +- .../core/Bracket/Bracket.ts | 152 +- .../core/Bracket/DoubleEliminationBracket.ts | 72 +- .../core/Bracket/RoundRobinBracket.ts | 53 +- .../core/Bracket/SingleEliminationBracket.ts | 48 +- .../core/Bracket/SwissBracket.ts | 55 +- .../tournament-bracket/core/Bracket/utils.ts | 10 +- .../tournament-bracket/core/PreparedMaps.ts | 2 +- .../tournament-bracket/core/Swiss.test.ts | 59 +- app/features/tournament-bracket/core/Swiss.ts | 533 --- .../core/Tournament.server.ts | 13 +- .../core/Tournament.test.ts | 7 +- .../tournament-bracket/core/Tournament.ts | 183 +- .../core/brackets-manager/crud-db.server.ts | 501 --- .../core/brackets-manager/crud.server.ts | 218 -- .../core/brackets-manager/index.ts | 1 - .../core/brackets-manager/manager.server.ts | 9 - .../core/brackets-manager/manager.ts | 9 - .../core/engine/create/builder.ts | 368 ++ .../engine/create/double-elimination.test.ts | 40 + .../core/engine/create/double-elimination.ts | 44 + .../core/engine/create}/helpers.test.ts | 262 +- .../core/engine/create/helpers.ts | 357 ++ .../core/engine/create/index.ts | 120 + .../core/engine/create/round-robin.test.ts | 212 + .../core/engine/create/round-robin.ts | 112 + .../core/engine/create/seeding.ts} | 94 +- .../core/engine/create/settings.ts | 88 + .../engine/create/single-elimination.test.ts | 93 + .../core/engine/create/single-elimination.ts | 31 + .../core/engine/create/swiss.ts | 144 + .../core/engine/general.test.ts | 258 ++ .../tournament-bracket/core/engine/index.ts | 24 + .../propagation/double-elimination.test.ts | 273 ++ .../core/engine/propagation/dropped-teams.ts | 59 + .../core/engine/propagation/helpers.ts | 382 ++ .../core/engine/propagation/report-result.ts | 25 + .../core/engine/propagation/reset-result.ts | 64 + .../engine/propagation/round-robin.test.ts | 245 ++ .../core/engine/propagation/set.test.ts | 132 + .../core/engine/propagation/set.ts | 142 + .../propagation/single-elimination.test.ts | 94 + .../core/engine/propagation/store.ts | 120 + .../core/engine/propagation/traversal.ts | 612 +++ .../core/engine/propagation/update.test.ts | 178 + .../core/engine/status.test.ts | 100 + .../tournament-bracket/core/engine/status.ts | 232 ++ .../core/engine/swiss/pairing.ts | 295 ++ .../core/engine/swiss/team-status.ts | 94 + .../tournament-bracket/core/engine/types.ts | 278 ++ .../tournament-bracket/core/rounds.ts | 16 +- .../core/summarizer.server.ts | 23 +- .../core/summarizer.test.ts | 62 +- .../tournament-bracket/core/tests/mocks-li.ts | 3461 +++++++---------- .../core/tests/mocks-sos.ts | 950 ++--- .../core/tests/mocks-zones-weekly.ts | 139 +- .../tournament-bracket/core/tests/mocks.ts | 2463 +++++------- .../core/tests/test-utils.ts | 161 +- .../tournament-bracket/core/toMapList.ts | 37 +- .../queries/deletePickBanEvent.server.ts | 17 - .../routes/to.$id.brackets.tsx | 2 +- .../tournament-bracket-schemas.server.ts | 29 +- .../tournament-bracket-utils.test.ts | 25 - .../tournament-bracket-utils.ts | 7 - .../TournamentMatchRepository.server.test.ts | 12 +- .../TournamentMatchRepository.server.ts | 129 +- .../actions/to.$id.matches.$mid.server.ts | 459 +-- .../components/TournamentMatchActionTab.tsx | 20 +- .../components/TournamentMatchAdminTab.tsx | 43 +- .../components/TournamentMatchBanner.tsx | 12 +- .../components/TournamentMatchTabs.tsx | 10 +- .../tournament-match/core/mapList.server.ts | 76 - .../loaders/to.$id.matches.$mid.server.ts | 27 +- .../tournament-match/match-page-context.tsx | 9 +- .../deleteMatchPickBanEvents.server.ts | 10 - ...eParticipantsByMatchGameResultId.server.ts | 12 - ...eteTournamentMatchGameResultById.server.ts | 10 - .../insertTournamentMatchGameResult.server.ts | 16 - ...namentMatchGameResultParticipant.server.ts | 16 - .../updateMatchGameResultPoints.server.ts | 20 - .../routes/to.$id.matches.$mid.test.ts | 7 +- .../tournament-match-utils.test.ts | 95 +- .../tournament-match-utils.ts | 40 - .../tournament-organization/test-utils.ts | 1 - .../tournament/TournamentRepository.server.ts | 69 - .../tournament/core/Standings.test.ts | 155 +- app/features/tournament/core/Standings.ts | 13 +- .../loaders/to.$id.teams.$tid.server.ts | 5 +- .../createSwissBracketInTransaction.server.ts | 126 - .../queries/updateRoundMaps.server.ts | 19 - .../tournament/tournament-test-utils.ts | 52 +- .../tournament/tournament-utils.server.ts | 61 +- app/features/tournament/tournament-utils.ts | 2 +- app/modules/brackets-manager/README.md | 1 - app/modules/brackets-manager/base/getter.ts | 678 ---- app/modules/brackets-manager/base/updater.ts | 331 -- app/modules/brackets-manager/create.ts | 975 ----- app/modules/brackets-manager/get.ts | 75 - app/modules/brackets-manager/helpers.ts | 1376 ------- app/modules/brackets-manager/index.ts | 11 - app/modules/brackets-manager/manager.ts | 119 - app/modules/brackets-manager/reset.ts | 54 - .../test/double-elimination.test.ts | 391 -- .../brackets-manager/test/general.test.ts | 389 -- .../brackets-manager/test/round-robin.test.ts | 511 --- .../test/single-elimination.test.ts | 234 -- .../brackets-manager/test/update.test.ts | 289 -- app/modules/brackets-manager/types.ts | 209 - app/modules/brackets-manager/update.ts | 25 - app/modules/brackets-memory-db/README.md | 1 - app/modules/brackets-memory-db/index.ts | 264 -- app/modules/brackets-model/index.ts | 4 - app/modules/brackets-model/input.ts | 147 - app/modules/brackets-model/other.ts | 66 - app/modules/brackets-model/storage.ts | 95 - app/modules/brackets-model/unions.ts | 55 - app/routines/syncTournamentVods.test.ts | 2 +- db-test.sqlite3 | Bin 1466368 -> 1474560 bytes e2e/helpers/playwright.ts | 18 +- e2e/helpers/tournament-match.ts | 14 + e2e/seeds/db-seed-AB_RR.sqlite3 | Bin 10522624 -> 10514432 bytes e2e/seeds/db-seed-DEFAULT.sqlite3 | Bin 10506240 -> 10539008 bytes e2e/seeds/db-seed-FINALIZED_BRACKET.sqlite3 | Bin 10551296 -> 10526720 bytes e2e/seeds/db-seed-IN_SQ_MATCH.sqlite3 | Bin 10526720 -> 10547200 bytes e2e/seeds/db-seed-NO_SCRIMS.sqlite3 | Bin 10514432 -> 10510336 bytes e2e/seeds/db-seed-NO_SQ_GROUPS.sqlite3 | Bin 10498048 -> 10559488 bytes e2e/seeds/db-seed-NO_TOURNAMENT_TEAMS.sqlite3 | Bin 10469376 -> 10473472 bytes e2e/seeds/db-seed-NZAP_IN_TEAM.sqlite3 | Bin 10522624 -> 10526720 bytes e2e/seeds/db-seed-REG_OPEN.sqlite3 | Bin 10502144 -> 10498048 bytes e2e/seeds/db-seed-SMALL_SOS.sqlite3 | Bin 10543104 -> 10489856 bytes e2e/seeds/db-seed-TEAM_MAP_PREFS.sqlite3 | Bin 10223616 -> 10235904 bytes e2e/tournament-bracket.spec.ts | 34 +- ...161-tournament-match-nullable-opponents.js | 107 + 161 files changed, 10111 insertions(+), 14543 deletions(-) create mode 100644 app/features/tournament-bracket/BracketRepository.server.ts delete mode 100644 app/features/tournament-bracket/core/Swiss.ts delete mode 100644 app/features/tournament-bracket/core/brackets-manager/crud-db.server.ts delete mode 100644 app/features/tournament-bracket/core/brackets-manager/crud.server.ts delete mode 100644 app/features/tournament-bracket/core/brackets-manager/index.ts delete mode 100644 app/features/tournament-bracket/core/brackets-manager/manager.server.ts delete mode 100644 app/features/tournament-bracket/core/brackets-manager/manager.ts create mode 100644 app/features/tournament-bracket/core/engine/create/builder.ts create mode 100644 app/features/tournament-bracket/core/engine/create/double-elimination.test.ts create mode 100644 app/features/tournament-bracket/core/engine/create/double-elimination.ts rename app/{modules/brackets-manager/test => features/tournament-bracket/core/engine/create}/helpers.test.ts (74%) create mode 100644 app/features/tournament-bracket/core/engine/create/helpers.ts create mode 100644 app/features/tournament-bracket/core/engine/create/index.ts create mode 100644 app/features/tournament-bracket/core/engine/create/round-robin.test.ts create mode 100644 app/features/tournament-bracket/core/engine/create/round-robin.ts rename app/{modules/brackets-manager/ordering.ts => features/tournament-bracket/core/engine/create/seeding.ts} (61%) create mode 100644 app/features/tournament-bracket/core/engine/create/settings.ts create mode 100644 app/features/tournament-bracket/core/engine/create/single-elimination.test.ts create mode 100644 app/features/tournament-bracket/core/engine/create/single-elimination.ts create mode 100644 app/features/tournament-bracket/core/engine/create/swiss.ts create mode 100644 app/features/tournament-bracket/core/engine/general.test.ts create mode 100644 app/features/tournament-bracket/core/engine/index.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/double-elimination.test.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/dropped-teams.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/helpers.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/report-result.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/reset-result.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/round-robin.test.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/set.test.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/set.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/single-elimination.test.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/store.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/traversal.ts create mode 100644 app/features/tournament-bracket/core/engine/propagation/update.test.ts create mode 100644 app/features/tournament-bracket/core/engine/status.test.ts create mode 100644 app/features/tournament-bracket/core/engine/status.ts create mode 100644 app/features/tournament-bracket/core/engine/swiss/pairing.ts create mode 100644 app/features/tournament-bracket/core/engine/swiss/team-status.ts create mode 100644 app/features/tournament-bracket/core/engine/types.ts delete mode 100644 app/features/tournament-bracket/queries/deletePickBanEvent.server.ts delete mode 100644 app/features/tournament-match/queries/deleteMatchPickBanEvents.server.ts delete mode 100644 app/features/tournament-match/queries/deleteParticipantsByMatchGameResultId.server.ts delete mode 100644 app/features/tournament-match/queries/deleteTournamentMatchGameResultById.server.ts delete mode 100644 app/features/tournament-match/queries/insertTournamentMatchGameResult.server.ts delete mode 100644 app/features/tournament-match/queries/insertTournamentMatchGameResultParticipant.server.ts delete mode 100644 app/features/tournament-match/queries/updateMatchGameResultPoints.server.ts delete mode 100644 app/features/tournament/queries/createSwissBracketInTransaction.server.ts delete mode 100644 app/features/tournament/queries/updateRoundMaps.server.ts delete mode 100644 app/modules/brackets-manager/README.md delete mode 100644 app/modules/brackets-manager/base/getter.ts delete mode 100644 app/modules/brackets-manager/base/updater.ts delete mode 100644 app/modules/brackets-manager/create.ts delete mode 100644 app/modules/brackets-manager/get.ts delete mode 100644 app/modules/brackets-manager/helpers.ts delete mode 100644 app/modules/brackets-manager/index.ts delete mode 100644 app/modules/brackets-manager/manager.ts delete mode 100644 app/modules/brackets-manager/reset.ts delete mode 100644 app/modules/brackets-manager/test/double-elimination.test.ts delete mode 100644 app/modules/brackets-manager/test/general.test.ts delete mode 100644 app/modules/brackets-manager/test/round-robin.test.ts delete mode 100644 app/modules/brackets-manager/test/single-elimination.test.ts delete mode 100644 app/modules/brackets-manager/test/update.test.ts delete mode 100644 app/modules/brackets-manager/types.ts delete mode 100644 app/modules/brackets-manager/update.ts delete mode 100644 app/modules/brackets-memory-db/README.md delete mode 100644 app/modules/brackets-memory-db/index.ts delete mode 100644 app/modules/brackets-model/index.ts delete mode 100644 app/modules/brackets-model/input.ts delete mode 100644 app/modules/brackets-model/other.ts delete mode 100644 app/modules/brackets-model/storage.ts delete mode 100644 app/modules/brackets-model/unions.ts create mode 100644 migrations/161-tournament-match-nullable-opponents.js diff --git a/app/components/match-page/MatchActionTab.module.css b/app/components/match-page/MatchActionTab.module.css index b8b018a77..870054a45 100644 --- a/app/components/match-page/MatchActionTab.module.css +++ b/app/components/match-page/MatchActionTab.module.css @@ -21,7 +21,7 @@ } } -.withPoints { +.withKo { grid-template-areas: "header header header" "actions actions actions" diff --git a/app/components/match-page/MatchActionTab.tsx b/app/components/match-page/MatchActionTab.tsx index 34536f839..fe6702496 100644 --- a/app/components/match-page/MatchActionTab.tsx +++ b/app/components/match-page/MatchActionTab.tsx @@ -39,8 +39,8 @@ interface MatchActionTabProps { ownTeamId: number | null; stageId: StageId; mode: ModeShort; - withPoints: boolean; - onSubmit?: (data: { winnerId: number; points?: [number, number] }) => void; + withKo: boolean; + onSubmit?: (data: { winnerId: number; ko?: boolean }) => void; isSubmitting?: boolean; setEnding?: SetEndingData; actionButtons?: React.ReactNode; @@ -52,7 +52,7 @@ export function MatchActionTab({ ownTeamId, stageId, mode, - withPoints, + withKo, onSubmit, isSubmitting, setEnding, @@ -73,14 +73,7 @@ export function MatchActionTab({ const submit = () => { if (winnerId === null) return; - const submitPoints: [number, number] | undefined = withPoints - ? isKo - ? winnerId === teams[0].id - ? [100, 0] - : [0, 100] - : [0, 0] - : undefined; - onSubmit?.({ winnerId, points: submitPoints }); + onSubmit?.({ winnerId, ko: withKo ? isKo : undefined }); }; return ( @@ -92,14 +85,14 @@ export function MatchActionTab({ mode={mode} winnerId={winnerId} teams={teams} - withPoints={withPoints} + withKo={withKo} isKo={isKo} isSubmitting={isSubmitting} onBack={() => setConfirming(false)} onConfirm={submit} /> ) : ( -
+
{t("q:match.action.selectWinner")}
{actionButtons ? (
{actionButtons}
@@ -156,7 +149,7 @@ export function MatchActionTab({ /> - {withPoints ? ( + {withKo ? (