Структура данных
Для эффективного лидерборда нужна правильная структура данных,
которая позволит быстро сортировать, фильтровать и отображать
результаты игроков.
Класс Leaderboard
class Leaderboard { constructor() { this.players = [];
this.sortCriteria = 'score'; // По умолчанию сортируем по очкам
this.filters = { timeRange: 'all', // all, week, month, today
gameMode: 'all', // all, easy, medium, hard minGames: 0 // Минимальное
количество игр }; } // Добавить игрока addPlayer(playerData) { const
existingPlayer = this.players.find(p => p.id === playerData.id); if
(existingPlayer) { this.updatePlayerStats(existingPlayer, playerData);
} else { this.players.push({ id: playerData.id, name: playerData.name,
avatar: playerData.avatar || '👤', totalScore: playerData.score || 0,
gamesPlayed: 1, gamesWon: playerData.won ? 1 : 0, averageAttempts:
playerData.attempts || 0, bestTime: playerData.time || Infinity,
lastPlayed: new Date(), winRate: playerData.won ? 100 : 0, streak:
playerData.won ? 1 : 0, level: 1, achievements: [] }); }
this.sortPlayers(); } // Обновить статистику игрока
updatePlayerStats(player, gameData) { player.totalScore +=
gameData.score || 0; player.gamesPlayed++; if (gameData.won) {
player.gamesWon++; player.streak++; } else { player.streak = 0; } //
Обновляем средние показатели player.winRate =
Math.round((player.gamesWon / player.gamesPlayed) * 100); // Обновляем
лучшее время if (gameData.time && gameData.time < player.bestTime) {
player.bestTime = gameData.time; } // Обновляем среднее количество
попыток const totalAttempts = (player.averageAttempts *
(player.gamesPlayed - 1)) + (gameData.attempts || 0);
player.averageAttempts = Math.round(totalAttempts /
player.gamesPlayed); player.lastPlayed = new Date(); } // Сортировка
игроков sortPlayers() { this.players.sort((a, b) => { switch
(this.sortCriteria) { case 'score': return b.totalScore -
a.totalScore; case 'winRate': return b.winRate - a.winRate; case
'games': return b.gamesPlayed - a.gamesPlayed; case 'streak': return
b.streak - a.streak; case 'time': return a.bestTime - b.bestTime;
default: return b.totalScore - a.totalScore; } }); // Обновляем ранги
this.players.forEach((player, index) => { player.rank = index + 1; });
} // Получить топ игроков getTopPlayers(count = 10) { return
this.players.slice(0, count); } // Найти игрока по ID
getPlayerRank(playerId) { const player = this.players.find(p => p.id
=== playerId); return player ? player.rank : -1; } // Получить игроков
около определенного ранга getPlayersAround(targetRank, range = 2) {
const startIndex = Math.max(0, targetRank - range - 1); const endIndex
= Math.min(this.players.length, targetRank + range); return
this.players.slice(startIndex, endIndex); } }
Отображение лидерборда
function renderLeaderboard(players) { const container =
document.getElementById('leaderboard-list'); container.innerHTML = '';
players.forEach((player, index) => { const listItem =
document.createElement('li'); listItem.className = `leaderboard-item
${getTopClass(index + 1)}`; const rankDisplay = getRankDisplay(index +
1); listItem.innerHTML = ` ${rankDisplay}
${player.avatar} ${player.name}
${player.gamesPlayed} игр • ${player.winRate}% побед • серия:
${player.streak}
${player.totalScore.toLocaleString()}
`; // Анимация появления listItem.style.animationDelay = `${index *
0.1}s`; container.appendChild(listItem); }); } function
getRankDisplay(rank) { switch (rank) { case 1: return '
👑
'; case 2: return '
🥈
'; case 3: return '
🥉
'; default: return `
${rank}
`; } } function getTopClass(rank) { switch (rank) { case 1: return
'top-1'; case 2: return 'top-2'; case 3: return 'top-3'; default:
return ''; } }