Files
flxn-app/src/features/tournaments/utils/bracket-generator.ts
2026-03-01 16:21:27 -06:00

104 lines
2.6 KiB
TypeScript

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;
}