Pure functions in src/lib/scoring implementing docs/SCORING.md: cell values (override > judge average), weighted raw totals, raw and placement points with tie averaging, and competition-ranked standings. Coverage thresholds are enforced at 100% for the module. Also silences the hydration warning caused by browser extensions modifying <html>. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
44 lines
1.9 KiB
Markdown
44 lines
1.9 KiB
Markdown
# Scoring rules
|
||
|
||
All implemented in `src/lib/scoring/` as pure functions. Tests must cover every rule below.
|
||
|
||
## 1. Cell value (team × criterion)
|
||
- **Direct game:** the admin score row. Missing → not scored. Judge rows (e.g. left over from a mode switch) are ignored.
|
||
- **Judged game:**
|
||
- If an admin override row exists → use it.
|
||
- Else average of non-revoked judges' values for that cell. Missing judges are ignored (not zero).
|
||
- No judge values → not scored.
|
||
|
||
## 2. Team raw score for a game
|
||
`raw = Σ (cellValue × criterion.weight)` over scored criteria.
|
||
A team with zero scored cells is **unscored** for that game (not 0).
|
||
`raw` is rounded to 6dp before ranking so float noise from averaging can't break a real tie.
|
||
|
||
## 3. Game points
|
||
- **raw mode:** `points = round2(raw × multiplier)`.
|
||
- **placement mode:** rank scored teams by raw desc. Award `placement_points[rank-1] ?? 0`.
|
||
- Ties: tied teams share the **average** of the places they span, rounded to 2dp.
|
||
e.g. two teams tied for 1st with [10,7,5] → each gets 8.5; next team gets 5.
|
||
- Then × multiplier.
|
||
- Unscored teams get 0 and are not ranked.
|
||
- Only games with status `live` or `done` count toward totals. `upcoming` games contribute nothing
|
||
(prevents half-set-up games leaking).
|
||
|
||
## 4. Event total
|
||
`total = Σ game points + Σ adjustments.points`
|
||
|
||
## 5. Standings
|
||
Sort by total desc. Ties share rank (standard competition: 1,1,3). Tie display order: team `sort_order`.
|
||
|
||
## Test cases to include (minimum)
|
||
1. Karaoke: 5 criteria × 1–5, 3 judges, one judge missing a team → average uses 2 judges.
|
||
2. Override beats judge average.
|
||
3. Revoked judge excluded.
|
||
4. Weights applied.
|
||
5. Placement with a 2-way tie at 1st and 3 teams.
|
||
6. Placement with more teams than placement_points → extra teams get 0.
|
||
7. Multiplier on both modes.
|
||
8. Upcoming game ignored.
|
||
9. Adjustments (+ and −) added; adjustment tied to an upcoming game still counts (it's explicit).
|
||
10. Standings tie → shared rank.
|