Commit 95fa4b2c by ziho

Initial commit

parents
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import random"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class Player:\n",
" def __init__(self,name,rating):\n",
" self.name=name\n",
" self.rating=rating\n",
" self.wins=0\n",
" self.losses=0\n",
" self.draw=0\n",
" self.num_game=0\n",
" \n",
"\n",
"class EloRatingSystem:\n",
" WIN=1\n",
" LOSS=0\n",
" DRAW=0.5\n",
" def __init__(self,base_rating=1500,recordSaveFile='gameRecord.txt'):\n",
" self.base_rating=base_rating\n",
" self.players={}\n",
" \n",
" self.saveFile=recordSaveFile\n",
" \n",
" self.readFlag=False\n",
" \n",
" def addModel(self,name):\n",
" p=Player(name,self.base_rating)\n",
" self.players[name]=p\n",
" return p\n",
" \n",
" def addModels(self,models):\n",
" for m in models:\n",
" self.addModel(m)\n",
" print('add model to system:{}'.format(models))\n",
" \n",
" def check(self,name,online=True):\n",
" if name not in self.players:\n",
" if online:\n",
" print('{} not in player list,please use <addmodel> to add it'.format(name))\n",
" return False\n",
" return True\n",
" \n",
"\n",
" def playgame(self,p1,p2,winrate,numgames=1,online=True):\n",
" '''\n",
" result [int]: \n",
" 假设没有平局,赢了算1,输了算0,平局是0.5\n",
" '''\n",
" self.readFlag=True\n",
" if not self.check(p1,online) or not self.check(p2,online):\n",
" return\n",
" \n",
" if online:\n",
" #只是避免把文件里读出来的再写回去\n",
" with open(self.saveFile,'a') as f:\n",
" f.write('{},{},{},{}\\n'.format(p1,p2,winrate,numgames))\n",
" \n",
" player1=self.players[p1]\n",
" player2=self.players[p2]\n",
" \n",
" p1_win=int(numgames*winrate)\n",
" p2_win=numgames-p1_win\n",
"\n",
" player1.wins+=p1_win\n",
" player2.losses+=p1_win\n",
" player2.wins+=p2_win\n",
" player1.losses+=p2_win\n",
" player1.num_game+=numgames\n",
" player2.num_game+=numgames\n",
" \n",
" R1=player1.rating\n",
" R2=player2.rating \n",
" \n",
" def expectScore(rating1,rating2):\n",
" #base on https://en.wikipedia.org/wiki/Elo_rating_system\n",
" #same function as E1=1/(1+10**((rating1-rating2)/400))\n",
" Q1=10**(rating1/400)\n",
" Q2=10**(rating2/400)\n",
" E1=Q1/(Q1+Q2)\n",
" E2=Q2/(Q1+Q2)\n",
" return E1,E2\n",
" \n",
" def choose_k(rating):\n",
" #参考FIDE:https://gobase.org/studying/articles/elo/\n",
" #另有EGD规则:http://europeangodatabase.eu/EGD/EGF_rating_system.php\n",
" if rating<2000:\n",
" return 30\n",
" elif rating>2400:\n",
" return 10\n",
" else:\n",
" return 130-rating/20\n",
" \n",
" \n",
" E1,E2=expectScore(R1,R2)\n",
" \n",
" k=choose_k(R1)\n",
" player1.rating=R1+k*(winrate-E1)\n",
" k=choose_k(R2)\n",
" player2.rating=R2+k*(1-winrate-E2)\n",
" \n",
" if player1.rating<0:\n",
" player1.rating=50\n",
" elif player2.rating<0:\n",
" player2.rating=50\n",
" \n",
" if online:\n",
" #print('{}\\'s rating:{}->{} ; {}\\'s rating:{}->{}'.format(p1,R1,player1.rating,p2,R2,player2.rating))\n",
" pass \n",
" \n",
" def readRecords(self,filename):\n",
" if self.readFlag:\n",
" print('[warning] system is running! will not do anything')\n",
" return\n",
" self.readFlag=True\n",
" with open(filename,'r') as f:\n",
" lines=f.readlines()\n",
" for l in lines:\n",
" record=l.split(',')\n",
" self.playgame(record[0],record[1],float(record[2]),int(record[3]),False)\n",
" self.summary()\n",
" \n",
" def summary(self):\n",
" players=list(self.players.values())\n",
" def sortKey(p):\n",
" return p.rating\n",
" players.sort(key=sortKey,reverse=True)\n",
" \n",
" print('{:<10s}{:<10s}{:<10s}{:<10s}{:<10s}'.format('model','rating','games','wins','losses'))\n",
" for p in players:\n",
" print('{:<10s}{:<10.2f}{:<10d}{:<10d}{:<10d}'.format(str(p.name),p.rating,p.num_game,p.wins,p.losses))\n",
" \n",
" def getPlayers(self,sortByRank=False):\n",
" players=list(self.players.values())\n",
" if sortByRank: \n",
" def sortKey(p):\n",
" return p.rating\n",
" players.sort(key=sortKey,reverse=True)\n",
" return players \n",
" \n",
" def getRating(self):\n",
" return [p.rating for p in list(self.players.values())]\n",
" \n",
" def getNearPlayer(self,name):\n",
" #获取同等级的对手,以供匹配,相差200分以内吧\n",
" res=[]\n",
" if not self.check(name):\n",
" return\n",
" R=self.players[name].rating\n",
" for p in self.players.values():\n",
" if abs(p.rating-R)<200 and p.name!=name:\n",
" res.append(p.name)\n",
" if len(res)==0:\n",
" return None\n",
" return res\n",
" \n",
" "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"models=[str(i) for i in range (0,50000,1000)]\n",
"models\n",
"\n",
"def pk(model1,model2,num_games):\n",
" winrate=1-0.5**(abs(int(model1)-int(model2))/1000) #假设新模型以一定概率战胜旧模型\n",
" res=random.choices([elo.WIN,elo.DRAW,elo.LOSS],weights=[winrate,0,1-winrate],k=1) \n",
" if int(model1)>int(model2):\n",
" return res[0]\n",
" else:\n",
" return 1-res[0]"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"add model to system:['0', '1000', '2000', '3000', '4000', '5000', '6000', '7000', '8000', '9000', '10000', '11000', '12000', '13000', '14000', '15000', '16000', '17000', '18000', '19000', '20000', '21000', '22000', '23000', '24000', '25000', '26000', '27000', '28000', '29000', '30000', '31000', '32000', '33000', '34000', '35000', '36000', '37000', '38000', '39000', '40000', '41000', '42000', '43000', '44000', '45000', '46000', '47000', '48000', '49000']\n",
"model rating games wins losses \n",
"0 1500.00 0 0 0 \n",
"1000 1500.00 0 0 0 \n",
"2000 1500.00 0 0 0 \n",
"3000 1500.00 0 0 0 \n",
"4000 1500.00 0 0 0 \n",
"5000 1500.00 0 0 0 \n",
"6000 1500.00 0 0 0 \n",
"7000 1500.00 0 0 0 \n",
"8000 1500.00 0 0 0 \n",
"9000 1500.00 0 0 0 \n",
"10000 1500.00 0 0 0 \n",
"11000 1500.00 0 0 0 \n",
"12000 1500.00 0 0 0 \n",
"13000 1500.00 0 0 0 \n",
"14000 1500.00 0 0 0 \n",
"15000 1500.00 0 0 0 \n",
"16000 1500.00 0 0 0 \n",
"17000 1500.00 0 0 0 \n",
"18000 1500.00 0 0 0 \n",
"19000 1500.00 0 0 0 \n",
"20000 1500.00 0 0 0 \n",
"21000 1500.00 0 0 0 \n",
"22000 1500.00 0 0 0 \n",
"23000 1500.00 0 0 0 \n",
"24000 1500.00 0 0 0 \n",
"25000 1500.00 0 0 0 \n",
"26000 1500.00 0 0 0 \n",
"27000 1500.00 0 0 0 \n",
"28000 1500.00 0 0 0 \n",
"29000 1500.00 0 0 0 \n",
"30000 1500.00 0 0 0 \n",
"31000 1500.00 0 0 0 \n",
"32000 1500.00 0 0 0 \n",
"33000 1500.00 0 0 0 \n",
"34000 1500.00 0 0 0 \n",
"35000 1500.00 0 0 0 \n",
"36000 1500.00 0 0 0 \n",
"37000 1500.00 0 0 0 \n",
"38000 1500.00 0 0 0 \n",
"39000 1500.00 0 0 0 \n",
"40000 1500.00 0 0 0 \n",
"41000 1500.00 0 0 0 \n",
"42000 1500.00 0 0 0 \n",
"43000 1500.00 0 0 0 \n",
"44000 1500.00 0 0 0 \n",
"45000 1500.00 0 0 0 \n",
"46000 1500.00 0 0 0 \n",
"47000 1500.00 0 0 0 \n",
"48000 1500.00 0 0 0 \n",
"49000 1500.00 0 0 0 \n"
]
}
],
"source": [
"elo=EloRatingSystem()\n",
"elo.addModels(models)\n",
"elo.summary()"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"model rating games wins losses \n",
"49000 3045.67 5002 3388 1614 \n",
"48000 2980.58 5746 3496 2250 \n",
"47000 2868.33 6564 3516 3048 \n",
"46000 2818.49 6540 3328 3212 \n",
"45000 2742.48 6228 3316 2912 \n",
"44000 2673.12 5896 3148 2748 \n",
"43000 2605.95 5942 3102 2840 \n",
"42000 2574.61 6292 3272 3020 \n",
"41000 2470.60 6142 3206 2936 \n",
"40000 2448.55 5884 3034 2850 \n",
"39000 2388.98 5954 3018 2936 \n",
"37000 2295.24 5952 3040 2912 \n",
"38000 2282.79 6082 3062 3020 \n",
"36000 2204.54 5938 3012 2926 \n",
"35000 2186.72 6008 3030 2978 \n",
"34000 2088.25 5944 3000 2944 \n",
"33000 1999.89 6004 3062 2942 \n",
"32000 1952.80 5854 2958 2896 \n",
"31000 1881.68 6102 3098 3004 \n",
"30000 1810.49 5962 2992 2970 \n",
"29000 1753.48 5976 3016 2960 \n",
"27000 1712.87 5920 2978 2942 \n",
"28000 1657.39 6024 3024 3000 \n",
"26000 1557.71 6070 3016 3054 \n",
"24000 1517.55 6062 3066 2996 \n",
"25000 1511.32 5948 2984 2964 \n",
"23000 1432.79 6176 3098 3078 \n",
"22000 1317.84 6166 3042 3124 \n",
"21000 1258.16 6064 3010 3054 \n",
"20000 1254.75 6032 2996 3036 \n",
"19000 1210.90 6138 3018 3120 \n",
"18000 1052.93 5950 2926 3024 \n",
"17000 1012.31 6060 2988 3072 \n",
"15000 934.62 5898 2870 3028 \n",
"16000 923.44 5918 2902 3016 \n",
"14000 922.92 5854 2880 2974 \n",
"13000 779.87 5804 2852 2952 \n",
"12000 715.08 6146 3018 3128 \n",
"11000 687.11 5880 2876 3004 \n",
"10000 579.16 6010 2912 3098 \n",
"8000 504.51 6006 2914 3092 \n",
"9000 487.08 6156 2992 3164 \n",
"7000 387.35 6008 2904 3104 \n",
"5000 359.80 5924 2856 3068 \n",
"6000 341.27 5982 2860 3122 \n",
"4000 263.29 6234 3018 3216 \n",
"3000 225.60 6516 3216 3300 \n",
"2000 163.61 6318 2904 3414 \n",
"1000 76.26 5620 2188 3432 \n",
"0 34.34 5044 1568 3476 \n"
]
}
],
"source": [
"for i in range(5000): \n",
" players=elo.getPlayers(sortByRank=True) #选择分数相近的作为比赛对手\n",
" black=random.choice(players).name\n",
" matchList=elo.getNearPlayer(black)\n",
" if matchList: \n",
" white=random.choice(matchList)\n",
" else:\n",
" continue #谁都打不过,或者谁都打不过\n",
" \n",
" num=2\n",
" winrate=pk(black,white,num) #就比一局\n",
" elo.playgame(black,white,winrate,numgames=num)\n",
"\n",
"elo.summary()"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"add model to system:['0', '1000', '2000', '3000', '4000', '5000', '6000', '7000', '8000', '9000', '10000', '11000', '12000', '13000', '14000', '15000', '16000', '17000', '18000', '19000', '20000', '21000', '22000', '23000', '24000', '25000', '26000', '27000', '28000', '29000', '30000', '31000', '32000', '33000', '34000', '35000', '36000', '37000', '38000', '39000', '40000', '41000', '42000', '43000', '44000', '45000', '46000', '47000', '48000', '49000']\n",
"model rating games wins losses \n",
"49000 3045.67 5002 3388 1614 \n",
"48000 2980.58 5746 3496 2250 \n",
"47000 2868.33 6564 3516 3048 \n",
"46000 2818.49 6540 3328 3212 \n",
"45000 2742.48 6228 3316 2912 \n",
"44000 2673.12 5896 3148 2748 \n",
"43000 2605.95 5942 3102 2840 \n",
"42000 2574.61 6292 3272 3020 \n",
"41000 2470.60 6142 3206 2936 \n",
"40000 2448.55 5884 3034 2850 \n",
"39000 2388.98 5954 3018 2936 \n",
"37000 2295.24 5952 3040 2912 \n",
"38000 2282.79 6082 3062 3020 \n",
"36000 2204.54 5938 3012 2926 \n",
"35000 2186.72 6008 3030 2978 \n",
"34000 2088.25 5944 3000 2944 \n",
"33000 1999.89 6004 3062 2942 \n",
"32000 1952.80 5854 2958 2896 \n",
"31000 1881.68 6102 3098 3004 \n",
"30000 1810.49 5962 2992 2970 \n",
"29000 1753.48 5976 3016 2960 \n",
"27000 1712.87 5920 2978 2942 \n",
"28000 1657.39 6024 3024 3000 \n",
"26000 1557.71 6070 3016 3054 \n",
"24000 1517.55 6062 3066 2996 \n",
"25000 1511.32 5948 2984 2964 \n",
"23000 1432.79 6176 3098 3078 \n",
"22000 1317.84 6166 3042 3124 \n",
"21000 1258.16 6064 3010 3054 \n",
"20000 1254.75 6032 2996 3036 \n",
"19000 1210.90 6138 3018 3120 \n",
"18000 1052.93 5950 2926 3024 \n",
"17000 1012.31 6060 2988 3072 \n",
"15000 934.62 5898 2870 3028 \n",
"16000 923.44 5918 2902 3016 \n",
"14000 922.92 5854 2880 2974 \n",
"13000 779.87 5804 2852 2952 \n",
"12000 715.08 6146 3018 3128 \n",
"11000 687.11 5880 2876 3004 \n",
"10000 579.16 6010 2912 3098 \n",
"8000 504.51 6006 2914 3092 \n",
"9000 487.08 6156 2992 3164 \n",
"7000 387.35 6008 2904 3104 \n",
"5000 359.80 5924 2856 3068 \n",
"6000 341.27 5982 2860 3122 \n",
"4000 263.29 6234 3018 3216 \n",
"3000 225.60 6516 3216 3300 \n",
"2000 163.61 6318 2904 3414 \n",
"1000 76.26 5620 2188 3432 \n",
"0 34.34 5044 1568 3476 \n"
]
}
],
"source": [
"#从比赛记录中读取\n",
"#每一行是一条记录: \n",
"#黑棋,白棋,胜率,比赛场次\n",
"\n",
"elo=EloRatingSystem()\n",
"elo.addModels(models)\n",
"elo.readRecords('gameRecord.txt')"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x23c9ba99208>]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"import matplotlib\n",
"import matplotlib.pyplot as plt\n",
"plt.plot([int(m) for m in models],[int(r) for r in elo.getRating()])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "py3(myenv)",
"language": "python",
"name": "base"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#----------------------------------------------
# 参考代码:
# https://github.com/sublee/elo
# https://github.com/HankSheehan/EloPy
# 参考资料:
# https://en.wikipedia.org/wiki/Elo_rating_system
# https://en.wikipedia.org/wiki/Go_ranks_and_ratings#Elo-like_rating_systems_as_used_in_Go
# https://gobase.org/studying/articles/elo/
#
# CGOS用的bayesELO的介绍,没用这个,它公式不是很清楚
# http://www.yss-aya.com/cgos/19x19/bayes.html
# https://www.remi-coulom.fr/Bayesian-Elo/
#
#---------------------------------------------
import numpy as np
import random
def main():
models=[str(i) for i in range (0,50000,1000)]
play(models) #模拟5000盘游戏
elo=EloRatingSystem() #初始化
elo.addModels(models) #添加模型
elo.readRecords('gameRecord.txt') #从记录里恢复模型
#测试函数与示例
def play(models,total_game=5000):
elo=EloRatingSystem(recordSaveFile='gameRecord.txt') #初始化
elo.addModels(models) #添加模型
elo.summary() #查看现有模型
for i in range(total_game):
players=elo.getPlayers(sortByRank=True) #选择分数相近的作为比赛对手
black=random.choice(players).name
matchList=elo.getNearPlayer(black)
if matchList:
white=random.choice(matchList)
else:
continue #找不到对手,谁都打不过,或者谁都打不过
num=1 #比赛局数
winrate=pk(black,white,num)
elo.playgame(black,white,winrate,numgames=num) #得到胜率后时用elo.playgame
elo.summary()
#测试用pk函数
def pk(model1,model2,num_games):
winrate=1-0.5**(abs(int(model1)-int(model2))/1000) #假设新模型以一定概率战胜旧模型
res=random.choices([1,0.5,0],weights=[winrate,0,1-winrate],k=1)
if int(model1)>int(model2):
return res[0]
else:
return 1-res[0]
#以上是测试代码
class Player:
def __init__(self,name,rating):
self.name=name
self.rating=rating
self.wins=0
self.losses=0
self.draw=0
self.num_game=0
class EloRatingSystem:
WIN=1
LOSS=0
DRAW=0.5
def __init__(self,base_rating=1500,recordSaveFile='gameRecord.txt'):
self.base_rating=base_rating
self.players={}
self.saveFile=recordSaveFile
self.readFlag=False
def addModel(self,name):
p=Player(name,self.base_rating)
self.players[name]=p
return p
def addModels(self,models):
for m in models:
self.addModel(m)
print('add model to system:{}'.format(models))
def check(self,name,online=True):
if name not in self.players:
if online:
print('{} not in player list,please use <addmodel> to add it'.format(name))
return False
return True
def playgame(self,p1,p2,winrate,numgames=1,online=True):
'''
result [int]:
假设没有平局,赢了算1,输了算0,平局是0.5
'''
self.readFlag=True
if not self.check(p1,online) or not self.check(p2,online):
return
if online:
#只是避免把文件里读出来的再写回去
with open(self.saveFile,'a') as f:
f.write('{},{},{},{}\n'.format(p1,p2,winrate,numgames))
player1=self.players[p1]
player2=self.players[p2]
p1_win=int(numgames*winrate)
p2_win=numgames-p1_win
player1.wins+=p1_win
player2.losses+=p1_win
player2.wins+=p2_win
player1.losses+=p2_win
player1.num_game+=numgames
player2.num_game+=numgames
R1=player1.rating
R2=player2.rating
def expectScore(rating1,rating2):
#base on https://en.wikipedia.org/wiki/Elo_rating_system
#same function as E1=1/(1+10**((rating1-rating2)/400))
Q1=10**(rating1/400)
Q2=10**(rating2/400)
E1=Q1/(Q1+Q2)
E2=Q2/(Q1+Q2)
return E1,E2
def choose_k(rating):
#参考FIDE:https://gobase.org/studying/articles/elo/
#另有EGD规则:http://europeangodatabase.eu/EGD/EGF_rating_system.php
if rating<2000:
return 30
elif rating>2400:
return 10
else:
return 130-rating/20
E1,E2=expectScore(R1,R2)
k=choose_k(R1)
player1.rating=R1+k*(winrate-E1)
k=choose_k(R2)
player2.rating=R2+k*(1-winrate-E2)
# if player1.rating<0:
# player1.rating=0
# elif player2.rating<0:
# player2.rating=0
if online:
#print('{}\'s rating:{}->{} ; {}\'s rating:{}->{}'.format(p1,R1,player1.rating,p2,R2,player2.rating))
pass
def readRecords(self,filename):
'''
读取比赛记录
'''
if self.readFlag:
print('[warning] system is running! will not do anything')
return
self.readFlag=True
with open(filename,'r') as f:
lines=f.readlines()
for l in lines:
record=l.split(',')
self.playgame(record[0],record[1],float(record[2]),int(record[3]),False)
self.summary()
def summary(self):
players=list(self.players.values())
def sortKey(p):
return p.rating
players.sort(key=sortKey,reverse=True)
print('{:<10s}{:<10s}{:<10s}{:<10s}{:<10s}'.format('model','rating','games','wins','losses'))
for p in players:
print('{:<10s}{:<10.2f}{:<10d}{:<10d}{:<10d}'.format(str(p.name),p.rating,p.num_game,p.wins,p.losses))
def getPlayers(self,sortByRank=False):
#默认按模型排序,加入选项可以按分数排序
players=list(self.players.values())
if sortByRank:
def sortKey(p):
return p.rating
players.sort(key=sortKey,reverse=True)
return players
def getRating(self):
#获取分数,按模型输入顺序排,用来画图
return [float(p.rating) for p in list(self.players.values())]
def getNearPlayer(self,name):
#获取同等级的对手,以供匹配,相差200分以内吧
res=[]
if not self.check(name):
return
R=self.players[name].rating
for p in self.players.values():
if abs(p.rating-R)<200 and p.name!=name:
res.append(p.name)
if len(res)==0:
return None
return res
if __name__ == '__main__':
main()
#### 基本类
``` python
class EloRatingSystem:
def __init__(self,base_rating=1500,recordSaveFile='gameRecord.txt')
def addModel(self,name)
def addModels(self,models):
#添加一个/多个模型
def check(self,name,online=True):
#检查模型是否在列表中
def playgame(self,p1,p2,winrate,numgames=1,online=True):
#进行比赛,此过程将更新模型的rating
def readRecords(self,filename):
#读取比赛记录
def summary(self):
#总结整体情况
def getPlayers(self,sortByRank=False):
#返回player类型的列表,默认按模型排序,加入选项可以按分数排序
def getRating(self):
#获取分数,按模型输入顺序排,用来画图
def getNearPlayer(self,name):
#获取同等级的对手,以供匹配,相差200分以内吧
```
#### 公式
详细的见 https://en.wikipedia.org/wiki/Elo_rating_system
A对B的胜率估计如下,Ra,Rb为elo rating
$$
E_A=\frac{1}{1+10^{(R_B-R_A)/400}}
$$
$$
E_B=1-E_A
$$
更新公式,Sa为实际胜率,K为随rating变化的常数,一般在rating较高时较小
$$
R_A'=R_A+K(S_A-E_A)
$$
#### 参考代码:
​ https://github.com/sublee/elo
​ https://github.com/HankSheehan/EloPy
#### 参考资料:
​ https://en.wikipedia.org/wiki/Elo_rating_system
​ https://en.wikipedia.org/wiki/Go_ranks_and_ratings#Elo-like_rating_systems_as_used_in_Go
​ https://gobase.org/studying/articles/elo/
CGOS用的bayesELO的介绍,没用这个,它公式不是很清楚
http://www.yss-aya.com/cgos/19x19/bayes.html
https://www.remi-coulom.fr/Bayesian-Elo/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment