move dashboard script block content into own files with new classes to update, render to have a better code overview
Build and Push Docker Container / build-and-push (push) Successful in 3m55s
Build and Push Docker Container / build-and-push (push) Successful in 3m55s
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
class SnakeTable {
|
||||
static buildSnakesRows(turn, replay) {
|
||||
console.log(turn);
|
||||
|
||||
const currentTurn = Number(turn && turn.turn !== undefined ? turn.turn : 0);
|
||||
const turns = replay && Array.isArray(replay.turns) ? replay.turns : [];
|
||||
const lastSeenById = new Map();
|
||||
const lastSeenTurnById = new Map();
|
||||
const aliveById = new Map();
|
||||
|
||||
for (const historyTurn of turns) {
|
||||
const historyTurnNumber = Number(historyTurn && historyTurn.turn !== undefined ? historyTurn.turn : 0);
|
||||
if (historyTurnNumber > currentTurn) continue;
|
||||
for (const snake of (historyTurn.snakes || [])) {
|
||||
if (!snake) continue;
|
||||
const snakeId = snake.snake_id || snake.id || `${Utils.safeString(snake.snake_name)}-${historyTurnNumber}`;
|
||||
lastSeenById.set(snakeId, snake);
|
||||
lastSeenTurnById.set(snakeId, historyTurnNumber);
|
||||
if (historyTurnNumber === currentTurn) {
|
||||
aliveById.set(snakeId, snake);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const snakes = [];
|
||||
for (const [snakeId, snake] of lastSeenById.entries()) {
|
||||
const aliveSnake = aliveById.get(snakeId);
|
||||
snakes.push({
|
||||
...(aliveSnake || snake),
|
||||
_snake_id: snakeId,
|
||||
_is_dead: !aliveSnake,
|
||||
_last_seen_turn: Number(lastSeenTurnById.get(snakeId) ?? currentTurn),
|
||||
});
|
||||
}
|
||||
|
||||
snakes.sort((a, b) => {
|
||||
const youDelta = Number(Boolean(b.is_you)) - Number(Boolean(a.is_you));
|
||||
if (youDelta !== 0) return youDelta;
|
||||
const deadDelta = Number(Boolean(a._is_dead)) - Number(Boolean(b._is_dead));
|
||||
if (deadDelta !== 0) return deadDelta;
|
||||
return Utils.safeString(a.snake_name).localeCompare(Utils.safeString(b.snake_name));
|
||||
});
|
||||
|
||||
const colorById = SnakeUtils.buildSnakeColorById(turn, replay);
|
||||
if (snakes.length === 0) {
|
||||
return "<tr><td colspan=\"7\">No snake data available</td></tr>";
|
||||
}
|
||||
|
||||
return snakes.map((snake, idx) => {
|
||||
const healthValue = Number(snake.health ?? 0);
|
||||
const healthClamped = snake._is_dead ? 0 : Math.max(0, Math.min(100, healthValue));
|
||||
const healthColor = healthClamped > 60 ? "#28a264" : (healthClamped > 30 ? "#d39a1c" : "#c34939");
|
||||
const healthText = snake._is_dead ? "dead" : Utils.safeString(snake.health);
|
||||
const healthCell = `<span class="health-wrap"><span class="health-fill" style="width:${healthClamped}%;background:${healthColor};"></span></span><span class="health-text">${healthText}</span>`;
|
||||
const snakeId = snake._snake_id || snake.snake_id || snake.id || `${Utils.safeString(snake.snake_name)}-${idx}`;
|
||||
const rowColor = SnakeUtils.resolveSnakeColor(snakeId, snake.is_you, colorById);
|
||||
const rowBg = SnakeUtils.snakeRowBackground(rowColor);
|
||||
const rowStyle = `style="--snake-row-color:${rowColor};--snake-row-bg:${rowBg};"`;
|
||||
const diedTurn = Number(snake._last_seen_turn ?? currentTurn) + 1;
|
||||
const moveText = snake._is_dead ? `dead @ ${diedTurn}` : Utils.safeString(snake.inferred_move);
|
||||
const deadClass = snake._is_dead ? " dead-row" : "";
|
||||
const causeLabel = SnakeTable._getCauseLabel(snake, turns, replay);
|
||||
const deadLabel = snake._is_dead ? ` (dead${causeLabel})` : "";
|
||||
return `
|
||||
<tr class="snake-row${deadClass}" data-snake-id="${snakeId}" ${rowStyle}>
|
||||
<td class="name-cell">${Utils.safeString(snake.snake_name)}${snake.is_you ? " (you)" : ""}${deadLabel}</td>
|
||||
<td class="num-cell">${Utils.safeString(snake.latency)}</td>
|
||||
<td class="num-cell">${moveText}</td>
|
||||
<td class="num-cell">${healthCell}</td>
|
||||
<td class="num-cell">${Utils.safeString(snake.length)}</td>
|
||||
<td class="num-cell">${snake.head ? `${snake.head.x},${snake.head.y}` : "-"}</td>
|
||||
<td class="num-cell">${Array.isArray(snake.body) && snake.body.length > 0 ? `${snake.body[snake.body.length - 1].x},${snake.body[snake.body.length - 1].y}` : "-"}</td>
|
||||
</tr>`;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
static _getCauseLabel(snake, turns, replay) {
|
||||
if (!snake._is_dead) return "";
|
||||
const lastTurn = turns.find((t) => t && t.turn === snake._last_seen_turn);
|
||||
if (!lastTurn) return "";
|
||||
const lastSelf = (lastTurn.snakes || []).find((s) => (s.snake_id || s.id) === snake._snake_id);
|
||||
if (!lastSelf) return "";
|
||||
// Starvation: health ≤ 1 at last seen turn
|
||||
if (Number(lastSelf.health) <= 1) return " · starved";
|
||||
// Project head to next position based on body direction
|
||||
const body = Array.isArray(lastSelf.body) ? lastSelf.body : [];
|
||||
const h = lastSelf.head || (body[0] || null);
|
||||
const neck = body[1] || null;
|
||||
const projX = h && neck ? h.x + (h.x - neck.x) : null;
|
||||
const projY = h && neck ? h.y + (h.y - neck.y) : null;
|
||||
const nextTurn = turns.find((t) => t && t.turn === snake._last_seen_turn + 1);
|
||||
if (projX !== null) {
|
||||
const game = replay && replay.game ? replay.game : {};
|
||||
const bw = Number(game.width || 0);
|
||||
const bh = Number(game.height || 0);
|
||||
// Wall collision: projected head out of bounds
|
||||
if (bw > 0 && bh > 0 && (projX < 0 || projX >= bw || projY < 0 || projY >= bh)) {
|
||||
return " · wall";
|
||||
}
|
||||
if (nextTurn) {
|
||||
// Head-to-head: another alive snake's head at projected position
|
||||
for (const other of (nextTurn.snakes || [])) {
|
||||
const otherId = other.snake_id || other.id;
|
||||
if (otherId === snake._snake_id) continue;
|
||||
if (other.head && other.head.x === projX && other.head.y === projY) {
|
||||
return ` · head-to-head ${Utils.safeString(other.snake_name)}`;
|
||||
}
|
||||
}
|
||||
// Body collision: projected head inside another snake's body
|
||||
for (const other of (nextTurn.snakes || [])) {
|
||||
const otherId = other.snake_id || other.id;
|
||||
if (otherId === snake._snake_id) continue;
|
||||
const otherBody = Array.isArray(other.body) ? other.body : [];
|
||||
const hitBody = otherBody.some((seg, i) => i > 0 && seg.x === projX && seg.y === projY);
|
||||
if (hitBody) return ` · hit ${Utils.safeString(other.snake_name)}`;
|
||||
}
|
||||
// Hazard: projected position is in hazard list
|
||||
const hazards = nextTurn.hazards || [];
|
||||
if (hazards.some((hz) => hz.x === projX && hz.y === projY)) return " · Hazard";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user