From 1efa499461020ac175ba928a2fc15cbffab278bc Mon Sep 17 00:00:00 2001 From: "Kalle (Sendou)" <38327916+Sendouc@users.noreply.github.com> Date: Sun, 9 Jan 2022 23:33:54 +0200 Subject: [PATCH] Advance losers on report if BYE --- .../tournament/DuringMatchActions.tsx | 4 +- app/core/tournament/algorithms.ts | 33 ++++++++----- app/services/tournament.ts | 49 +++++++++++++++---- app/utils/hooks.ts | 8 +-- app/validators/user.ts | 24 +++++---- 5 files changed, 80 insertions(+), 38 deletions(-) diff --git a/app/components/tournament/DuringMatchActions.tsx b/app/components/tournament/DuringMatchActions.tsx index a26b93414..1ccd5873c 100644 --- a/app/components/tournament/DuringMatchActions.tsx +++ b/app/components/tournament/DuringMatchActions.tsx @@ -89,8 +89,8 @@ export function DuringMatchActions({ )}
- {roundInfos.map((info) => ( -
{info}
+ {roundInfos.map((info, i) => ( +
{info}
))}
diff --git a/app/core/tournament/algorithms.ts b/app/core/tournament/algorithms.ts index 4bdcf2115..7a95f88a9 100644 --- a/app/core/tournament/algorithms.ts +++ b/app/core/tournament/algorithms.ts @@ -56,11 +56,14 @@ export function eliminationBracket( !(upperTeam === "BYE" && lowerTeam === "BYE"), "Unexpected both teams in the first round are BYEs" ); - const firstRoundMatch = createMatch({ - upperTeam, - lowerTeam, - side: "winners", - }); + const firstRoundMatch = createMatch( + { + upperTeam, + lowerTeam, + side: "winners", + }, + upperTeam === "BYE" || lowerTeam === "BYE" + ); matchesWQueue.push(firstRoundMatch); matchesLQueue.push(firstRoundMatch); @@ -123,9 +126,13 @@ export function eliminationBracket( invariant(match1, "Unexpected no match1 in losers bracket"); invariant(match2, "Unexpected no match2 in losers bracket"); - const losersMatch = createMatch({ - side: "losers", - }); + const losersMatch = createMatch( + { + side: "losers", + }, + // If the first match already has a bye then this losers match will also be skipped + match1.lowerTeam === "BYE" || match1.upperTeam === "BYE" + ); match1[ match1.side === "winners" @@ -169,11 +176,11 @@ export function eliminationBracket( return bracket; - // TODO: also need to make match number 0 if loser bye - function createMatch(args: Omit): Match { - const number = [args.lowerTeam, args.upperTeam].includes("BYE") - ? 0 - : matchNumber++; + function createMatch( + args: Omit, + willBeSkipped?: boolean + ): Match { + const number = willBeSkipped ? 0 : matchNumber++; return { id: uuidv4(), number, diff --git a/app/services/tournament.ts b/app/services/tournament.ts index fb8264e38..20816b7e5 100644 --- a/app/services/tournament.ts +++ b/app/services/tournament.ts @@ -672,11 +672,14 @@ function resolveNewOrder({ newMatchId, }: { bracket: NonNullable; - oldMatch: NonNullable; + oldMatch: { id: string }; newMatchId: string; }): TeamOrder { const allMatches = bracket.rounds.flat().flatMap((round) => { - return round.matches; + return round.matches.map((match) => ({ + ...match, + isWinners: round.position > 0, + })); }); const newMatch = allMatches.find((m) => m.id === newMatchId); @@ -688,7 +691,19 @@ function resolveNewOrder({ newMatchId ) ) - .sort((a, b) => a.position - b.position); + .sort((a, b) => { + // first let's put teams from winners above teams from losers + // this might come to question if losers match is bye (so position 0) + // and would thus be above the winners bracket match if not for + // this condition + if (a.isWinners !== b.isWinners) { + return Number(b.isWinners) - Number(a.isWinners); + } + + // otherwise if match number is smaller it + // should mean the match is above the other match. + return a.position - b.position; + }); invariant( matchesThatLeadToNewMatch.length === 2, `matchesThatLeadToNewMatch length was unexpected: ${matchesThatLeadToNewMatch.length}` @@ -698,10 +713,6 @@ function resolveNewOrder({ "oldMatch not among matchesThatLeadToNewMatch" ); - // if match number is smaller it should mean the match is above - // the other match. thanks to sorting above 0 index should have - // the smaller match number. Winner's bracket match should - // always have the smaller number compared to loser's bracket match if (matchesThatLeadToNewMatch[0].id === oldMatch.id) return "UPPER"; return "LOWER"; } @@ -741,9 +752,29 @@ function newParticipantsForMatches({ }), teamId: loserTeam.teamId, }); - } - // if match number === 0 let's advance + const losersMatch = bracket.rounds + .flatMap((round) => round.matches) + .find(({ id }) => id === match.loserDestinationMatchId); + invariant(losersMatch, "losersMatch undefined"); + + // if the match will have a BYE then we need to generate one more participant + if (losersMatch.position === 0) { + invariant( + losersMatch.winnerDestinationMatchId, + "losersMatch.winnerDestinationMatchId undefined" + ); + result.push({ + matchId: losersMatch.winnerDestinationMatchId, + order: resolveNewOrder({ + bracket, + oldMatch: losersMatch, + newMatchId: losersMatch.winnerDestinationMatchId, + }), + teamId: loserTeam.teamId, + }); + } + } return result; } diff --git a/app/utils/hooks.ts b/app/utils/hooks.ts index 003234e4f..f9f3b2e60 100644 --- a/app/utils/hooks.ts +++ b/app/utils/hooks.ts @@ -6,15 +6,15 @@ import { LoggedInUserSchema } from "~/validators/user"; export const useUser = () => { const [root] = useMatches(); - const { user } = LoggedInUserSchema.parse(root.data); - return user; + const parsed = LoggedInUserSchema.parse(root.data); + return parsed?.user; }; export const useBaseURL = () => { const [root] = useMatches(); - const { baseURL } = z.object({ baseURL: z.string() }).parse(root.data); - return baseURL; + const parsed = z.object({ baseURL: z.string() }).nullish().parse(root.data); + return parsed?.baseURL; }; // TODO: fix causes memory leak diff --git a/app/validators/user.ts b/app/validators/user.ts index 4fef82888..aeeee1a35 100644 --- a/app/validators/user.ts +++ b/app/validators/user.ts @@ -1,12 +1,16 @@ import { z } from "zod"; -export type LoggedInUser = z.infer["user"]; -export const LoggedInUserSchema = z.object({ - user: z - .object({ - id: z.string(), - discordId: z.string(), - discordAvatar: z.string().nullable(), - }) - .nullish(), -}); +export type LoggedInUser = NonNullable< + z.infer +>["user"]; +export const LoggedInUserSchema = z + .object({ + user: z + .object({ + id: z.string(), + discordId: z.string(), + discordAvatar: z.string().nullable(), + }) + .nullish(), + }) + .nullish();