home documents terms contact shop (coming soon!)

ELO Configuration

Configure the rating system that determines player rankings.

The heart of competitive ranking. Configure how player ratings change after each battle.

Rating System Selection

# elo.yaml
ratingSystem: POKEMON_SHOWDOWN  # or GLICKO2
startingElo: 1000
floorElo: 1000
SettingDefaultDescription
ratingSystemPOKEMON_SHOWDOWNRating algorithm to use
startingElo1000Rating for new players
floorElo1000Minimum possible rating

Pokemon Showdown Mode (Default)

Uses the classic Elo formula with K-Factor adjustments. Same system as Pokemon Showdown and chess.

# elo.yaml
pokemonShowdown:
  newPlayerGames: 30
  newPlayerKFactor: 50
  kFactorBands:
    - maxElo: 1500
      kFactor: 48
    - maxElo: 1700
      kFactor: 36
    - maxElo: 2000
      kFactor: 24
    - maxElo: 2500
      kFactor: 18
    - maxElo: 999999
      kFactor: 12
  streakBonus:
    enabled: false
    threshold3Wins: 3
    threshold5Wins: 5
K-Factor Bands Explained

K-Factor determines how much rating changes per match. Higher K = bigger swings.

Player TypeK-FactorReason
New players (first 30 games)50Quickly find true skill level
Low rating (< 1500)48Easier to climb out
Mid rating (1500-1700)36Balanced progression
Mid-high (1700-2000)24More stable
High (2000-2500)18Stable rankings
Top (2500+)12Very stable, small changes

Example:

  • Player A (1800 ELO, K=24) beats Player B (1800 ELO, K=24)
  • Player A gains: 24 × (1 - 0.5) = +12 ELO
Win Streak Bonus

Players on winning streaks receive a K-Factor boost. Disabled by default.

streakBonus:
  enabled: false       # Enable to boost K-Factor on streaks
  threshold3Wins: 3    # Bonus equal to this value after 3 wins
  threshold5Wins: 5    # Bonus equal to this value after 5+ wins
Win StreakK-Factor Bonus
3 wins+3
5+ wins+5

Glicko-2 Mode (Advanced)

Tracks rating and uncertainty (Rating Deviation). Better for servers where players have varying activity.

# elo.yaml
ratingSystem: GLICKO2

glicko2:
  startingRD: 150.0
  startingVolatility: 0.06
  systemConstant: 0.5
  rdDecayDays: 30
  maxRatingChange: 100
SettingDefaultDescription
startingRD150.0Rating Deviation — uncertainty in rating
startingVolatility0.06Expected rating fluctuation
systemConstant0.5Controls volatility changes (0.3-1.2)
rdDecayDays30Days of inactivity before RD increases
maxRatingChange100Maximum rating change per match
RD vs K-Factor
SystemWhat ChangesHow
Pokemon ShowdownK-Factor (fixed bands)Based on rating only
Glicko-2RD (dynamic per player)Based on certainty + activity

When to use Glicko-2:

  • Players have varying activity levels
  • You want more accurate matchmaking for returning players

When to use Pokemon Showdown:

  • Simpler to understand for players
  • More predictable rating changes

ELO Decay

Inactive players lose ELO over time to keep the leaderboard competitive. Forces high-rated players to stay active to maintain their ranking. Inspired by Pokemon Showdown, Chess (FIDE), and League of Legends systems.

# elo.yaml
eloDecay:
  enabled: false
  thresholdDays: 7        # Days before decay starts
  minimumElo: 1500        # Must be above this to decay
  decayPerDay: 10         # ELO lost per full day
  checkIntervalHours: 6   # How often to check
SettingDefaultDescription
enabledfalseEnable/disable ELO decay
thresholdDays7Days of inactivity before decay starts
minimumElo1500Only players above this ELO decay
decayPerDay10ELO lost per full day of inactivity
checkIntervalHours6How often the decay task runs

Note: Decay stops at the top-level floorElo setting (default: 1000).

How Decay Works

  1. Players above minimumElo who haven’t played in thresholdDays days start decaying
  2. Every full day of inactivity = decayPerDay ELO lost
  3. Decay stops when player reaches floorElo
  4. Playing any ranked match resets the inactivity timer

Example:

  • Player has 1800 ELO
  • Config: minimumElo: 1500, decayPerDay: 10, floorElo: 1000 (top-level setting)
  • Player doesn’t play for 20 days
  • Decay starts after 7 days = 13 days of decay
  • ELO loss: 13 × 10 = 130 ELO
  • Final ELO: 1800 - 130 = 1670
Cross-Server Setup

For multi-server networks, the decay task runs on one server only using a Redis lock. This prevents duplicate ELO reductions across your network.

Requirements:

No extra configuration needed — the lock is automatic when Redis is enabled.

Database Schema

ELO decay requires tracking last match time per player per format.

SQLite/MySQL: last_match_at column in format_stats table

-- Auto-created on startup
ALTER TABLE format_stats ADD COLUMN last_match_at BIGINT;

MongoDB: last_match_at field in format_stats documents (automatic)

Rank Tiers

Cosmetic ranks displayed in GUI and leaderboard:

# elo.yaml
rankTiers:
  - name: "POKEBALL"
    displayName: "Poké Ball"
    minElo: 0
  - name: "GREATBALL"
    displayName: "Great Ball"
    minElo: 1300
  - name: "ULTRABALL"
    displayName: "Ultra Ball"
    minElo: 1500
  - name: "MASTERBALL"
    displayName: "Master Ball"
    minElo: 1700
  - name: "BEASTBALL"
    displayName: "Beast Ball"
    minElo: 1900
  - name: "CHERISH"
    displayName: "Cherish Ball"
    minElo: 2100

📝 Tiers affect display and rank rewards only — they don’t change matchmaking. Use LuckPerms Integration for rank-based permissions.


See Also