elo
차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
다음 판 | 이전 판 | ||
elo [2018/02/27 13:49] – 만듦 rex8312 | elo [2024/05/08 13:34] (현재) – rex8312 | ||
---|---|---|---|
줄 1: | 줄 1: | ||
======= Elo ======= | ======= Elo ======= | ||
+ | |||
+ | |||
+ | <code python> | ||
+ | |||
+ | 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: | ||
+ | """ | ||
+ | player와 opponent와 게임에서 얻을 수 있는 기대 점수(승률) | ||
+ | |||
+ | :param player: player의 Elo | ||
+ | :param opponent: opponent의 Elo | ||
+ | """ | ||
+ | return 1 / (1 + 10 ** ((opponent - player) / 400)) | ||
+ | |||
+ | |||
+ | def elo(old_elo, | ||
+ | """ | ||
+ | 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, | ||
+ | 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, | ||
+ | player1_score += 0.5 | ||
+ | player2_score += 0.5 | ||
+ | |||
+ | player1_score /= n_games | ||
+ | player2_score /= n_games | ||
+ | |||
+ | player1_exp_score = expected_score(player1.elo, | ||
+ | player1_k = k_factor(player1) | ||
+ | player1.elo = elo(player1.elo, | ||
+ | |||
+ | player2_exp_score = expected_score(player2.elo, | ||
+ | player2_k = k_factor(player2) | ||
+ | player2.elo = elo(player2.elo, | ||
+ | return player1, player2 | ||
+ | |||
+ | |||
+ | def match_make(players): | ||
+ | return random.sample(players, | ||
+ | |||
+ | |||
+ | class Player: | ||
+ | def __init__(self, | ||
+ | self.name = name | ||
+ | self._performance = performance | ||
+ | self.elo = init_elo | ||
+ | |||
+ | def play(self): | ||
+ | # 정규 분포보다, | ||
+ | # return random.random() + self._performance | ||
+ | |||
+ | # std가 커질 수록 구분이 어려움 | ||
+ | return random.gauss(self._performance, | ||
+ | |||
+ | def __repr__(self): | ||
+ | return f' | ||
+ | |||
+ | |||
+ | def moving_average(xs, | ||
+ | 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_extended.insert(-1, | ||
+ | xs_extended = np.concatenate(xs_extended) | ||
+ | return np.convolve(xs_extended, | ||
+ | else: | ||
+ | return xs | ||
+ | |||
+ | |||
+ | if __name__ == ' | ||
+ | |||
+ | max_games = 100000 | ||
+ | n_game_per_iter = 10 | ||
+ | players = [Player(' | ||
+ | | ||
+ | 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, | ||
+ | records.append([p.elo for p in players]) | ||
+ | |||
+ | for player, elo_records in zip(players, | ||
+ | elo_records = moving_average(elo_records, | ||
+ | plt.plot(elo_records, | ||
+ | |||
+ | plt.legend() | ||
+ | plt.show() | ||
+ | |||
+ | # embed() | ||
+ | |||
+ | </ | ||
+ | |||
+ | ===== 참고 ===== | ||
* https:// | * https:// | ||
+ | * https:// | ||
+ | * [[trueskill|Trueskill]] |
elo.1519739370.txt.gz · 마지막으로 수정됨: (바깥 편집)