競技對戰評分系統:從 Elo 及其進化版演算法深度解析

在競技遊戲(Esports)與棋類比賽中,如何精確測量玩家的「真實實力」是一門結合統計學與心理學的深奧學問。最經典的 Elo 系統雖然奠定了基礎,但在現代遊戲複雜的環境下(如玩家長時間未玩、多人團戰等),它顯得有些力不從心。

本文將帶你走過評分系統的進化史,從 Elo 到 Glicko,再到微軟的 TrueSkill,最後解析現代遊戲常見的 MMR 雙軌制實作邏輯。

1. Elo 系統及其應用案例 (如 WoW, LoL)

Elo 系統由 Arpad Elo 教授發明,是所有評分系統的鼻祖。它的核心概念是:根據兩位玩家的分數差,預測勝率並在賽後調整分數。index image

核心公式

  1. 預期勝率 (Expected Score)
    $$E_A = \frac{1}{1 + 10^{(R_B - R_A)/400}}$$
    其中 $R_A$ 與 $R_B$ 分別為玩家 A 與 B 的當前評分。

  2. 評分更新 (Rating Update)
    $$R’_A = R_A + K(S_A - E_A)$$
    $S_A$ 為實際對戰結果(勝=1, 和=0.5, 敗=0),$K$ 為權重因子。

然而,單純的 Elo 系統存在幾項局限性:

  • 無法處理「不確定性」: 系統無法分辨一個打過 1000 場與打過 1 場的 1500 分玩家,對兩者的實力信心水準應有所不同。
  • 時間衰減問題: 玩家若一年未參賽,實力可能下滑,但 Elo 分數會停留在原地。

魔獸世界 (WoW) 與 英雄聯盟 (LoL) 的實踐

這類遊戲採用了 MMR (Matchmaking Rating)視覺分數 (Rank) 分離的機制,以工程手段部分迴避上述局限性:

  • 補分效應: 讓實力強的人能獲得額外積分,快速通過低分段,減少「不確定性」帶來的匹配失準。
  • 積分膨脹: 透過營運手段維持玩家的參與熱度,同時間接舒緩時間衰減問題。

實作參考:GitHub: sublee/elo

2. Glicko 系統:引入「可信度」概念

由 Mark Glickman 教授開發,Glicko 系統在積分中加入了 RD (Rating Deviation,評分偏差)

核心邏輯與公式

  1. RD 影響因子 (g)
    $$g(RD) = \frac{1}{\sqrt{1 + \frac{3q^2 RD^2}{\pi^2}}}$$
    其中 $q = \frac{\ln(10)}{400} \approx 0.0057565$。

  2. 修正後的預期勝率 (E)
    $$E = \frac{1}{1 + 10^{-g(RD_j)(R - R_j)/400}}$$
    其中下標 $j$ 代表對手,$R_j$ 與 $RD_j$ 分別為對手的評分與評分偏差。這表示對手的 RD 越高,其當前分數的參考價值就越低,預期勝率會更趨向 0.5。

  • 行為特徵: 每個玩家不只有一個分數,還有一個偏差值。
    • 頻繁對戰: RD 值減少,系統對你的實力更有把握。
    • 長期未玩: RD 值增加,代表系統對你的實力產生懷疑。
  • Glicko-2: 進一步引入了「波動率」(Volatility),是目前 OGS (Online Go Server) 與 Lichess 等平台的主流演算法。

實作參考:GitHub: sublee/glicko2

3. WHR (Whole-History Rating):圍棋界的精準導師

在圍棋研究中,WHR 被認為是比 Glicko 更精確的系統,它的特點在於「回溯性」。

核心公式 (Bradley-Terry)

$$P(A > B) = \frac{\gamma_A}{\gamma_A + \gamma_B} = \frac{e^{R_A}}{e^{R_A} + e^{R_B}}$$
WHR 將實力參數 $\gamma$ 定義為指數形式 $e^R$,這使得整個歷史紀錄的對數似然函數 (Log-Likelihood) 更易於進行牛頓法 (Newton’s method) 優化。

  • 全歷史推算: 與 Elo 不同,WHR 會在每場比賽後,重新審視玩家「整個職業生涯」的所有對戰紀錄。
  • 動態實力曲線: 它能更精準地描繪出一個棋手從新手到頂尖高手的成長軌跡,非常適合分析歷史棋譜。

實作參考:GitHub: peteberry/whr

4. Microsoft TrueSkill:團隊對戰的王者

這是由微軟研究院開發的專利演算法,專門解決 1v1 以外的多人團隊對戰需求。

核心公式 (勝率預測)

