import { RouteNames, SpeedCompetitionCategoryResult, } from '../models/Competition'; import { Participant, participantFromApiParticipant, } from '../models/Participant'; export interface SpeedRoundParticipant { id: string; firstName: string; hasWon: boolean; } export interface SpeedRoundPair { laneA?: Participant; laneB?: Participant; winner?: 'A' | 'B'; } export interface SpeedRound { pairs: SpeedRoundPair[]; roundIndex: number; roundName?: string; } export interface SpeedFlowchartResult { rounds: SpeedRound[]; } /** * * @param {number} roundNumber * @param {RouteNames} routeNames * @return {string | undefined} */ function getRoundName( roundNumber: number, routeNames: RouteNames, ): string | undefined { if (roundNumber < 2 || roundNumber > 6) return undefined; return routeNames[roundNumber]; } /** * * @param {string} name * @return {number} */ function getRoundRank(name: string): number { const match = name.match(/1\/([842])/); console.log(match); if (match === undefined || match === null || match.length !== 2) { return 2; } return parseInt(match[1]); } /** * * @param {SpeedRoundPair} pair * @param {number} roundIndex */ function computeWinnerOfPair(pair: SpeedRoundPair, roundIndex: number) { if ( pair.laneA?.results[roundIndex]?.rank === undefined || pair.laneB?.results[roundIndex]?.rank === undefined ) return; if (pair.winner === undefined) { pair.winner = pair.laneA.results[roundIndex].rank > pair.laneB.results[roundIndex].rank ? 'B' : 'A'; } } /** * * @param {SpeedRoundPair} pair * @param {number} roundNumber * @return {Participant | undefined} */ function getWinnerOfPair( pair: SpeedRoundPair, roundNumber: number, ): Participant | undefined { computeWinnerOfPair(pair, roundNumber); return pair.winner === 'A' ? pair.laneA : pair.laneB; } /** * * @param {SpeedRoundPair} pair * @param {number} roundNumber * @return {Participant | undefined} */ function getLooserOfPair( pair: SpeedRoundPair, roundNumber: number, ): Participant | undefined { computeWinnerOfPair(pair, roundNumber); return pair.winner === 'A' ? pair.laneB : pair.laneA; } /** * * @param {number} roundIndex index of the new round * @param {string} roundName name of the new round * @param {SpeedRound} previousRound * @param {boolean} takeLooser * @return {SpeedRound} */ function computeRoundFromPreviousRound( roundIndex: number, roundName: string, previousRound: SpeedRound, takeLooser = false, ): SpeedRound { const getAdvancingParticipant = takeLooser ? getLooserOfPair : getWinnerOfPair; const nextRoundPairs = new Array(previousRound.pairs.length / 2) .fill(0) .map((_, i) => { return { laneA: getAdvancingParticipant( previousRound.pairs[i * 2], previousRound.roundIndex, ), laneB: getAdvancingParticipant( previousRound.pairs[i * 2 + 1], previousRound.roundIndex, ), }; }); return { pairs: nextRoundPairs, roundIndex: roundIndex, roundName: roundName, }; } /** * * @param {SpeedCompetitionCategoryResult} result The result to process * @return {SpeedFlowchartResult} */ export function convertResultsToSpeedFlowchartResult( result: SpeedCompetitionCategoryResult, ): SpeedFlowchartResult { const rounds: SpeedRound[] = []; const convertedParticipants = result.participants .map(fromApi => participantFromApiParticipant(fromApi)) // sort by qualification result .sort((a, b) => a.results[0].rank - b.results[0].rank); const roundIndices = Object.keys(result.route_names) .map(number => parseInt(number)) .filter(number => number > 0); console.log(`Have final rounds:`, roundIndices); // process first round const firstRoundName = getRoundName(roundIndices[0], result.route_names); const firstRoundNumber = getRoundRank( getRoundName(roundIndices[0], result.route_names) ?? '', ); const getOpponent = (ofRank: number): Participant => { return convertedParticipants[firstRoundNumber * 2 - 1 - ofRank]; }; // Should be: // 0, 1, 2, 3, 4, 5, 6, 7 // - 1,16, 8, 9, 4,13, 5,12, 2,15, 7,10, 3,14, 6,11 // - 1, 8, 4, 5, 2, 7, 3, 6 for firstRoundNumber=8 // - 1, 4, 2, 3 for firstRoundNumber=4 // - 1, 2 for firstRoundNumber=2 // TODO: come up with a proper alogorithm maybe const ranksOfLaneAInOrder = [ [1, 2], [1, 4, 2, 3], [1, 8, 4, 5, 2, 7, 3, 6], ][firstRoundNumber / 4]; console.log(ranksOfLaneAInOrder); const firstRoundPairs = ranksOfLaneAInOrder.map(rank => { return { laneA: convertedParticipants[rank - 1], laneB: getOpponent(rank - 1), }; }); const firstRound: SpeedRound = { pairs: firstRoundPairs, roundIndex: roundIndices[0], roundName: firstRoundName, }; rounds.push(firstRound); // compute following rounds for (let i = 1; i < roundIndices.length - 2; i++) { rounds.push( computeRoundFromPreviousRound( roundIndices[i], result.route_names[roundIndices[i]] ?? '', rounds[i - 1], ), ); } // compute final and semi final const semifinalRoundIndex = roundIndices[roundIndices.length - 2]; const semifinal = computeRoundFromPreviousRound( semifinalRoundIndex, result.route_names[semifinalRoundIndex] ?? '', rounds[rounds.length - 1], true, ); computeWinnerOfPair(semifinal.pairs[0], semifinalRoundIndex); rounds.push(semifinal); const finalRoundIndex = roundIndices[roundIndices.length - 1]; const final = computeRoundFromPreviousRound( finalRoundIndex, result.route_names[finalRoundIndex] ?? '', rounds[rounds.length - 2], ); computeWinnerOfPair(final.pairs[0], finalRoundIndex); rounds.push(final); return { rounds: rounds, }; }