import React, { useMemo } from 'react'; import { Link } from 'react-router-dom'; import { Trophy, Table, CheckCircle2, Medal, SearchX } from 'lucide-react'; import { useCTF } from './CTFContext'; import { calculateTeamTotalScore, calculateChallengeValue, getFirstBloodBonusFactor } from './services/scoring'; import { Button } from './UIComponents'; export const Scoreboard: React.FC = () => { const { state } = useCTF(); const rankings = useMemo(() => state.teams.filter(t => !t.isAdmin && !t.isDisabled).map(team => ({ ...team, score: calculateTeamTotalScore(team.id, state.challenges, state.solves), solveCount: state.solves.filter(s => s.teamId === team.id).length })).sort((a, b) => b.score - a.score), [state]); return (
| RANK | TEAM_IDENTIFIER | SOLVES | TOTAL_POINTS |
|---|---|---|---|
|
{idx + 1}
|
{team.name} | {team.solveCount} | {team.score} |
The matrix is currently empty. Be the first to solve a challenge!
| Team / Challenge | {sortedChallenges.map(c => { const diffColor = c.difficulty === 'Low' ? 'text-[#00ff00]' : c.difficulty === 'Medium' ? 'text-[#ffaa00]' : 'text-[#ff0000]'; return (
{/* Vertical text container using writing-mode so it stretches cell height automatically */}
{c.title}
{/* Solve count tag at the bottom of the header cell */}
{(c.solves || []).length} SOLVES
|
);
})}
|
|---|---|---|
| {team.name} ({team.score}) | {sortedChallenges.map(c => { const solve = state.solves.find(s => s.challengeId === c.id && s.teamId === team.id); if (!solve) { return (
•
|
);
}
// Find rank for the medal and bonus
const challengeSolves = state.solves
.filter(s => s.challengeId === c.id)
.sort((a, b) => a.timestamp - b.timestamp);
const rank = challengeSolves.findIndex(s => s.teamId === team.id);
// Calculate point gain for this specific solve
const baseValue = calculateChallengeValue(
c.initialPoints,
c.minimumPoints || 0,
c.decaySolves || 1,
(c.solves || []).length
);
const bonus = Math.floor(c.initialPoints * getFirstBloodBonusFactor(rank));
const totalPoints = baseValue + bonus;
return (
{rank === 0 ? (
{totalPoints}
|
);
})}