Files
game-scoreboard/docs/SCORING.md
T
joshuaandClaude Opus 5.5 84dfe6a54b Phase 2: scoring engine with full branch coverage
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>
2026-10-03 13:33:21 +02:00

44 lines
1.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.