feat: add Ship-Class Silhouettes and Red Sunk Markers

This commit is contained in:
2026-08-30 01:24:16 +03:00
parent dfc6b023f7
commit 81d70a7862
7 changed files with 305 additions and 3 deletions
+53
View File
@@ -179,6 +179,58 @@
return ['', 'Вода'];
}
const fleetClasses = [
{ length: 4, count: 1, symbol: 'ship-4-battleship', label: 'Линкор', className: 'battleship' },
{ length: 3, count: 2, symbol: 'ship-3-cruiser', label: 'Крейсер', className: 'cruiser' },
{ length: 2, count: 3, symbol: 'ship-2-destroyer', label: 'Эсминец', className: 'destroyer' },
{ length: 1, count: 4, symbol: 'ship-1-cutter', label: 'Катер', className: 'cutter' },
];
function sunkShipLengths(board) {
const visited = new Set();
const lengths = [];
for (let start = 0; start < board.length; start += 1) {
if (board[start] !== '4' || visited.has(start)) continue;
let length = 0;
const pending = [start]; visited.add(start);
while (pending.length) {
const index = pending.pop(); length += 1;
const x = index % 10; const y = Math.floor(index / 10);
[[x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]].forEach(([nextX, nextY]) => {
const next = nextY * 10 + nextX;
if (nextX >= 0 && nextX < 10 && nextY >= 0 && nextY < 10 && board[next] === '4' && !visited.has(next)) {
visited.add(next); pending.push(next);
}
});
}
lengths.push(length);
}
return lengths;
}
function fleetStatusElement(index, title) {
const sunkLengths = sunkShipLengths(state.boards[index]);
const fleet = document.createElement('section'); fleet.className = 'fleet-status'; fleet.setAttribute('aria-label', `Флот: ${title}`);
fleetClasses.forEach(shipClass => {
const row = document.createElement('div'); row.className = 'fleet-row';
row.append(Object.assign(document.createElement('span'), { className: 'fleet-class', textContent: shipClass.label }));
const ships = document.createElement('div'); ships.className = 'fleet-ships';
for (let number = 0; number < shipClass.count; number += 1) {
const sunkAt = sunkLengths.indexOf(shipClass.length);
const sunk = sunkAt !== -1;
if (sunk) sunkLengths.splice(sunkAt, 1);
const ship = document.createElement('span');
ship.className = `fleet-ship ${shipClass.className}${sunk ? ' sunk' : ''}`;
ship.setAttribute('role', 'img'); ship.setAttribute('aria-label', `${shipClass.label}${sunk ? 'потоплен' : 'цел'}`);
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('aria-hidden', 'true'); svg.setAttribute('focusable', 'false');
const use = document.createElementNS('http://www.w3.org/2000/svg', 'use'); use.setAttribute('href', `/ship-sprite.svg#${shipClass.symbol}`);
svg.append(use); ship.append(svg); ships.append(ship);
}
row.append(ships); fleet.append(row);
});
return fleet;
}
function boardElement(index, title, targetable, result) {
const board = document.createElement('section');
board.className = 'board';
@@ -223,6 +275,7 @@
}
}
board.append(grid);
board.append(fleetStatusElement(index, title));
if (result) board.hidden = false;
return board;
}