diff --git a/app/core/tournament/algorithms.test.ts b/app/core/tournament/algorithms.test.ts index b6e8e5b33..5bf40ac62 100644 --- a/app/core/tournament/algorithms.test.ts +++ b/app/core/tournament/algorithms.test.ts @@ -113,6 +113,7 @@ BracketPaths("Following losers", () => { let latest: Match = bracket16.winners[0]; let rounds = 0; + let countWinnerDestNoLoserDest = 0; let roundIds = new Set(); while (latest) { rounds++; @@ -120,6 +121,9 @@ BracketPaths("Following losers", () => { if (!latest.winnerDestinationMatch && !latest.loserDestinationMatch) { break; } + if (latest.winnerDestinationMatch && !latest.loserDestinationMatch) { + countWinnerDestNoLoserDest++; + } latest = (latest.loserDestinationMatch ?? latest.winnerDestinationMatch)!; } @@ -127,6 +131,7 @@ BracketPaths("Following losers", () => { assert.equal(roundIds.size, rounds); assert.equal(rounds, count.losers + 3); // 3 rounds of winners: first round, grand finals & bracket reset + assert.equal(countWinnerDestNoLoserDest, count.losers); }); FillParticipantsWithNull("17", () => { diff --git a/app/core/tournament/algorithms.ts b/app/core/tournament/algorithms.ts index c5ef9f356..ae9d76132 100644 --- a/app/core/tournament/algorithms.ts +++ b/app/core/tournament/algorithms.ts @@ -74,8 +74,6 @@ export function eliminationBracket( invariant(match2, "Unexpected no match2 in winners bracket"); const winnersBracketMatch = createMatch({ - match1, - match2, side: "winners", }); @@ -125,8 +123,6 @@ export function eliminationBracket( invariant(match2, "Unexpected no match2 in losers bracket"); const losersMatch = createMatch({ - match1, - match2, side: "losers", }); @@ -153,8 +149,6 @@ export function eliminationBracket( // Add final match and bracket reset const grandFinals = createMatch({ - match1, - match2, side: "winners", }); @@ -164,8 +158,6 @@ export function eliminationBracket( bracket.winners.push(grandFinals); const bracketReset = createMatch({ - match1: grandFinals, - match2: grandFinals, side: "winners", }); @@ -236,10 +228,6 @@ export interface Match { upperTeam?: TeamIdentifier; lowerTeam?: TeamIdentifier; winner?: TeamIdentifier; - /** Match that leads to this match */ - match1?: Match; - /** Match that leads to this match */ - match2?: Match; loserDestinationMatch?: Match; winnerDestinationMatch?: Match; side: EliminationBracketSide; diff --git a/app/core/tournament/bracket.ts b/app/core/tournament/bracket.ts index 70363bf16..294e5b89d 100644 --- a/app/core/tournament/bracket.ts +++ b/app/core/tournament/bracket.ts @@ -1,7 +1,10 @@ -import type { BracketType, Stage } from ".prisma/client"; +import type { BracketType, Stage, TeamOrder } from ".prisma/client"; import invariant from "tiny-invariant"; import { generateMapListForRounds } from "./mapList"; -import type { Bracket } from "./algorithms"; +import { Bracket, eliminationBracket } from "./algorithms"; +import type { UseTournamentRoundsState } from "../../hooks/useTournamentRounds/types"; +import { FindTournamentByNameForUrlI } from "../../services/tournament"; +import { TOURNAMENT_TEAM_ROSTER_MIN_SIZE } from "../../constants"; export function participantCountToRoundsInfo({ bracket, @@ -120,12 +123,12 @@ export function countRounds(bracket: Bracket): EliminationBracket { const losersMatchIds = new Set(bracket.losers.map((match) => match.id)); let losers = 0; - let losersMatch = bracket.losers[bracket.losers.length - 1]; + let losersMatch = bracket.losers[0]; while (true) { losers++; - const match1 = losersMatch?.match1; - const match2 = losersMatch?.match2; + const match1 = losersMatch?.winnerDestinationMatch; + const match2 = losersMatch?.winnerDestinationMatch; if (match1 && losersMatchIds.has(match1.id)) { losersMatch = match1; continue; @@ -168,6 +171,63 @@ export function resolveTournamentFormatString( : "Single Elimination"; } +export function countParticipants(teams: FindTournamentByNameForUrlI["teams"]) { + return teams.reduce((acc, team) => { + if (!team.checkedInTime) return acc; + invariant( + team.members.length < TOURNAMENT_TEAM_ROSTER_MIN_SIZE, + `Team with id ${team.id} has too small roster: ${team.members.length}` + ); + + return acc + 1; + }, 0); +} + +interface TournamentRoundForDB { + position: number; + stages: { + position: number; + stageId: number; + }[]; + matches: { + id: string; + winnerDestinationMatchId?: string; + loserDestinationMatchId?: string; + participants: { + teamId: string; + order: TeamOrder; + }; + }[]; +} +export function tournamentRoundsForDB({ + mapList, + participantCount, + bracketType, +}: { + mapList: UseTournamentRoundsState["bracket"]; + participantCount: number; + bracketType: BracketType; +}): TournamentRoundForDB[] { + const rounds = eliminationBracket(participantCount, bracketType); + const result: TournamentRoundForDB[] = []; + + for (const [i, side] of [rounds.winners, rounds.losers].entries()) { + const isWinners = i === 0; + for (const [roundI, round] of side.entries()) { + const position = isWinners ? roundI + 1 : -(roundI + 1); + + const stagesRaw = mapList[isWinners ? "winners" : "losers"][roundI]; + invariant(stagesRaw, "stagesRaw is undefined"); + const stages = stagesRaw.mapList.map((stage, i) => ({ + position: i + 1, + stageId: stage.id, + })); + } + } + + return result; +} + export type EliminationBracket = { winners: T; losers: T;