はじめに
対戦ゲームにおいて、プレイヤーの実力を数値化するレーティングシステムは欠かせない仕組みです。最も有名なのはEloレーティングですが、Eloにはレーティングの信頼度を扱えないという弱点があります。
Mark Glickmanが考案したGlicko-2は、Eloを発展させたベイズ推定ベースのレーティングシステムで、各プレイヤーについて以下の3つのパラメータを管理します。
| パラメータ | 意味 |
|---|---|
| Rating | 推定実力値(初期値: 1500.0) |
| Deviation | レーティングの不確実性。高いほど信頼度が低い(初期値: 350.0) |
| Volatility | パフォーマンスの変動幅。高いほど成績にムラがある(初期値: 0.06) |
Lichess、Counter-Strike 2、Splatoon 2など、多くのサービスで採用されています。
今回、このGlicko-2アルゴリズムをピュアElixirで実装し、Hexパッケージとして公開しました。
特徴
- Glickmanの公開論文に忠実な実装(収束にはIllinois法を使用)
- スコアは
[0.0, 1.0]の任意の値を受け付け、勝ち/負け/引き分け以外の段階的な結果にも対応 -
{rating, deviation, volatility}のシンプルなタプルで表現。構造体やプロセスは不使用 - 純粋関数のみで構成され、並行処理でも安全
- 入力値は境界で厳密にバリデーション
- ランタイム依存ゼロ
- Elixir 1.14+ / OTP 25+ 対応
使い方
# mix.exs
def deps do
[{:glicko_rating_system, "~> 1.0"}]
end
player = {1500.0, 200.0, 0.06}
opponent = {1400.0, 30.0, 0.06}
# 対局後のレーティング更新(1.0 = 勝ち)
GlickoRatingSystem.rate(player, [{opponent, 1.0}])
# 勝率の予測
GlickoRatingSystem.expected_score(player, opponent)
段階的スコア
Glicko-2の数学的な定義上、スコアは 1(勝ち)、0.5(引き分け)、0(負け)に限定されません。0.0 から 1.0 の間であれば、どんな値でも指定できます。
これにより、たとえば以下のような段階的な結果を表現できます。
| 結果 | 勝者のスコア | 敗者のスコア |
|---|---|---|
| 完全勝利 | 1.0 |
0.0 |
| 部分的勝利 | 0.75 |
0.25 |
| 引き分け | 0.5 |
0.5 |
唯一の制約は、両者のスコアの合計が 1.0 になることです。これによりレーティングプール全体のゼロサム性が保たれます。
具体的にどのスコア値をどの結果に割り当てるかは、各プラットフォームの設計判断に委ねられています。
開発の経緯
このライブラリは、チェス・将棋・象棋などの対戦プラットフォーム Sashité のために開発しました。勝利の質に応じてレーティング変動を差別化したいという要件がありましたが、アルゴリズムを独自に改変するのではなく、Glicko-2が本来持つスコアパラメータの柔軟性を活かすことで、仕様に忠実なまま実現できました。
リンク
- Hex: hex.pm/packages/glicko_rating_system
- ドキュメント: hexdocs.pm/glicko_rating_system
- GitHub: github.com/sashite/glicko_rating_system.ex
- 論文: Example of the Glicko-2 system (PDF)
Elixir Forum版との主な違い:
- Qiitaの読者層に合わせて、Glicko-2自体の説明を冒頭で丁寧にしている(Elixir Forumではレーティングシステムに馴染みのある読者を想定していたが、Qiitaではゲーム開発に興味がある幅広い層を想定)
- 技術用語は日本語で補足しつつ、原語も残して検索性を確保
- 「はじめに」→「特徴」→「使い方」→「段階的スコア」→「経緯」→「リンク」の流れで、Qiitaの記事として自然な構成に