Glicko-2 레이팅 직접 구현

만든 것3분조회 –

여러 명이 실시간으로 겨루는 추론 대결 게임을 만들고 있고, 한 판이 끝나면 참가자들 사이에 순위가 나옵니다. 그 결과를 모아 리더보드를 세우고 다음 판 상대를 붙여 주려면 사람마다 실력 점수가 있어야 합니다.

그런데 이 게임은 판수가 사람마다 크게 벌어져서, 어떤 사람은 첫날 50판을 하고 어떤 사람은 일주일에 두 판을 합니다. 그러면 점수가 같아도 그 점수를 믿을 만한 정도가 전혀 다른데, 점수 하나만 들고 있으면 그 차이를 표현할 자리가 없습니다.

레이팅이라고 하면 보통 Elo 를 먼저 떠올리고 구현도 가장 간단하니, Elo 로는 이 상황을 어떻게 다루는지부터 보고 Glicko-2 로 넘어갔습니다. Lichess 와 CS2 가 쓰는 방식입니다.

Elo와의 차이 — 추정의 불확실성

Elo 는 점수 하나만 다뤄서 이기면 오르고 지면 내리지만, “이 점수를 얼마나 믿을 수 있는가” 를 표현할 방법이 없습니다.

새로 가입한 사람의 1500점과 100판 한 사람의 1500점은 같은 1500 이 아닙니다. 앞은 아직 실력을 모르는 1500 이고 뒤는 확실한 1500 이지만, Elo 는 이 둘을 구분 못 합니다.

Glicko-2 는 그래서 값을 세 개로 나눠 들고 있습니다.

  • rating — 실력 추정치
  • RD(rating deviation) — 그 추정을 얼마나 믿을 수 있는지 (불확실성)
  • volatility — 실력이 얼마나 들쭉날쭉한지

RD 가 크면 “아직 잘 모름” 이라 한 판에 점수가 크게 움직이고 RD 가 작으면 “확실함” 이라 조금만 움직입니다. 오래 안 하면 RD 가 다시 커집니다 — 오래 쉰 사람의 실력은 다시 불확실해지기 때문입니다.

구현 — 논문의 계산 단계 그대로

Glicko-2 는 공식 논문에 계산 순서가 단계별로 정리돼 있어서 그걸 그대로 코드로 옮기면 되고, 시작은 값을 Glicko-2 스케일로 바꾸는 것입니다.

const TAU = 0.5          // 시스템 변동성 제약 (낮을수록 안정적)

function toGlicko2Scale(r: number, rd: number) {
  return { mu: (r - 1500) / 173.7178, phi: rd / 173.7178 }
}

1500 과 173.7178 은 Glicko-1 점수(익숙한 1500 기준)와 내부 계산 스케일을 오가는 상수입니다. 사용자에게는 1500 대 점수를 보여주고 계산은 내부 스케일에서 합니다.

핵심은 상대별 가중치 g(phi) 와 기대 승률 E 두 개입니다.

function g(phi: number): number {
  return 1 / Math.sqrt(1 + (3 * phi * phi) / (Math.PI * Math.PI))
}

function E(mu: number, mu_j: number, phi_j: number): number {
  return 1 / (1 + Math.exp(-g(phi_j) * (mu - mu_j)))
}

E 는 “내 실력 mu 로 상대 mu_j 를 이길 기대 확률” 이라, 실제 결과가 이 기대보다 좋으면 점수가 오르고 나쁘면 내립니다. 상대의 RD 가 크면(phi_j 큼) g 가 그 판의 영향을 줄입니다 — 불확실한 상대와의 결과는 덜 신뢰하는 겁니다.

한 판도 안 한 사람도 RD 는 커짐

놓치기 쉬운 게 하나 있는데, 이번 판에 아무 게임도 안 한 플레이어도 RD 는 갱신해야 합니다.

if (opponents.length === 0) {
  // 게임을 안 했으면 RD를 350 쪽으로 키워 불확실성을 반영
  const { mu, phi } = toGlicko2Scale(player.rating, player.rd)
  const phi_star = Math.min(
    Math.sqrt(phi * phi + player.volatility * player.volatility),
    350 / 173.7178,
  )
  const { rating, rd } = fromGlicko2Scale(mu, phi_star)
  return { rating, rd, volatility: player.volatility }
}

시간이 지날수록 “이 사람이 지금도 그 실력일까?” 라는 확신이 줄어듭니다. 350 이 RD 의 상한이라, 아주 오래 쉰 사람은 신규 가입자에 가까운 불확실성으로 돌아갑니다.

왜 Glicko-2였나

Elo 로는 50판 한 사람과 두 판 한 사람의 점수를 똑같이 취급해서, 적게 한 사람의 점수가 실제 실력을 반영하는지 알 수 없습니다.

Glicko-2 는 RD 로 이걸 구분하고, 점수만 들고 있는 게 아니라 그 점수가 얼마나 확실한지까지 같이 들고 있으니 리더보드에도 매칭에도 쓸 데가 생깁니다. 실력이 비슷하면서 둘 다 점수가 확실한 상대를 붙이려면 매칭할 때 RD 를 같이 보면 됩니다.

레이팅 시스템은 겉보기에는 점수 계산이지만 실제로는 그 숫자를 얼마나 믿을 수 있느냐를 다루는 문제였고, Glicko-2 는 그 신뢰도를 점수와 같은 급으로 올려 둔 설계입니다.

참고

  1. 불러오는 중