diff --git a/docs/guides/scoreboard-page.md b/docs/guides/scoreboard-page.md index 692b58f..59cb275 100644 --- a/docs/guides/scoreboard-page.md +++ b/docs/guides/scoreboard-page.md @@ -1,9 +1,9 @@ --- type: guide title: Scoreboard Page -description: How a signed-in player navigates /scoreboard, uses the four tabs (Ranking, Matrix, Event Log, Score Graph), and watches live solve updates arriving over the authenticated SSE stream. +description: How a signed-in player navigates /scoreboard, uses the four tabs, distinguishes up to ten visible players by collision-resolved colors, and watches live solve updates. tags: [guide, scoreboard, sse, live, ranking, matrix, event-log, score-graph, tester] -timestamp: 2026-07-23T05:41:00Z +timestamp: 2026-07-23T06:01:38Z --- # Overview @@ -58,9 +58,14 @@ A table (`data-testid="scoreboard-ranking"`) with columns **Rank**, * Each row has `data-testid="ranking-row-"` and a `data-rank=""` attribute. -* Each player name is prefixed with a colored swatch (10-entry - `PLAYER_COLOR_PALETTE` chosen by `stablePlayerColorIndex`) so the - same player keeps the same color across all four tabs. +* Each player name is prefixed with a colored swatch from the 10-entry + `PLAYER_COLOR_PALETTE`. `stablePlayerColorIndex` supplies the preferred + color; `assignUniquePlayerColors` keeps that color when available and + linearly selects the next free palette slot after a collision. Ranking, + Matrix, and Score Graph apply this assignment in their current display + order after initial REST loading and after live updates, so up to ten + simultaneously displayed players remain visually distinct within each + projection. * Ranking uses competition ranks: equal-point players share the same `rank` (1, 2, 2, 4, …). Zero-solve players are still listed at the bottom in user-id order. @@ -121,7 +126,9 @@ points for the **top 10 players** over the configured event window (`startUtc` → `endUtc`): * Above the chart, a legend (`data-testid="score-graph-legend-"`) - lists each player with their colored swatch. + lists each player with their colored swatch. The top ten visible series + receive distinct palette colors, including when two player IDs hash to + the same preferred color. * Each player has a `polyline` element (`data-testid="score-graph-line-"`) using the same color index as the Ranking and Matrix tabs. @@ -152,9 +159,11 @@ The same `solve` frame that lights up the `awardedPoints`, `solvedCount` increments by 1, and the list is re-sorted (with competition ranks applied). If the player is new, they are appended with `points = awardedPoints`, `solvedCount = 1`. + Colors are reassigned in sorted ranking order to avoid palette collisions. * **Matrix**: the cell at `[, ]` becomes `1`, `2`, or `3` if the new `position` is in 1–3, otherwise `'solved'`. - New players are appended to the matrix row list. + New players are appended to the matrix row list, then visible row colors + are collision-resolved in matrix order. * **Event Log**: a new `EventLogRow` is prepended to the list (deduplicated by `solveId` so reconnects don't duplicate) and the list is capped at 200. The SSE solve frame carries @@ -166,7 +175,8 @@ The same `solve` frame that lights up the * **Score Graph**: the matching player's series receives a new `{tUtc: awardedAtUtc, value: prevValue + awardedPoints}` point, the series list is re-sorted by final value DESC and trimmed to the - top 10. + top 10. Those ten series are then assigned unique palette slots in + graph order. On `ngOnDestroy`, `store.stop()` aborts the SSE transport and clears the reconnect timer so no callbacks fire after the page leaves the @@ -256,6 +266,21 @@ DOM. tabs and matches the same player's swatch in the challenges board modal solvers list. +## Verifying collision-resolved player colors + +1. Seed or create up to ten visible players, including player IDs whose + preferred palette hashes collide. +2. Open `/scoreboard` and inspect **Ranking**. Every one of the first ten + displayed players has a distinct swatch; players without a collision keep + their preferred hashed color. +3. Open **Matrix**. Up to ten visible player rows likewise use distinct + swatches in matrix row order. +4. Open **Score Graph**. Its ten legend swatches and matching polylines use + ten distinct palette colors. +5. Trigger a solve. Ranking and graph order may change, but each projection + resolves its displayed colors again and remains collision-free for up to + ten players. + # See also - [Challenges Endpoints](/api/challenges.md) — `/api/v1/scoreboard/*` request/response shapes. diff --git a/docs/index.md b/docs/index.md index 1866336..a5bd0db 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,7 +11,7 @@ scoreboard, an event window with a public countdown, theming, and admin controls. The docs below are organized by purpose so agents can pull just the slice -they need. Last regenerated 2026-07-23T05:41:00Z. +they need. Last regenerated 2026-07-23T06:01:38Z. # Architecture @@ -119,8 +119,8 @@ they need. Last regenerated 2026-07-23T05:41:00Z. broadcast to clients (public unauthenticated stream + authenticated `/api/v1/events`). * [Scoreboard Page](/guides/scoreboard-page.md) - How a signed-in player - navigates `/scoreboard`, uses the 4 tabs (Ranking, Matrix, Event Log, - Score Graph), and watches live solve updates. + navigates `/scoreboard`, uses the 4 tabs, verifies collision-resolved + colors for up to ten visible players, and watches live solve updates. * [Notifications](/guides/notifications.md) - The root notification store + global HTTP error interceptor that translate backend error codes and statuses into friendly user-facing messages.