「 勝負は 乱数を使ったジャンケンで、 ラウンド数を入力できるようにしましょう」
📄 step_1_0.py
:
import random
#
# python step_1_0.py
#
# 0, 1, 2 のいずれかを返す
def gyanken():
return random.randint(0, 2)
if __name__ == "__main__":
print("Please input round number(1-100):")
round = int(input())
counts = [0,0,0]
for i in range(0, round):
result = gyanken()
counts[result] += 1
if result == 0:
print("aiko")
elif result == 1:
print("A win")
elif result == 2:
print("B win")
else:
print("Error")
print(f"aiko: {counts[0]}, A win: {counts[1]}, B win: {counts[2]}")
「 👇 Wikipedia では、 チェスでは 引き分けは 0.5勝、0.5敗 としているという説明があるだけで、計算方法は書いてないぜ」
📖 イロレーティング
「 0.5 勝 マイナス 0.5 敗 は 0 だから、レーティングは 0 動くらしい」
「 レーティング差が 0 の2者のとき、対局後のレーティングはどう動くんだぜ?」
「 A 側の勝率が50%で、 B 側の勝率が50%で、K が 32 のときは 32 * 0.5 で 16 動くのよ」
📄 step_1_1_0.py
:
#
# python step_1_1_0.py
#
import random
games = [0,0,0]
# 0, 1, 2 のいずれかを返す
def on_gyanken_1():
return random.randint(0, 2)
def main(
on_gyanken,
on_result,
on_end):
print("Please input round number(1-100):")
round = int(input())
for i in range(0, round):
result_1 = on_gyanken()
games[result_1] += 1
on_result(result_1)
on_end()
if __name__ == "__main__":
def on_result_1(result):
if result == 0:
print("aiko")
elif result == 1:
print("A win")
elif result == 2:
print("B win")
else:
print("Error")
def on_end_1():
print(f"aiko: {games[0]}, A win: {games[1]}, B win: {games[2]}")
main(on_gyanken=on_gyanken_1,
on_result=on_result_1,
on_end=on_end_1)
📄 step_2_0.py
:
#
# python step_1_1_0.py
#
import math
from step_1_1_0 import on_gyanken_1, games, main
# R0 = 2000
ratings = [0, 2000, 2000]
# Constant K
K = 32
# Rating calculate
def get_x_by_y(y):
if y==0:
return 1
else:
return 400 * math.log10(y)
# Rating calculate
def get_y_by_x(x):
return x ** (x / 400)
# Win rate
def get_win_rate_for_upper_rating(win_games):
return win_games / (win_games + 1)
# Win rate
def get_win_rate_for_lower_rating(win_games):
return 1 / (win_games + 1)
if __name__ == "__main__":
def on_result_1(result):
# レーティング差
ab = ratings[1] - ratings[2]
ba = ratings[2] - ratings[1]
y = abs(ba)
print(f"y: {y}")
x = get_x_by_y(y)
# 勝率
win_rate_for_lower_rating = 1 / (x+1)
#win_rate_for_upper_rating = x / (x+1)
if result == 0:
# レーティングは動きません
print(f"aiko > ratings A {ratings[1]}, B {ratings[2]}")
elif result == 1:
offset = K * win_rate_for_lower_rating
ratings[1] += offset
ratings[2] -= offset
print(f"A win > ratings A {ratings[1]}, B {ratings[2]}")
elif result == 2:
offset = K * win_rate_for_lower_rating
ratings[2] += offset
ratings[1] -= offset
print(f"B win > ratings A {ratings[1]}, B {ratings[2]}")
else:
print("Error")
def on_end_1():
print(f"games: aiko: {games[0]}, A win: {games[1]}, B win: {games[2]}")
print(f"ratings: aiko: {ratings[0]}, A win: {ratings[1]}, B win: {ratings[2]}")
main(on_gyanken=on_gyanken_1,
on_result=on_result_1,
on_end=on_end_1)
📄 step_2_0.py
:
#
# python step_2_0.py
#
import math
from step_1_1_0 import on_gyanken_1, games, main
# R0 = 2000
ratings = [0, 2000, 2000]
# Constant K
K = 32
# 1勝するために必要な対局数(暗記表の x )を取得
# 実数でも算出できるが、(1.0 以上の数になるよう数式を調整している前提で)整数にして返す
def get_games_by_rating_difference(
rating_difference): # 暗記表の y
# ゼロなら
if rating_difference==0:
return 1
# 負数なら
elif rating_difference <0:
# 負数を指定できないので、符号をひっくり返して、あとで戻す
return -math.floor(400 * math.log10(-rating_difference))
# マイナス符号の付ける位置で結果が変わってくるので注意
#return math.floor(-400 * math.log10(-rating_difference))
# 正の数なら
else:
return math.floor(400 * math.log10(rating_difference))
# レーティング差(暗記表の y )を取得
def get_rating_difference_by_games(
games): # 暗記表の x : 実数
return math.floor(10 ** (games / 400))
# Win rate : 実数
def get_win_rate_for_upper_rating(win_games):
return win_games / (win_games + 1)
# Win rate : 実数
def get_win_rate_for_lower_rating(win_games):
return 1 / (win_games + 1)
if __name__ == "__main__":
def on_result_1(result):
# あいこ
if result == 0:
print("""\
+------+
| aiko |
+------+\
""")
# レーティングは動きません
print(f"* ratings: A {ratings[1]}, B {ratings[2]}")
# A が勝った
elif result == 1:
print("""\
+-------+
| A win |
+-------+\
""")
# b から見た a とのレーティング差
difference_b_to_a = ratings[1] - ratings[2]
print(f"* b から見た a とのレーティング差: {difference_b_to_a}")
# b から見た a に1勝するために必要な対局数
games_b_to_a = get_games_by_rating_difference(difference_b_to_a)
print(f"* b から見た a に1勝するために必要な対局数: {games_b_to_a}")
# b から見た a への勝率
if 0 <= difference_b_to_a:
Wba = get_win_rate_for_upper_rating(games_b_to_a)
else:
Wba = get_win_rate_for_lower_rating(games_b_to_a)
print(f"* b から見た a への勝率(Wba): {Wba}")
offset = math.floor(K * Wba)
ratings[1] += offset
ratings[2] -= offset
print(f"* K: {K}, offset: {offset}, ratings: A {ratings[1]}, B {ratings[2]}")
# B が勝った
elif result == 2:
print("""\
+-------+
| B win |
+-------+\
""")
# a から見た b とのレーティング差
difference_a_to_b = ratings[2] - ratings[1]
print(f"* a から見た b とのレーティング差: {difference_a_to_b}")
# a から見た b に1勝するために必要な対局数
games_a_to_b = get_games_by_rating_difference(difference_a_to_b)
print(f"* a から見た b に1勝するために必要な対局数: {games_a_to_b}")
# a から見た b への勝率
if 0 <= difference_a_to_b:
Wab = get_win_rate_for_upper_rating(games_a_to_b)
else:
Wab = get_win_rate_for_lower_rating(games_a_to_b)
print(f"* a から見た b への勝率(Wab): {Wab}")
offset = math.floor(K * Wab)
ratings[2] += offset
ratings[1] -= offset
print(f"* K: {K}, offset: {offset}, ratings: A {ratings[1]}, B {ratings[2]}")
else:
print("Error")
def on_end_1():
print(f"""\
+--------+
| result |
+--------+
* games: aiko: {games[0]:4}, A win: {games[1]:4}, B win: {games[2]:4}
* ratings: aiko: {ratings[0]:4}, A win: {ratings[1]:4}, B win: {ratings[2]:4}\
""")
print("""\
+-------+
| start |
+-------+\
""")
# レーティングは動きません
print(f"* ratings: A {ratings[1]}, B {ratings[2]}")
main(on_gyanken=on_gyanken_1,
on_result=on_result_1,
on_end=on_end_1)
「 その前に 大会の結果を ファイルに保存するようにしようぜ?」
# ファイルへ保存
with open('data/step_2_0.csv', mode='w') as f:
f.write(f"""\
player, win, rating
------, ----, ------
aiko, {games[0]:4}, {ratings[0]:6}
A, {games[1]:4}, {ratings[1]:6}
B, {games[2]:4}, {ratings[2]:6}
""")
「 それは 100局の集計にはなるけど、
大会を 100回 記録するには?」
「 集計に 足し込むか、ファイルを100個作ればいいのでは?」
player_1_name, player_1_rating_before_game, player_2_name, player_2_rating_before_game, win_player, moving_rating
A, 2000, B, 2000, 2, 16
A, 1984, B, 2016, 2, 31
A, 1953, B, 2047, 2, 31
A, 1922, B, 2078, 1, -1
A, 1921, B, 2079, 0, 0
「 👆 レーティングが大きい方が勝ったら、なんで もっと大きくレーティングが移動するんだぜ?」
# 1勝するために必要な対局数(暗記表の x )を取得
# 実数でも算出できるが、(1.0 以上の数になるよう数式を調整している前提で)整数にして返す
def get_games_by_rating_difference(
rating_difference): # 暗記表の y
return math.floor(10 ** (rating_difference / 400))
## ゼロなら
#if rating_difference==0:
# return 1
#
## 負数なら
#elif rating_difference <0:
# # 負数を指定できないので、符号をひっくり返して、あとで戻す
# return -math.floor(400 * math.log10(-rating_difference))
# # マイナス符号の付ける位置で結果が変わってくるので注意
# #return math.floor(-400 * math.log10(-rating_difference))
#
## 正の数なら
#else:
# return math.floor(400 * math.log10(rating_difference))
# レーティング差(暗記表の y )を取得
def get_rating_difference_by_games(
games): # 暗記表の x : 実数
#return math.floor(10 ** (games / 400))
return math.floor(400 * math.log10(games))
player_1_name, player_1_rating_before_game, player_2_name, player_2_rating_before_game, win_player, moving_rating
A, 2000, B, 2000, 0, 0
A, 2000, B, 2000, 1, 16
A, 2016, B, 1984, 1, 16
A, 2032, B, 1968, 1, 16
A, 2048, B, 1952, 1, 16
A, 2064, B, 1936, 1, 21
A, 2085, B, 1915, 1, 21
A, 2106, B, 1894, 1, 24
A, 2130, B, 1870, 1, 25
A, 2155, B, 1845, 2, 32
A, 2123, B, 1877, 0, 0
A, 2123, B, 1877, 1, 25
A, 2148, B, 1852, 2, 32
A, 2116, B, 1884, 2, 32
「 👆 レーティング差が広がったら、移動するレーティング量も増えてしまうの、なんでだぜ?」
player_1_name, player_1_rating_before_game, player_2_name, player_2_rating_before_game, win_player, moving_rating
A, 2000, B, 2000, 1, 16
A, 2016, B, 1984, 2, 32
A, 1984, B, 2016, 1, 32
A, 2016, B, 1984, 0, 0
A, 2016, B, 1984, 1, 16
A, 2032, B, 1968, 1, 16
A, 2048, B, 1952, 0, 0
A, 2048, B, 1952, 0, 0
A, 2048, B, 1952, 2, 32
A, 2016, B, 1984, 2, 32
A, 1984, B, 2016, 2, 16
A, 1968, B, 2032, 2, 16
A, 1952, B, 2048, 1, 32
A, 1984, B, 2016, 0, 0
A, 1984, B, 2016, 2, 16
「 これだけあれば、
プレイヤーを3人に増やして ランダム・マッチングも行けるだろう」
add C 2000
remove C
「 👆 プレイヤーの新規追加、削除も トランザクション・データとして残しておけば
データの読取が楽じゃない?」
「 イロ・レーティングって プレイヤーが2サイドなことを前提としてるよな」
id display_name rating
-------- ------------ ------
player_1 Alice 2000
player_2 Bob 2000
+-------------------+
| tournament result |
+-------------------+
* name: Alice , rating: 2102
* name: Francisca , rating: 2073
* name: Bob , rating: 2013
* name: Charley , rating: 1969
* name: Eric , rating: 1922
* name: Dingo , rating: 1921
「 👆 ランダム・マッチングで 100ラウンド、
ジャンケンでも 確率的というだけで これだけ ばらけるしな。
ガウス分布になってくだろ」
「 プレイヤーにちからの差がないと
レーティングの機能は 働かないのかもしれないわねぇ」
+-------------------+
| tournament result |
+-------------------+
* name: Alice , rating: 2379
* name: Bob , rating: 2287
* name: Charley , rating: 2087
* name: Dingo , rating: 1989
* name: Eric , rating: 1804
* name: Francisca , rating: 1454
「 レーティングが 400 以上離れているやつは 対局しないようにする必要があるんじゃないか?」
+-------------------+
| tournament result |
+-------------------+
* name: Alice , rating: 2436
* name: Bob , rating: 2282
* name: Charley , rating: 2195
* name: Dingo , rating: 1954
* name: Eric , rating: 1672
* name: Francisca , rating: 1461
「 👆 レーティングが 400 以上離れているやつは なるべく対局しないようにしてみたぜ」
「 レーティングが ほとんど動かないような対局が減るから、
レーティングが よく動くようになったんじゃないの?」
.
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント