FaustCTF 2024 Scoring Formula
Scoring formula for FaustCTF 2024 organized by FAUST.
Summary
The total score of each team is calculated from offense
, defense
and sla
components of every team for each of their services and rounds played.
The checker returns one of three results for each service:
up
, recovering
and down
. A service is considered recovering
if SLA
checks both suceeded and failed for one round (each) in the
recovery period.
The following python pseudo-code captures the team score calculation:
def score(rnd, team):
attack = 0
for r in range(rnd+1):
for flag in flags_captured_by(r, team):
if flag_owner(f) != "NOP":
attack += 1 + 1 / len(total_captures_of(flag))
defense = 0
for r in range(rnd+1):
for flag in flags_deployed_in(r, team):
defense -= len(total_captures_of(flag)) ** 0.75
teams_up = [t for t in teams if checker_status(rnd, t) == 'up']
teams_rec = [t for t in teams if checker_status(rnd, t) == 'recovering']
sla = (len(teams_up) + 0.5 * len(teams_rec)) * sqrt(len(teams))
return attack + defense + sla
def scores():
return {rnd: {team: score(rnd, team) for team in teams} for rnd in rounds}
Review
- Scoring is easy to understand and reason about
- Since
total_captures_of(flag)
is independent of round time, it may change after a previous attack score has been calculated, meaning efficient recalculation of team score each round is somewhat non-trivial. Additionally, ccore recalculation may cause total attack points to decrease from one round to the next, which tends to confuse new players. - In the worst-case, when all teams capture flags of a service, the defense points \((N_\texttt{Teams} - 1)^{0.75}\) lost outweigh the SLA points \((N_\texttt{Teams})^{0.5}\) gained for that service, thus violating Tenet 4