Files
HIPCTF2/backend/src/modules/challenges/scoring.util.ts
T

55 lines
1.5 KiB
TypeScript

import { timingSafeEqual } from 'crypto';
export type Difficulty = 'LOW' | 'MEDIUM' | 'HIGH';
export const DIFFICULTY_RANK: Readonly<Record<Difficulty, number>> = {
LOW: 0,
MEDIUM: 1,
HIGH: 2,
};
export function compareDifficulty(a: Difficulty, b: Difficulty): number {
return DIFFICULTY_RANK[a] - DIFFICULTY_RANK[b];
}
export function computeLivePoints(
initial: number,
minimum: number,
decaySolves: number,
currentSolveCount: number,
): number {
if (!Number.isFinite(initial) || !Number.isFinite(minimum)) return minimum;
const i = Math.max(0, Math.floor(initial));
const m = Math.max(0, Math.floor(minimum));
const d = Math.max(0, Math.floor(decaySolves));
const c = Math.max(0, Math.floor(currentSolveCount));
if (i < m) return Math.max(0, m);
if (d <= 0) return i;
if (c >= d) return m;
const span = i - m;
const raw = i - span * (c / d);
return Math.max(m, Math.round(raw));
}
export function rankBonusForPosition(position: number): number {
if (!Number.isInteger(position) || position <= 0) return 0;
if (position === 1) return 15;
if (position === 2) return 10;
if (position === 3) return 5;
return 0;
}
export function safeEqualString(a: string, b: string): boolean {
const ab = Buffer.from(a, 'utf8');
const bb = Buffer.from(b, 'utf8');
if (ab.length !== bb.length) {
let diff = ab.length ^ bb.length;
const max = Math.max(ab.length, bb.length);
for (let i = 0; i < max; i += 1) {
diff |= (ab[i] ?? 0) ^ (bb[i] ?? 0);
}
return false;
}
return timingSafeEqual(ab, bb);
}