AI Implementation feature(911): Scoreboard: Ranking, Matrix, Event Log and Score Graph 1.01 (#54)
This commit was merged in pull request #54.
This commit is contained in:
@@ -94,6 +94,32 @@ export function stablePlayerColorIndex(playerId: string): number {
|
||||
return ((hash % n) + n) % n;
|
||||
}
|
||||
|
||||
export function assignUniquePlayerColors<T extends { playerId: string }>(
|
||||
items: readonly T[],
|
||||
getColorIndex: (t: T) => number,
|
||||
capacity: number = PLAYER_COLOR_PALETTE.length,
|
||||
): T[] {
|
||||
const used = new Set<number>();
|
||||
const cap = Math.max(1, Math.floor(capacity));
|
||||
const out: T[] = new Array(items.length);
|
||||
for (let i = 0; i < items.length; i += 1) {
|
||||
const item = items[i];
|
||||
const preferredRaw = getColorIndex(item);
|
||||
const preferred = ((preferredRaw % cap) + cap) % cap;
|
||||
let chosen = preferred;
|
||||
if (used.has(preferred)) {
|
||||
let probe = (preferred + 1) % cap;
|
||||
while (used.has(probe) && probe !== preferred) {
|
||||
probe = (probe + 1) % cap;
|
||||
}
|
||||
chosen = probe;
|
||||
}
|
||||
used.add(chosen);
|
||||
out[i] = { ...item, colorIndex: chosen } as T;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function applyRankingSort(rows: readonly RankingRow[]): RankingRow[] {
|
||||
const list = rows.map((r) => ({ ...r }));
|
||||
list.sort((a, b) => {
|
||||
@@ -136,7 +162,8 @@ export function parseSolveEventIntoRanking(
|
||||
colorIndex: stablePlayerColorIndex(payload.playerId),
|
||||
});
|
||||
}
|
||||
return applyRankingSort(out);
|
||||
const sorted = applyRankingSort(out);
|
||||
return assignUniquePlayerColors(sorted, (r) => r.colorIndex);
|
||||
}
|
||||
|
||||
export function dedupEventLogBySolveId(
|
||||
@@ -221,8 +248,9 @@ export function applySolveToGraph(
|
||||
const finalValue = (s: GraphSeries) => s.points[s.points.length - 1]?.value ?? 0;
|
||||
seriesList.sort((a, b) => finalValue(b) - finalValue(a));
|
||||
const top = seriesList.slice(0, 10);
|
||||
const uniqueTop = assignUniquePlayerColors(top, (s) => s.colorIndex);
|
||||
|
||||
return { startUtc: start, endUtc: end, serverNowUtc, state, series: top };
|
||||
return { startUtc: start, endUtc: end, serverNowUtc, state, series: uniqueTop };
|
||||
}
|
||||
|
||||
export function mutateMatrixFromSolve(
|
||||
@@ -251,7 +279,8 @@ export function mutateMatrixFromSolve(
|
||||
cellRow[payload.challengeId] = 'solved';
|
||||
}
|
||||
cells[payload.playerId] = cellRow;
|
||||
return { players, challenges: matrix.challenges, cells };
|
||||
const uniquePlayers = assignUniquePlayerColors(players, (p) => p.colorIndex);
|
||||
return { players: uniquePlayers, challenges: matrix.challenges, cells };
|
||||
}
|
||||
|
||||
export function formatSolveDateTime(utc: string | null | undefined): string {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ScoreboardTab,
|
||||
SolveLivePayload,
|
||||
applySolveToGraph,
|
||||
assignUniquePlayerColors,
|
||||
dedupEventLogBySolveId,
|
||||
mutateMatrixFromSolve,
|
||||
parseSolveEventIntoRanking,
|
||||
@@ -67,10 +68,13 @@ export class ScoreboardStore {
|
||||
const matrix = await this.toPromise<MatrixView>(() => this.api.getMatrix());
|
||||
const eventLog = await this.toPromise<EventLogRow[]>(() => this.api.getEventLog(50));
|
||||
const graph = await this.toPromise<GraphView>(() => this.api.getGraph());
|
||||
this._ranking.set(ranking);
|
||||
this._matrix.set(matrix);
|
||||
const uniqueRanking = assignUniquePlayerColors(ranking, (r) => r.colorIndex);
|
||||
const uniqueMatrixPlayers = assignUniquePlayerColors(matrix.players, (p) => p.colorIndex);
|
||||
const uniqueGraphSeries = assignUniquePlayerColors(graph.series, (s) => s.colorIndex);
|
||||
this._ranking.set(uniqueRanking);
|
||||
this._matrix.set({ ...matrix, players: uniqueMatrixPlayers });
|
||||
this._eventLog.set(eventLog);
|
||||
this._graph.set(graph);
|
||||
this._graph.set({ ...graph, series: uniqueGraphSeries });
|
||||
} catch (err: any) {
|
||||
this._error.set(err?.message ?? 'Failed to load scoreboard');
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user