Requirements & Scope
Functional Specifications
- Dynamic Rank Updates: Support score changes for any player with efficient
O(log n)updates, reflecting instantly on rank listings. - Efficient Top-K Queries: Maintain sorted indexes to retrieve the top
Kplayers inO(K)time without sorting the entire dataset. - Tie-Breaking Timestamp Logic: If multiple players hold identical scores, the player who reached the score first is prioritized with the higher rank.
- Nearby-Rank Window Queries: Retrieve a slice of the leaderboard centered on a target player (e.g., the target player plus the
Nranks above andNranks below).
Leaderboard Skip List Architecture
Entity relationships and hierarchical skip list indexing models:
Loading...
Design Patterns
- Composite Key Pattern: Bundles multiple sorting metrics (score, timestamp, playerId) into a single comparable key to enforce deterministic natural sorting.
- Index Multi-Leveling (Skip List): Utilizes probabilistic node heights to build skip links, bypassing long scans to find matching nodes in logarithmic time.
- Order-Statistic Tree Expansion: Augments index nodes with subtree sizes to make rank retrieval an
O(log n)operation instead ofO(n)scan.
Execution Workflows
Score Update & Re-indexing Workflow
- Lookup Player: Query
PlayerMapto verify if the player has an existing score record. - Index Invalidation: If present, extract the previous composite key and remove the matching node from the
SkipListIndex. - Generate New Key: Record the new score and capture the current timestamp. Instantiated as the new composite key.
- Sorted Insertion: Traverse down skip list layers. Insert new node at random height and repair pointer tracks in
O(log n). - Rank Resolution: Count the cumulative size of preceding nodes at higher intervals to resolve the updated rank instantly.
Clean Code Blueprint
Production reference implementations demonstrating composite score keys, sorted skip lists, dynamic rank lookup, and sliding rank windows:
// βββ JAVA BLUEPRINT ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
import java.util.*;
import java.util.concurrent.*;
class ConcurrentLeaderboard {
private final ConcurrentHashMap<String, PlayerScore> playerMap = new ConcurrentHashMap<>();
private final ConcurrentSkipListSet<ScoreKey> sortedScores = new ConcurrentSkipListSet<>();
public synchronized void updateScore(String playerId, double score) {
long now = System.currentTimeMillis();
PlayerScore existing = playerMap.get(playerId);
if (existing != null) {
// Remove old key from sorted set
sortedScores.remove(new ScoreKey(existing.getScore(), existing.getTimestamp(), playerId));
existing.setScore(score, now);
} else {
existing = new PlayerScore(playerId, score, now);
playerMap.put(playerId, existing);
}
// Insert new key to sorted set
sortedScores.add(new ScoreKey(score, now, playerId));
}
public synchronized int getRank(String playerId) {
PlayerScore player = playerMap.get(playerId);
if (player == null) return -1;
ScoreKey key = new ScoreKey(player.getScore(), player.getTimestamp(), playerId);
// HeadSet gives elements strictly greater than key.
// Size operation is O(log n) when using index traversal or order-statistic skip list structures.
return sortedScores.headSet(key).size() + 1;
}
public List<LeaderboardEntry> getTopK(int k) {
List<LeaderboardEntry> result = new ArrayList<>();
int count = 0;
int rank = 1;
for (ScoreKey key : sortedScores) {
if (count >= k) break;
result.add(new LeaderboardEntry(key.getPlayerId(), key.getScore(), rank++));
count++;
}
return result;
}
public List<LeaderboardEntry> getWindow(String playerId, int n) {
PlayerScore target = playerMap.get(playerId);
if (target == null) return Collections.emptyList();
List<ScoreKey> all = new ArrayList<>(sortedScores);
int targetIdx = -1;
for (int i = 0; i < all.size(); i++) {
if (all.get(i).getPlayerId().equals(playerId)) {
targetIdx = i;
break;
}
}
if (targetIdx == -1) return Collections.emptyList();
int start = Math.max(0, targetIdx - n);
int end = Math.min(all.size() - 1, targetIdx + n);
List<LeaderboardEntry> window = new ArrayList<>();
for (int i = start; i <= end; i++) {
ScoreKey key = all.get(i);
window.add(new LeaderboardEntry(key.getPlayerId(), key.getScore(), i + 1));
}
return window;
}
public static class PlayerScore {
private final String playerId;
private double score;
private long timestamp;
public PlayerScore(String playerId, double score, long timestamp) {
this.playerId = playerId;
this.score = score;
this.timestamp = timestamp;
}
public String getPlayerId() { return playerId; }
public double getScore() { return score; }
public long getTimestamp() { return timestamp; }
public void setScore(double score, long timestamp) {
this.score = score;
this.timestamp = timestamp;
}
}
public static class ScoreKey implements Comparable<ScoreKey> {
private final double score;
private final long timestamp;
private final String playerId;
public ScoreKey(double score, long timestamp, String playerId) {
this.score = score;
this.timestamp = timestamp;
this.playerId = playerId;
}
public double getScore() { return score; }
public long getTimestamp() { return timestamp; }
public String getPlayerId() { return playerId; }
@Override
public int compareTo(ScoreKey other) {
if (Double.compare(other.score, this.score) != 0) {
// Descending score order
return Double.compare(other.score, this.score);
}
if (this.timestamp != other.timestamp) {
// Ascending timestamp order (tie-breaker: earlier is better)
return Long.compare(this.timestamp, other.timestamp);
}
return this.playerId.compareTo(other.playerId);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ScoreKey scoreKey = (ScoreKey) o;
return Double.compare(scoreKey.score, score) == 0 &&
timestamp == scoreKey.timestamp &&
playerId.equals(scoreKey.playerId);
}
@Override
public int hashCode() {
return Objects.hash(score, timestamp, playerId);
}
}
public static class LeaderboardEntry {
private final String playerId;
private final double score;
private final int rank;
public LeaderboardEntry(String playerId, double score, int rank) {
this.playerId = playerId;
this.score = score;
this.rank = rank;
}
public String getPlayerId() { return playerId; }
public double getScore() { return score; }
public int getRank() { return rank; }
}
}
public class Main {
public static void main(String[] args) {
System.out.println("=== GAME LEADERBOARD DEMO (JAVA) ===");
ConcurrentLeaderboard leaderboard = new ConcurrentLeaderboard();
// Update scores
leaderboard.updateScore("ApexPredator", 9850);
leaderboard.updateScore("ShadowSlayer", 8600);
leaderboard.updateScore("CyberPunk", 8600); // Equal score, later timestamp (ranks lower)
leaderboard.updateScore("PixelKing", 7200);
System.out.println("Initial Top 3 Players:");
for (ConcurrentLeaderboard.LeaderboardEntry entry : leaderboard.getTopK(3)) {
System.out.println("Rank #" + entry.getRank() + ": " + entry.getPlayerId() + " - Score: " + entry.getScore());
}
System.out.println("\nRank of CyberPunk: " + leaderboard.getRank("CyberPunk"));
System.out.println("Rank of ShadowSlayer: " + leaderboard.getRank("ShadowSlayer"));
System.out.println("\nRetrieving window around ShadowSlayer (Radius 1):");
for (ConcurrentLeaderboard.LeaderboardEntry entry : leaderboard.getWindow("ShadowSlayer", 1)) {
System.out.println("Rank #" + entry.getRank() + ": " + entry.getPlayerId() + " - Score: " + entry.getScore());
}
System.out.println("\nUpdating CyberPunk score to 9900...");
leaderboard.updateScore("CyberPunk", 9900);
System.out.println("New Top 3 Players:");
for (ConcurrentLeaderboard.LeaderboardEntry entry : leaderboard.getTopK(3)) {
System.out.println("Rank #" + entry.getRank() + ": " + entry.getPlayerId() + " - Score: " + entry.getScore());
}
}
}
Interactive Simulator
β Logarithmic Game Leaderboard Simulator
Real-time score updates, composite tie-breakers, and visual multi-level index skip lists.
π Skip List Multi-Level Index Visualizer
Layer 3
Head
βββΊApexPredator
Score: 9850
Layer 2
Head
βββΊApexPredator
Score: 9850
PixelKing
Score: 7200
Layer 1
Head
βββΊApexPredator
Score: 9850
ShadowSlayer
Score: 8600
PixelKing
Score: 7200
SpeedRunner
Score: 6500
Layer 0
Head
βββΊApexPredator
Score: 9850
ShadowSlayer
Score: 8600
CyberPunk
Score: 8600
PixelKing
Score: 7200
SpeedRunner
Score: 6500
Valkyrie
Score: 5400
π‘ Higher layers act as expressways, skipping redundant nodes to execute score index lookups in O(log n) time.
π Dynamic Score Update Center
π Active Leaderboard Scoreboard
RankPlayer IDScoreTimestamp Tie-breaker
#1ApexPredator9,85010:13:20 AM
#2ShadowSlayer8,60010:30:00 AM
#3CyberPunk8,60010:46:40 AM
#4PixelKing7,20010:21:40 AM
#5SpeedRunner6,50011:03:20 AM
#6Valkyrie5,40010:38:20 AM
π Nearby-Rank Window Query
Center Player:
Radius (N):
#2ShadowSlayer 8,600
#3CyberPunk (Center Target)8,600
#4PixelKing 7,200
π― Instant O(log n) Rank Lookup
Index Traversal Console Logs
[13:50:00] Concurrent Skip List index initialized.
[13:50:02] Loaded 6 default player scores.
Review
Help Us Improve
How helpful was this walkthrough?
Click a star to rate. We actively use this feedback to refine and update our system design content.
Placeholder
Discussion
Share your thoughts, ask questions, or help others.
Loading comments...