以下為 1v1 的簡化版本(兩位玩家各一個 $\sigma$):
$$P(W > L) = \Phi\left( \frac{\mu_w - \mu_l}{\sqrt{2\beta^2 + \sigma_w^2 + \sigma_l^2}} \right)$$
其中 $\Phi$ 為標準常態分佈的累積分布函數 (CDF),$\beta$ 為表現波動參數。此公式計算兩組高斯分佈相減後結果大於 0 的機率。在團隊模式下,分母的 $\sigma^2$ 項需對所有隊員的個人不確定性求和。

  • 團隊邏輯: TrueSkill 的精髓在於能將「團隊表現」拆解回「個人貢獻」。
  • 貝氏推論 (Bayesian Inference): 它將玩家實力視為一個「高斯分佈」(高斯曲線),包含平均值(實力 $\mu$)與標準差(不確定性 $\sigma$)。

開源替代方案: 由於 TrueSkill 有專利限制,獨立開發者通常選用 OpenSkill/openskill.py

5. 深度解析:MMR/Rank 雙軌制的實作邏輯

為什麼遊戲不直接顯示 MMR?因為 MMR 波動極其敏感,直接顯示會讓玩家產生劇烈的負面情緒。雙軌制提供了一個「緩衝區」。

核心運作公式

視覺積分(如 LP)的增減通常取決於 (MMR - 當前牌位對應分數) 的差距:

  1. 高度領先 (MMR > 牌位分數): 贏球獲得加成獎勵(Gain = Base + Bonus),輸球輕微扣分。
  2. 實力下滑 (MMR < 牌位分數): 贏球微量加分,輸球巨幅扣分。系統試圖將你拉回真實 MMR 區間。

Python 範例實作:MMR 與 LP 的動態修正

以下程式碼示範如何根據隱藏 MMR 來計算視覺積分 (LP) 的變動:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from typing import Final

class Player:
"""代表對戰玩家的類別,處理隱藏實力與視覺積分的連動。"""

BASE_CHANGE: Final[int] = 20 # 基礎積分增減值

def __init__(self, name: str, hidden_mmr: int, visual_lp: int):
"""
初始化玩家資料。

Args:
name (str): 玩家名稱。
hidden_mmr (int): 系統判斷的隱藏實力分數。
visual_lp (int): 玩家在介面上看到的視覺積分。
"""
self.name: str = name
self.hidden_mmr: int = hidden_mmr
self.visual_lp: int = visual_lp

def update_visual_lp(self, is_win: bool) -> None:
"""
根據對戰結果更新視覺積分 (LP)。

Args:
is_win (bool): 比賽是否勝利。
"""
# 計算差距 (Gap)
# 假設 MMR 高於視覺積分,表示玩家處於「低估」狀態,應快速升段
gap = self.hidden_mmr - self.visual_lp

# 修正係數 (差距越大,修正力道越強)
adjustment = gap * 0.05

if is_win:
lp_gain = max(10, round(self.BASE_CHANGE + adjustment))
self.visual_lp += lp_gain
print(f"[{self.name}] 勝利!獲得 {lp_gain} LP (當前總計: {self.visual_lp})")
else:
# 如果 MMR 遠低於視覺積分,輸球會扣除大量積分
lp_loss = max(10, round(self.BASE_CHANGE - adjustment))
self.visual_lp -= lp_loss
print(f"[{self.name}] 敗北... 扣除 {lp_loss} LP (當前總計: {self.visual_lp})")

# 範例測試
if __name__ == "__main__":
# 案例一:實力(MMR)極高,但目前視覺積分很低 (系統低估玩家)
player_a = Player("ElitePro", hidden_mmr=2500, visual_lp=1500)
player_a.update_visual_lp(is_win=True) # 預期加分會大幅超過 20

# 案例二:靠運氣上分,MMR 其實很低 (系統高估玩家)
player_b = Player("LuckyGuy", hidden_mmr=1000, visual_lp=1500)
player_b.update_visual_lp(is_win=False) # 預期扣分會大幅超過 20

演算法對照總結

系統名稱主要特點常用領域實作參考連結
Elo簡單直觀、計算成本低1v1、基礎排名sublee/elo
Glicko-2引入評分偏差與波動率OGS, CS2, Lichesssublee/glicko2
WHR完整歷史回溯推算圍棋歷史分析peteberry/whr
TrueSkill支援多人/多團隊、收斂極快Xbox Live, Halosublee/trueskill
OpenSkillTrueSkill 的開源替代方案獨立遊戲開發openskill.py

開發建議與情境選擇

  1. 僅有 1v1: 推薦使用 Glicko-2。它比 Elo 更能反映玩家近期是否活躍。
  2. 混合 1v1 與 2v2: 強烈建議使用 OpenSkill
  3. 追求營運成就感: 務必實作第 5 章的 MMR/Rank 雙軌制,這能提供良好的心理緩衝。
  4. 防虐菜機制: 持續監測 (MMR - Rank_Score),當差值過大時,直接賦予該帳號「快速攀升狀態」。

也許你也會想看看