Files
hipctf/services/scoring.ts
m0rph3us1987 1c756af238 initial commit
2026-01-07 13:27:11 +01:00

59 lines
2.0 KiB
TypeScript

/**
* Dynamic Scoring Algorithm
* Logic:
* 1. Base Score starts at initialPoints.
* 2. As solves increase, the score for EVERYONE who solved it decreases.
* 3. We use a decay function: currentPoints = max(min_points, initial * decay_factor ^ (solves - 1))
* 4. Plus a "First Blood" bonus: 1st solver gets 10% extra, 2nd 5%, 3rd 2%.
*/
const MIN_POINTS_PERCENTAGE = 0.2; // Points won't drop below 20% of initial
const DECAY_CONSTANT = 0.92; // Aggressive decay per solve
export const calculateChallengeValue = (initialPoints: number, solveCount: number): number => {
if (solveCount === 0) return initialPoints;
const minPoints = Math.floor(initialPoints * MIN_POINTS_PERCENTAGE);
const decayedPoints = Math.floor(initialPoints * Math.pow(DECAY_CONSTANT, solveCount - 1));
return Math.max(minPoints, decayedPoints);
};
export const getFirstBloodBonus = (rank: number): number => {
if (rank === 0) return 0.10; // 1st
if (rank === 1) return 0.05; // 2nd
if (rank === 2) return 0.02; // 3rd
return 0;
};
export const calculateTeamTotalScore = (
teamId: string,
challenges: { id: string, initialPoints: number, solves: string[] }[],
solves: { teamId: string, challengeId: string, timestamp: number }[]
): number => {
let total = 0;
// Filter solves for this team
const teamSolves = solves.filter(s => s.teamId === teamId);
teamSolves.forEach(solve => {
const challenge = challenges.find(c => c.id === solve.challengeId);
if (!challenge) return;
// Current value of challenge (shared by all)
const baseValue = calculateChallengeValue(challenge.initialPoints, challenge.solves.length);
// Calculate rank for bonus
// Find all solves for this challenge sorted by time
const challengeSolves = solves
.filter(s => s.challengeId === solve.challengeId)
.sort((a, b) => a.timestamp - b.timestamp);
const rank = challengeSolves.findIndex(s => s.teamId === teamId);
const bonus = Math.floor(baseValue * getFirstBloodBonus(rank));
total += (baseValue + bonus);
});
return total;
};