290 lines
7.1 KiB
JavaScript
290 lines
7.1 KiB
JavaScript
|
.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,
|
||
|
};
|
||
|
}
|