feat: WM2026 Tippspiel - Initial Backend + Frontend
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { PointsCalculation, POINTS } from '../types';
|
||||
|
||||
interface Score {
|
||||
home: number;
|
||||
away: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet die Punkte für einen Tipp.
|
||||
*
|
||||
* Punkteregeln:
|
||||
* - Exaktes Ergebnis: 3 Punkte
|
||||
* - Richtige Tendenz: 1 Punkt (Sieg/Unentschieden/Niederlage korrekt)
|
||||
* - Falsche Tendenz: 0 Punkte
|
||||
*
|
||||
* @param tip - Der abgegebene Tipp
|
||||
* @param actual - Das tatsächliche Ergebnis
|
||||
*/
|
||||
export const calculatePoints = (
|
||||
tip: Score,
|
||||
actual: Score
|
||||
): PointsCalculation => {
|
||||
// Exaktes Ergebnis
|
||||
if (tip.home === actual.home && tip.away === actual.away) {
|
||||
return { result: 'exact', points: POINTS.EXACT };
|
||||
}
|
||||
|
||||
// Tendenz prüfen (Sieg Heim / Unentschieden / Sieg Auswärts)
|
||||
const tipTendency = getTendency(tip.home, tip.away);
|
||||
const actualTendency = getTendency(actual.home, actual.away);
|
||||
|
||||
if (tipTendency === actualTendency) {
|
||||
return { result: 'tendency', points: POINTS.TENDENCY };
|
||||
}
|
||||
|
||||
return { result: 'wrong', points: POINTS.WRONG };
|
||||
};
|
||||
|
||||
type Tendency = 'home' | 'draw' | 'away';
|
||||
|
||||
const getTendency = (home: number, away: number): Tendency => {
|
||||
if (home > away) return 'home';
|
||||
if (home < away) return 'away';
|
||||
return 'draw';
|
||||
};
|
||||
|
||||
/**
|
||||
* Berechnet Punkte für die gesamte Tipp-History eines Users.
|
||||
* Nützlich für Statistiken.
|
||||
*/
|
||||
export const calculateUserStats = (
|
||||
tips: Array<{
|
||||
tipHome: number;
|
||||
tipAway: number;
|
||||
actualHome: number | null;
|
||||
actualAway: number | null;
|
||||
points: number | null;
|
||||
}>
|
||||
) => {
|
||||
const evaluated = tips.filter(
|
||||
(t) => t.actualHome !== null && t.actualAway !== null
|
||||
);
|
||||
|
||||
const exactCount = evaluated.filter((t) => t.points === POINTS.EXACT).length;
|
||||
const tendencyCount = evaluated.filter(
|
||||
(t) => t.points === POINTS.TENDENCY
|
||||
).length;
|
||||
const wrongCount = evaluated.filter((t) => t.points === POINTS.WRONG).length;
|
||||
const totalPoints = evaluated.reduce((sum, t) => sum + (t.points ?? 0), 0);
|
||||
|
||||
const accuracy =
|
||||
evaluated.length > 0
|
||||
? Math.round(
|
||||
((exactCount + tendencyCount) / evaluated.length) * 100
|
||||
)
|
||||
: 0;
|
||||
|
||||
return {
|
||||
totalPoints,
|
||||
exactCount,
|
||||
tendencyCount,
|
||||
wrongCount,
|
||||
accuracy,
|
||||
evaluatedCount: evaluated.length,
|
||||
pendingCount: tips.length - evaluated.length,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user