export interface BracketMatch { lid: number; round: number; order: number | null; bye: boolean; home_seed?: number; away_seed?: number; home_from_lid: number | null; home_from_loser: boolean; away_from_lid: number | null; away_from_loser: boolean; reset: boolean; } export interface BracketTemplate { winners: BracketMatch[][]; losers: BracketMatch[][]; } export function generateSingleEliminationBracket(teamCount: number): BracketTemplate { if (teamCount < 2) { throw new Error("Need at least 2 teams for a bracket"); } const nextPowerOf2 = Math.pow(2, Math.ceil(Math.log2(teamCount))); const totalRounds = Math.log2(nextPowerOf2); const byesNeeded = nextPowerOf2 - teamCount; const firstRoundMatches = Math.floor(teamCount / 2); const winners: BracketMatch[][] = []; let currentLid = 0; let currentOrder = 1; for (let round = 0; round < totalRounds; round++) { const roundMatches: BracketMatch[] = []; const matchesInRound = Math.pow(2, totalRounds - round - 1); for (let matchIndex = 0; matchIndex < matchesInRound; matchIndex++) { const match: BracketMatch = { lid: currentLid++, round, order: currentOrder++, bye: false, home_from_lid: null, home_from_loser: false, away_from_lid: null, away_from_loser: false, reset: false, }; if (round === 0) { const homePosition = matchIndex * 2; const awayPosition = matchIndex * 2 + 1; if (homePosition < teamCount && awayPosition < teamCount) { match.home_seed = homePosition + 1; match.away_seed = awayPosition + 1; } else if (homePosition < teamCount) { match.home_seed = homePosition + 1; match.bye = true; } else { match.bye = true; } } else { const prevRound = winners[round - 1]; const homeFeedIndex = matchIndex * 2; const awayFeedIndex = matchIndex * 2 + 1; if (homeFeedIndex < prevRound.length) { match.home_from_lid = prevRound[homeFeedIndex].lid; } if (awayFeedIndex < prevRound.length) { match.away_from_lid = prevRound[awayFeedIndex].lid; } } roundMatches.push(match); } winners.push(roundMatches); } return { winners, losers: [], }; } export function generateGroupMismatchSeeding( numGroups: number, teamsPerGroup: number ): number[] { const seeding: number[] = []; for (let rank = 0; rank < teamsPerGroup; rank++) { for (let group = 0; group < numGroups; group++) { seeding.push(group * teamsPerGroup + rank); } } return seeding; }