.pragma library /** * Function to extract results from PartiipantFromApi * @param {ParticipantFromApi} fromApi * @return {Result[]} */ function _extractResults(fromApi) { const results = Array(7); const existingResults = Object.keys(fromApi).filter(key => key.match(/result[0-9]/), ); for (const result of existingResults) { let roundNumber = 0; const match = result.match(/result([0-9])/); if (match !== undefined && match !== null) { roundNumber = parseInt(match[1]); } if (roundNumber < 0) { continue; } results[roundNumber] = { rank: parseInt(fromApi[`result_rank${roundNumber}`] as string), result: fromApi[`result${roundNumber}`] as string, }; } return results; } /** * Function to clean up participants from the api * @param {ParticipantFromApi} fromApi * @return {Participant} */ function participantFromApiParticipant( fromApi, ) { const results = _extractResults(fromApi); return { id: fromApi.PerId, firstName: fromApi.firstname, lastName: fromApi.lastname, results: results, overallRank: fromApi.result_rank, startNumber: parseInt(fromApi.start_number), }; } /** * * @param {number} roundNumber * @param {RouteNames} routeNames * @return {string | undefined} */ function getRoundName( roundNumber, routeNames, ) { if (roundNumber < 2 || roundNumber > 6) return undefined; return routeNames[roundNumber]; } /** * * @param {string} name * @return {number} */ function getRoundRank(name) { const match = name.match(/1\/([842])/); if (match === undefined || match === null || match.length !== 2) { return 2; } return parseInt(match[1]); } /** * * @param {SpeedRoundPair} pair * @param {number} roundIndex */ function computeWinnerOfPair(pair, roundIndex) { if ( !(pair.laneA && pair.laneA.participant.results[roundIndex] && pair.laneA.participant.results[roundIndex].rank) || !(pair.laneB && pair.laneB.participant.results[roundIndex] && pair.laneB.participant.results[roundIndex].rank) ) return; pair.laneA.result = pair.laneA.participant.results[roundIndex]; pair.laneB.result = pair.laneB.participant.results[roundIndex]; if (pair.winner === undefined) { pair.winner = pair.laneA.participant.results[roundIndex].rank > pair.laneB.participant.results[roundIndex].rank ? 'B' : 'A'; } } /** * * @param {SpeedRoundPair} pair * @param {number} roundNumber * @return {Participant | undefined} */ function getWinnerOfPair( pair, roundNumber, ) { computeWinnerOfPair(pair, roundNumber); return { ['A']: pair.laneA ? pair.laneA.participant : undefined, ['B']: pair.laneB ? pair.laneB.participant : undefined, ['']: undefined, }[pair.winner ? pair.winner:'']; } /** * * @param {SpeedRoundPair} pair * @param {number} roundNumber * @return {Participant | undefined} */ function getLooserOfPair( pair, roundNumber, ) { computeWinnerOfPair(pair, roundNumber); return { ['A']: pair.laneB ? pair.laneB.participant : undefined, ['B']: pair.laneA ? pair.laneA.participant : undefined, ['']: undefined, }[pair.winner ? pair.winner:'']; } /** * * @param {number} roundIndex index of the new round * @param {string} roundName name of the new round * @param {SpeedRound} previousRound * @param {number} roundRank * @param {boolean} takeLooser * @return {SpeedRound} */ function computeRoundFromPreviousRound( roundIndex, roundName, previousRound, roundRank, takeLooser = false, ) { const getAdvancingParticipant = takeLooser ? getLooserOfPair : getWinnerOfPair; const nextRoundPairs = new Array(roundRank / 2).fill(0).map((_, i) => { const laneAParticipant = getAdvancingParticipant( previousRound.pairs[i * 2], previousRound.roundIndex, ); const laneBParticipant = getAdvancingParticipant( previousRound.pairs[i * 2 + 1], previousRound.roundIndex, ); return { laneA: laneAParticipant === undefined ? undefined : { participant: laneAParticipant, }, laneB: laneBParticipant === undefined ? undefined : { participant: laneBParticipant, }, }; }); return { pairs: nextRoundPairs, roundIndex: roundIndex, roundName: roundName, }; } /** * * @param {SpeedCompetitionCategoryResult} result The result to process * @return {SpeedFlowchartResult} */ function convertResultsToSpeedFlowchartResult( result, ) { const rounds = []; 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); // process first round const firstRoundName = getRoundName(roundIndices[0], result.route_names); const tmpRoundName = getRoundName(roundIndices[0], result.route_names); const firstRoundRank = getRoundRank( tmpRoundName ? tmpRoundName:'', ); const getOpponent = (ofRank) => { return convertedParticipants[firstRoundRank * 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], ][Math.floor(firstRoundRank / 4)]; const firstRoundPairs = ranksOfLaneAInOrder.map(rank => { return { laneA: { participant: convertedParticipants[rank - 1] }, laneB: { participant: getOpponent(rank - 1) }, }; }); const firstRound = { pairs: firstRoundPairs, roundIndex: roundIndices[0], roundName: firstRoundName, }; rounds.push(firstRound); // compute following rounds let roundIndex = roundIndices[1]; for (let roundRank = firstRoundRank; roundRank > 2; roundRank /= 2) { rounds.push( computeRoundFromPreviousRound( roundIndex, result.route_names[roundIndex] ? result.route_names[roundIndex]:'', rounds[rounds.length - 1], roundRank, ), ); roundIndex++; } // compute final and semi final const semifinalRoundIndex = roundIndex++; const semifinal = computeRoundFromPreviousRound( semifinalRoundIndex, result.route_names[semifinalRoundIndex] ? result.route_names[semifinalRoundIndex]:'', rounds[rounds.length - 1], 2, true, ); computeWinnerOfPair(semifinal.pairs[0], semifinalRoundIndex); rounds.push(semifinal); const finalRoundIndex = roundIndex; const final = computeRoundFromPreviousRound( finalRoundIndex, result.route_names[finalRoundIndex] ? result.route_names[finalRoundIndex]:'', rounds[rounds.length - 2], 2, ); computeWinnerOfPair(final.pairs[0], finalRoundIndex); rounds.push(final); return { rounds: rounds, }; }