elo
Elo
import random import collections import math import numpy as np import matplotlib.pyplot as plt from IPython import embed def k_factor(player): """ player의 k factor 계산 """ if player.elo < 1100: return 25 elif player.elo < 2400: return 15 else: return 10 def expected_score(player: float, opponent: float): """ player와 opponent와 게임에서 얻을 수 있는 기대 점수(승률) :param player: player의 Elo :param opponent: opponent의 Elo """ return 1 / (1 + 10 ** ((opponent - player) / 400)) def elo(old_elo, true_score, exp_score, k): """ player의 새로운 Elo 값 계산 :param old_elo: 이전 Elo :param true_score: player의 실제 점수 :param exp_score: player의 기대 점수 :param k: k-factor """ return old_elo + k * (true_score - exp_score) def play_game(player1, player2, n_games=1): score1 = player1.play() score2 = player2.play() player1_score = 0.0 player2_score = 0.0 for _ in range(n_games): if score1 > score2: player1_score += 1.0 player2_score += 0.0 elif score1 < score2: player1_score += 0.0 player2_score += 1.0 elif math.isclose(score1, score2): player1_score += 0.5 player2_score += 0.5 player1_score /= n_games player2_score /= n_games player1_exp_score = expected_score(player1.elo, player2.elo) player1_k = k_factor(player1) player1.elo = elo(player1.elo, player1_score, player1_exp_score, player1_k) player2_exp_score = expected_score(player2.elo, player1.elo) player2_k = k_factor(player2) player2.elo = elo(player2.elo, player2_score, player2_exp_score, player2_k) return player1, player2 def match_make(players): return random.sample(players, 2) class Player: def __init__(self, name, performance, init_elo=1000): self.name = name self._performance = performance self.elo = init_elo def play(self): # 정규 분포보다, 균등분포가 Elo가 크게 벌어짐 # return random.random() + self._performance # std가 커질 수록 구분이 어려움 return random.gauss(self._performance, 1.0) def __repr__(self): return f'{self.name}: {self._performance}, {int(self.elo):d}' def moving_average(xs, window_size=3): assert len(xs) > 0 assert window_size > 0 if window_size % 2 == 0: window_size += 1 if window_size > 1 and len(xs) > window_size: weights = np.ones(window_size) / window_size xs_extended = [xs] for _ in range(window_size // 2): xs_extended.insert(0, xs[:1]) xs_extended.insert(-1, xs[-1:]) xs_extended = np.concatenate(xs_extended) return np.convolve(xs_extended, weights, mode='valid') else: return xs if __name__ == '__main__': max_games = 100000 n_game_per_iter = 10 players = [Player('A', 1, init_elo=1200), Player('B', 0.5), Player('C', 0.45), Player('D', 0.40), Player('E', 0.30)] records = collections.deque(maxlen=max_games // n_game_per_iter) for n_games in range(max_games // n_game_per_iter): player1, player2 = match_make(players) play_game(player1, player2, n_game_per_iter) records.append([p.elo for p in players]) for player, elo_records in zip(players, zip(*records)): elo_records = moving_average(elo_records, 100) plt.plot(elo_records, label=player.name) plt.legend() plt.show() # embed()
참고
elo.txt · 마지막으로 수정됨: 2024/05/08 13:34 저자 rex8312