speed-flowchart-web/src/data/SpeedFlowchart.tsx

242 lines
5.8 KiB
TypeScript

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,
};
}