fix: ties on final match of bracket

This commit is contained in:
2026-04-15 17:02:41 -04:00
Unverified
parent 8c5af69144
commit 19aff04e60

View File

@@ -53,6 +53,14 @@ interface KnockoutRoundView {
projected?: boolean; projected?: boolean;
} }
interface BracketMatchEntry {
id: number;
player1: string;
player2: string;
currentTurnColor: 1 | 2 | null;
result: LiveGame["result"];
}
export default function SpectatePage() { export default function SpectatePage() {
const router = useRouter(); const router = useRouter();
const { const {
@@ -305,6 +313,10 @@ export default function SpectatePage() {
if (tournamentType === "KnockoutBracket" && !knockoutRawData) { if (tournamentType === "KnockoutBracket" && !knockoutRawData) {
setSelectedGame((prev) => (prev === msg.matchId ? null : prev)); setSelectedGame((prev) => (prev === msg.matchId ? null : prev));
} }
setTimeout(() => {
send(cmd.gameList());
send(cmd.playerList());
}, 750);
break; break;
case "GAME_TERMINATED": case "GAME_TERMINATED":
@@ -386,8 +398,14 @@ export default function SpectatePage() {
[players], [players],
); );
const knockoutBracket = useMemo( const knockoutBracket = useMemo(
() => buildKnockoutBracket(knockoutRounds, liveGames, tournamentWinner), () =>
[knockoutRounds, liveGames, tournamentWinner], buildKnockoutBracket(
knockoutRounds,
gameList,
liveGames,
tournamentWinner,
),
[gameList, knockoutRounds, liveGames, tournamentWinner],
); );
const seedingMatches = useMemo(() => { const seedingMatches = useMemo(() => {
const seen = new Set<number>(); const seen = new Set<number>();
@@ -813,14 +831,37 @@ function parseKnockoutRounds(raw: string) {
function buildKnockoutBracket( function buildKnockoutBracket(
rounds: string[][], rounds: string[][],
gameList: GameEntry[],
liveGames: Map<number, LiveGame>, liveGames: Map<number, LiveGame>,
tournamentWinner: string | null, tournamentWinner: string | null,
) { ) {
if (rounds.length === 0) return [] as KnockoutRoundView[]; if (rounds.length === 0) return [] as KnockoutRoundView[];
const liveGameEntries = Array.from(liveGames.values()).sort( const activeMatchIds = new Set(gameList.map((game) => game.id));
(a, b) => b.id - a.id, const bracketMatchEntries: BracketMatchEntry[] = [
); ...[...gameList]
.sort((a, b) => b.id - a.id)
.map((game) => {
const live = liveGames.get(game.id);
return {
id: game.id,
player1: game.player1,
player2: game.player2,
currentTurnColor: live?.currentTurnColor ?? null,
result: null,
} satisfies BracketMatchEntry;
}),
...Array.from(liveGames.values())
.sort((a, b) => b.id - a.id)
.filter((game) => !activeMatchIds.has(game.id))
.map((game) => ({
id: game.id,
player1: game.player1,
player2: game.player2,
currentTurnColor: game.currentTurnColor,
result: game.result,
})),
];
const displayRounds: Array<{ const displayRounds: Array<{
label: string; label: string;
players: Array<string | null>; players: Array<string | null>;
@@ -837,7 +878,7 @@ function buildKnockoutBracket(
if (index % 2 !== 0) return acc; if (index % 2 !== 0) return acc;
const player1 = source[index] ?? null; const player1 = source[index] ?? null;
const player2 = source[index + 1] ?? null; const player2 = source[index + 1] ?? null;
const liveMatch = findLiveMatch(liveGameEntries, player1, player2); const liveMatch = findBracketMatch(bracketMatchEntries, player1, player2);
acc.push( acc.push(
resolveLiveWinner(liveMatch, tournamentWinner, player1, player2), resolveLiveWinner(liveMatch, tournamentWinner, player1, player2),
); );
@@ -860,7 +901,7 @@ function buildKnockoutBracket(
([player1, player2], matchIndex) => { ([player1, player2], matchIndex) => {
const nextRound = displayRounds[roundIndex + 1]; const nextRound = displayRounds[roundIndex + 1];
const nextRoundPlayer = nextRound?.players[matchIndex] ?? null; const nextRoundPlayer = nextRound?.players[matchIndex] ?? null;
const liveMatch = findLiveMatch(liveGameEntries, player1, player2); const liveMatch = findBracketMatch(bracketMatchEntries, player1, player2);
const displayPlayer1 = liveMatch?.player1 ?? player1; const displayPlayer1 = liveMatch?.player1 ?? player1;
const displayPlayer2 = liveMatch?.player2 ?? player2; const displayPlayer2 = liveMatch?.player2 ?? player2;
const hasAdvancedPastRound = const hasAdvancedPastRound =
@@ -906,22 +947,21 @@ function pairPlayers(players: Array<string | null>) {
return pairs; return pairs;
} }
function findLiveMatch( function findBracketMatch(
liveGames: LiveGame[], matches: BracketMatchEntry[],
player1: string | null, player1: string | null,
player2: string | null, player2: string | null,
) { ) {
if (!player1 || !player2) return null; if (!player1 || !player2) return null;
return ( return (
liveGames.find((game) => matches.find((game) => samePlayers(game.player1, game.player2, player1, player2)) ??
samePlayers(game.player1, game.player2, player1, player2), null
) ?? null
); );
} }
function resolveLiveWinner( function resolveLiveWinner(
liveMatch: LiveGame | null, liveMatch: BracketMatchEntry | null,
tournamentWinner: string | null, tournamentWinner: string | null,
player1: string | null, player1: string | null,
player2: string | null, player2: string | null,