Commit 95fa4b2c by ziho

Initial commit

parents
This diff is collapsed. Click to expand it.
#!/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