Elo, Glicko-2, MMR rating systems, queue design, anti-smurfing, bot detection, and skill drift — complete matchmaking architecture for Ludo.
View AlgorithmsMatchmaking quality is determined by your rating system. Ludo has a unique challenge: four players compete simultaneously, so standard head-to-head ELO doesn't apply directly. Here are the three main approaches:
Classic 2-player Elo adapted to 4-player Ludo using placement-based scoring. Simple to implement, widely understood. Works well when combined with a K-factor that decreases as players gain rating.
Adds a Rating Deviation (RD) factor — the system expresses uncertainty about a player's true skill. New players have high RD and gain/lose more points per game, quickly converging to an accurate rating. Recommended for Ludo due to high new-player volume.
Hidden Match Making Rating separate from displayed rank. Used alongside visible ranks to provide finer-grained matching without exposing players to dramatic rank volatility. Blizzard's system popularized this approach.
# Multiplayer Elo: placement-based scoring for 4-player Ludo
# Placement scores: 1st=1.0, 2nd=0.66, 3rd=0.33, 4th=0.0
import math
K_FACTOR_DEFAULT = 32
K_FACTOR_VETERAN = 16
def expected_score(player_elo, opponent_elo):
"""Calculate expected score against one opponent."""
return 1.0 / (1.0 + 10 ** ((opponent_elo - player_elo) / 400))
def calculate_multiplayer_elo(players):
"""
players: list of dicts with {id, elo, placement}
Returns: dict mapping player id to new elo
"""
n = len(players)
placement_scores = {1: 1.0, 2: 0.66, 3: 0.33, 4: 0.0}
results = {}
for p in players:
# Expected score: average of expected scores against all others
expected = sum(
expected_score(p['elo'], o['elo'])
for o in players if o['id'] != p['id']
) / (n - 1)
# Actual score from placement
actual = placement_scores[p['placement']]
# K-factor: higher for new players (elo < 1600)
k = K_FACTOR_DEFAULT if p['elo'] < 1600 else K_FACTOR_VETERAN
# New Elo
new_elo = round(p['elo'] + k * (actual - expected))
results[p['id']] = new_elo
return results
# Example: 4 players after a game
players = [
{'id': 'red', 'elo': 1200, 'placement': 1},
{'id': 'blue', 'elo': 1150, 'placement': 2},
{'id': 'green', 'elo': 1300, 'placement': 3},
{'id': 'yellow', 'elo': 1100, 'placement': 4},
]
print(calculate_multiplayer_elo(players))
# {'red': 1219, 'blue': 1146, 'green': 1285, 'yellow': 1107}
// Redis Sorted Set: O(log N) insertion and range queries
// Score = composite of Elo + wait time bias
const MATCHMAKING_QUEUE = 'mm:queue:classic:ap-south-1';
async function joinQueue(playerId, elo, region) {
const key = `mm:queue:${region}`;
const compositeScore = elo + (Date.now() / 100000); // Elo + timestamp for FIFO within range
await redis.zadd(key, compositeScore, playerId);
await redis.hset(`player:${playerId}`, { elo, joinedAt: Date.now() });
}
async function findMatch(lookingElo, region, maxEloDiff = 150) {
const key = `mm:queue:${region}`;
const candidates = await redis.zRangeByScore(
key,
lookingElo - maxEloDiff,
lookingElo + maxEloDiff,
{ LIMIT: ['0', 4] }
);
return candidates; // Up to 4 players for a Ludo room
}
async function expandSearch(lookingElo, region) {
// Progressive relaxation: expand range as wait time increases
const waitMs = Date.now() - (await redis.hget(`player:${playerId}`, 'joinedAt'));
const baseDiff = 100;
const expansions = [100, 200, 400, 800, Infinity];
const tier = Math.min(Math.floor(waitMs / 30000), expansions.length - 1);
return await findMatch(lookingElo, region, expansions[tier]);
}
Smurfing (high-skill players creating new accounts to play against beginners) and bots undermine competitive integrity. Ludo's luck element makes smurfing harder than in chess, but both are still concerns for ranked play.
Phone verification: Link accounts to verified phone numbers — smurf detection is 90%+ effective with phone-linked accounts. Device fingerprinting: Detect multiple accounts from the same hardware. Account linkage: Accounts from the same device share a matchmaking pool — they can only be in games together, not against each other. Win-rate monitoring: Flag accounts with 80%+ win rate over 20+ games for manual review.
Response time analysis: Humans have 500ms-5s reaction time. Bots respond in <100ms consistently. Move entropy: Human move timing has variance; bots are too consistent. Account age correlation: Bots are typically created in batches — detect synchronized account creation patterns. Challenge verification: Require CAPTCHA after detecting bot-like behavior patterns.
Skill drift is the gradual change in a player's true skill rating over time. Players improve with practice, or sometimes decline after long breaks. Your rating system must adapt to drift without making ranked games feel unfair. Glicko-2 handles this naturally through the Rating Deviation — inactive players accumulate higher RD, meaning their rating becomes less trusted and adjusts more aggressively when they return.
Queue design best practices: Separate ranked and casual queues. Ranked uses strict Elo matching with progression rewards. Casual uses broader Elo ranges for faster matchmaking. Party matchmaking should use party-average Elo with a coordination bonus to the acceptable range. Implement dynamic queue sizing — smaller Elo windows during off-peak hours with wider windows during peak.
Link accounts to verified phone numbers, implement device fingerprinting to detect multiple accounts from the same hardware, and apply minimum game requirements (e.g., 10 games played) before entering ranked matchmaking. The LudoKing API anti-cheat module includes smurf detection by default.
Players are removed from the queue immediately upon WebSocket disconnect. If a match has already been formed but the player has not accepted within 10 seconds, the match is invalidated and remaining players are returned to the queue with their wait time preserved.
Yes. When the matchmaking queue cannot fill a match within the timeout threshold, the system fills remaining slots with AI bots. Bot difficulty levels (Easy, Medium, Hard) are matched to the human player Elo range. Bot AI for Ludo uses Monte Carlo Tree Search (MCTS) with position evaluation heuristics.
LudoKing API includes built-in Elo rating, skill-based matchmaking, region routing, and party support.
💬 Chat on WhatsApp