feat: complete human-vs-ESP32 gameplay and cumulative statistics
This commit is contained in:
+22
-6
@@ -16,6 +16,8 @@
|
||||
let info;
|
||||
let state;
|
||||
let selectedTarget;
|
||||
let cumulativeStatistics;
|
||||
let statisticsGameId;
|
||||
let activeBoard = 'own';
|
||||
let socket;
|
||||
let retryTimer;
|
||||
@@ -106,7 +108,7 @@
|
||||
function ownIndex() { return role === 'player2' ? 1 : 0; }
|
||||
function opponentIndex() { return ownIndex() ^ 1; }
|
||||
function canShoot() { return state?.phase === 'in_progress' && state.turn === role && isPlayer(); }
|
||||
function playerLabel(index) { return `Игрок ${index + 1}`; }
|
||||
function playerLabel(index) { return index === 1 && state?.mode === 'bot' ? 'ESP32' : `Игрок ${index + 1}`; }
|
||||
|
||||
function cellInfo(value) {
|
||||
if (value === '1') return ['ship', 'Корабль'];
|
||||
@@ -157,7 +159,7 @@
|
||||
];
|
||||
return [
|
||||
{ index: ownIndex(), title: 'Моё поле', key: 'own', targetable: false },
|
||||
{ index: opponentIndex(), title: 'Поле соперника', key: 'opponent', targetable: canShoot() }
|
||||
{ index: opponentIndex(), title: state.mode === 'bot' ? 'Поле ESP32' : 'Поле соперника', key: 'opponent', targetable: canShoot() }
|
||||
];
|
||||
}
|
||||
|
||||
@@ -198,7 +200,7 @@
|
||||
function renderGame() {
|
||||
if (!state) return;
|
||||
showScreen('game');
|
||||
ui.turn.textContent = state.turn === role ? 'Ваш ход. Выберите клетку соперника.' : `Ходит ${state.turn === 'player1' ? 'игрок 1' : 'игрок 2'}.`;
|
||||
ui.turn.textContent = state.turn === role ? 'Ваш ход. Выберите клетку соперника.' : `Ходит ${playerLabel(state.turn === 'player2' ? 1 : 0)}.`;
|
||||
ui.wins.textContent = `Победы: ${state.wins[0]} : ${state.wins[1]}`;
|
||||
renderBoards(ui.boards);
|
||||
const available = canShoot();
|
||||
@@ -211,10 +213,12 @@
|
||||
|
||||
function renderResult() {
|
||||
showScreen('result');
|
||||
if (statisticsGameId !== state.gameId) refreshStatistics();
|
||||
const waiting = state.phase === 'rematch_wait';
|
||||
const winner = state.winner === 0 || state.winner === 1 ? `Победил игрок ${state.winner + 1}. ` : '';
|
||||
const statistics = state.statistics.map((entry, index) => `Игрок ${index + 1}: выстрелы ${entry[0]}, попадания ${entry[1]}, промахи ${entry[2]}, потоплено ${entry[3]}.`).join(' ');
|
||||
ui.result.textContent = `${winner}${statistics} ${waiting ? 'Ожидается подтверждение повторной игры.' : 'Поля раскрыты. Можно подтвердить повторную игру.'}`;
|
||||
const winner = state.winner === 0 || state.winner === 1 ? `Победил ${playerLabel(state.winner)}. ` : '';
|
||||
const matchSummary = state.statistics.map((entry, index) => `${playerLabel(index)}: выстрелы ${entry[0]}, попадания ${entry[1]}, промахи ${entry[2]}, потоплено ${entry[3]}.`).join(' ');
|
||||
const cumulative = cumulativeStatistics?.cumulative?.map((entry, index) => `${playerLabel(index)} всего: игр ${entry[0]}, побед ${entry[1]}, поражений ${entry[2]}, выстрелов ${entry[4]}, попаданий ${entry[5]}, промахов ${entry[6]}, потоплено ${entry[3]}.`).join(' ') || '';
|
||||
ui.result.textContent = `${winner}${matchSummary} ${cumulative} ${waiting ? 'Ожидается подтверждение повторной игры.' : 'Поля раскрыты. Можно подтвердить повторную игру.'}`;
|
||||
ui.rematch.hidden = !isPlayer();
|
||||
ui.rematch.disabled = !isPlayer();
|
||||
ui.rematch.textContent = waiting ? 'Подтвердить повторно' : 'Сыграть ещё';
|
||||
@@ -242,6 +246,18 @@
|
||||
} catch (error) { notify(error.message, true); throw error; }
|
||||
}
|
||||
|
||||
async function refreshStatistics() {
|
||||
const requestedGameId = state?.gameId;
|
||||
try {
|
||||
const response = await request('/api/statistics', { cache: 'no-store', headers: token ? { 'X-Session-Token': token } : {} });
|
||||
if (state?.gameId === requestedGameId && response.gameId === requestedGameId && Array.isArray(response.cumulative)) {
|
||||
cumulativeStatistics = response;
|
||||
statisticsGameId = requestedGameId;
|
||||
renderResult();
|
||||
}
|
||||
} catch (_) { /* The state snapshot still renders the current-match statistics. */ }
|
||||
}
|
||||
|
||||
async function join(requestedRole) {
|
||||
const name = ui.name.value.trim();
|
||||
if (!name) { notify('Введите имя.', true); ui.name.focus(); return; }
|
||||
|
||||
Reference in New Issue
Block a user