Back to WCA Statistics

Metric

World record

Bundles every derivable metric from a round's 5 solves into one aggregator: official Single / Average plus BAo5 (best 3 of 5), WAo5 (worst 3 of 5), Mo5 (mean of all 5, no trim), BPA / WPA (best / worst possible average from the first 4), Median, Best / Worst Counting, Worst, Variance, and Best/Avg Ratio — 13 metrics in total.

One raw input (5 attempts per round) repeatedly squeezed to surface luck vs consistency. The UI groups the 13 metrics into Basic / Composite / Distribution dropdowns.

By the numbers

13
Metric subclasses
Each one a RoundMetric subclass
3
Groups
Basic / Composite / Distribution
2
Views
Ranking + WR history
EVENTS_WITH_AO5
Event scope
Official ao5 events only (excl. BLD / MBLD / FMC)

Data source

This class issues no SQL of its ownquery() returns `. Each child (wr_bao5 / wr_mo5 / wr_bpa / ...) hooks into the RoundMetric base class, which runs one batch SQL across all ao5 events and shares the attempt rows. Each child only overrides computeMetric(values)` to define how the 5 numbers collapse.

The aggregator wraps the 13 children's toJson() outputs as MetricPanel[] keyed by id, and adds metricGroups metadata so the UI can render group dropdowns.

Aggregator — child-by-child import + GC
ts
// 13 个子类按 id 注册,逐个 import + run
for (const def of METRIC_DEFS) {
  const mod = await def.module();
  const inst = new (Object.values(mod).find(v => typeof v === 'function'))();
  const sub = await inst.toJson();
  metricPanels.push({
    id: def.id, labelEn: def.label,
    labelZh: sub.titleZh, panels: sub.panels,
  });
  if (global.gc) global.gc();   // 子统计完释放,防 OOM
}

Algorithm / pipeline

1
Base class batch-loads attempts
RoundMetric shares one SQL across all batch-mode children — round attempts come back as a GROUP_CONCAT string. The cached rows feed all 11 batch children, dodging 11× redundant queries.
2
Each child defines `computeMetric(values)`
A child overrides one method: take a length-5 array (DNFs as -1), return a number or null. e.g. BAo5 = mean of the smallest 3; WAo5 = mean of the largest 3; BPA = best-3 of the first 4.
3
Rank: per-event top 10 + WR history
Base class emits two panels per child: Ranking (per-person best metric, top 10 by event) and History (chronological scan keeping strict improvements). WR history uses filterWrHistory(strict=true).
4
Aggregator wraps into MetricPanel[]
The 13 sub toJson() outputs concatenate in METRIC_DEFS order; each entry holds id / labelEn / labelZh / panels. The UI matches metricPanels[i].id against the dropdown.
5
Emit metricGroups → UI three-section dropdown
Basic = {single, average}, Composite = {bao5, wao5, mo5, bpa, wpa}, Distribution = {median, bestc, worstc, worst, variance, ratio}. UI renders a grouped dropdown rather than 13 flat items.

Key formulae

BAo5 / WAo5 / Mo5
BAo5 = (s₁+s₂+s₃)/3 WAo5 = (s₃+s₄+s₅)/3 Mo5 = (s₁+...+s₅)/5
with s₁ ≤ s₂ ≤ ... ≤ s₅ being the round's 5 solves sorted ascending. Mo5 / WAo5 need all 5 valid; BAo5 needs at least 3 valid.
BPA / WPA (pre-last-solve hypothetical)
BPA = mean(best 3 of {s₁,s₂,s₃,s₄}) WPA = mean(worst 3 of {s₁,s₂,s₃,s₄})
Uses only the first 4: BPA = best ao5 achievable if solve 5 lands perfectly; WPA = worst ao5 if solve 5 implodes. The "BPA-locked" tension you see on stream comes from here.

Caveats & edges

Related stats & links