Files
tribunal-app/src/lib/ranking.ts
T
Valentin ROBIN 9f54b459f3 Généralise Podium et computeRanks au-delà des points
computeRanksBy(items, comparator) remplace la logique de classement
"ex-aequo partagent le rang" en la rendant générique ; computeRanks
devient un simple appel avec le comparateur points desc, sans rien
changer pour l'appelant existant. Podium accepte maintenant un prop
renderValue (+ valueLabel optionnel) à la place du "points/gloires" en
dur, pour pouvoir afficher un classement par temps (Course du Char)
avec le même composant que le classement par points.
2026-07-27 18:53:54 +02:00

56 lines
1.7 KiB
TypeScript

export type Ranked<T> = T & { rank: number };
export type RankedProfile<T extends { points: number }> = Ranked<T>;
// Classement "compétition" générique : les ex-aequo (selon le comparateur)
// partagent le même rang, et le rang suivant saute en conséquence
// (1, 2, 2, 4 — pas 1, 2, 2, 3).
export function computeRanksBy<T>(items: T[], compare: (a: T, b: T) => number): Ranked<T>[] {
const sorted = [...items].sort(compare);
let lastItem: T | null = null;
let lastRank = 0;
return sorted.map((item, index) => {
if (lastItem === null || compare(item, lastItem) !== 0) {
lastRank = index + 1;
lastItem = item;
}
return { ...item, rank: lastRank };
});
}
export function computeRanks<T extends { points: number }>(profiles: T[]): RankedProfile<T>[] {
return computeRanksBy(profiles, (a, b) => b.points - a.points);
}
const ROMAN_DIGITS: [number, string][] = [
[10, "X"],
[9, "IX"],
[5, "V"],
[4, "IV"],
[1, "I"],
];
export function toRoman(value: number): string {
let remaining = value;
let result = "";
for (const [digit, symbol] of ROMAN_DIGITS) {
while (remaining >= digit) {
result += symbol;
remaining -= digit;
}
}
return result || "0";
}
export type Progress = { direction: "up" | "down" | "same" | "none"; amount: number };
export function computeProgress(currentRank: number, previousRank: number | null): Progress {
if (previousRank == null) {
return { direction: "none", amount: 0 };
}
const amount = previousRank - currentRank;
if (amount > 0) return { direction: "up", amount };
if (amount < 0) return { direction: "down", amount: -amount };
return { direction: "same", amount: 0 };
}