Files
game-scoreboard/docs/SCORING.md
T
joshuaandClaude Opus 5.5 c89a82295b Phase 0: scaffold Next.js, Tailwind tokens, Supabase clients, env validation
Next.js 16 App Router with strict TypeScript, Tailwind 4 theme tokens from
docs/UI.md, Vitest, and the three Supabase clients. Env vars are validated
with zod in next.config.ts so dev and build fail fast with a readable error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:27:11 +02:00

1.8 KiB
Raw Blame History

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.
  • 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).

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.