Skip to content

Commit

Permalink
Mathematical Performance Rating
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristerNilsson committed Oct 28, 2024
1 parent 63c076c commit 922445a
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 140 deletions.
1 change: 0 additions & 1 deletion coffee/globals.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export g = {}

g.EXPONENT = 1.01 # 1 or 1.01 (or 2)
g.COLORS = 1 # 2 ej tillåtet, då kan www eller bbb uppstå.
g.BONUS = 1 # % fördel för svart vid remi. Dubbelt vid vinst. Motsvarande negativt för vit.

###########################################

Expand Down
10 changes: 5 additions & 5 deletions coffee/page_standings.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class Standings extends Page
header += ' ' + g.txtT "Elo", 4, RIGHT
header += ' ' + g.txtT "Name", 25, LEFT
header += '' + g.txtT rheader, 3*@round, LEFT
header += ' ' + g.txtT "Quality", 8, RIGHT
header += ' ' + g.txtT "Perf.rat", 8, RIGHT
# header += ' ' + g.txtT "Score", 5, RIGHT

@playersByPerformance = _.clone @t.playersByID.slice 0,g.N
Expand Down Expand Up @@ -98,8 +98,8 @@ export class Standings extends Page
# s += " = #{g.K}*(#{pa.res[r]/2}-p(#{diff})) p(#{diff})=#{g.F(diff).toFixed(3)}"
# else
s += ' ' + g.txtT chg.toFixed(1), 7, RIGHT
if pa.res[r] == '1' then s += " = 0.5 * (#{g.OFFSET} + #{g.txtT pb.elo, 7, RIGHT})"
if pa.res[r] == '2' then s += " = #{g.OFFSET} + #{g.txtT pb.elo, 7, RIGHT}"
if pa.res[r] == '1' then s += " = 0.5 * (#{g.txtT pb.elo, 7, RIGHT})"
if pa.res[r] == '2' then s += " = #{g.txtT pb.elo, 7, RIGHT}"
#if pa.res[r] == '1' then s += " = 0.5 * #{g.txtT pb.elo, 7, RIGHT}"
#if pa.res[r] == '2' then s += " = #{g.txtT pb.elo, 7, RIGHT}"

Expand Down Expand Up @@ -150,7 +150,7 @@ export class Standings extends Page
header += ' ' + g.txtT "Name", 25, LEFT
for r in range @t.round
header += g.txtT "#{r+1}", 6, RIGHT
header += ' ' + g.txtT "Quality", 8, RIGHT
header += ' ' + g.txtT "Perf.rat", 8, RIGHT

for player,i in @playersByPerformance
if i % @t.ppp == 0 then res.push header
Expand All @@ -164,7 +164,7 @@ export class Standings extends Page
if player.opp[r] == -2 then s += ' P '
if player.opp[r] == -1 then s += ' BYE'
if player.opp[r] >= 0
s += g.txtT "#{1+player.opp[r]}#{g.RINGS[player.col[r][0]]}#{"0½1"[player.res[r]]}", 6, RIGHT
s += g.txtT "#{1+player.opp[r]}#{g.RINGS[player.col[r][0]]}#{"0½1"[player.res[r]]}", 6, RIGHT

s += ' ' + g.txtT player.change(@t.round+1).toFixed(1), 7, RIGHT
# s += ' ' + g.txtT player.perChg(@t.round+1).toFixed(1), 7, RIGHT
Expand Down
74 changes: 52 additions & 22 deletions coffee/player.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { g,print,range,scalex,scaley,SEPARATOR } from './globals.js'

export class Player
constructor : (@id, @name="", @elo="1400", @opp=[], @col="", @res="", @active = true) ->
@cache = {}
# @cache = {}
@pos = [] # one for each round

toggle : ->
Expand All @@ -21,32 +21,36 @@ export class Player
# g.K * (@res[r]/2 - g.F diff)

calcRound1 : (r) ->
if @opp[r] == g.BYE then return @elo + g.OFFSET
if @opp[r] == g.BYE then return @elo
if @opp[r] == g.PAUSE then return 0
if r >= @res.length then return 0
b = g.tournament.playersByID[@opp[r]].elo + g.OFFSET
b = g.tournament.playersByID[@opp[r]].elo
color = @col[r]
if color == 'b' then faktor = 1 + 2 * g.BONUS/100 # t ex 1.02
if color == 'w' then faktor = 1 - 2 * g.BONUS/100 # t ex 0.98
if @res[r] == '2' then return faktor * b # WIN
if @res[r] == '1' then return faktor * b/2 # DRAW
# if color == 'b' then faktor = 1 + 2 * g.BONUS/100 # t ex 1.02
# if color == 'w' then faktor = 1 - 2 * g.BONUS/100 # t ex 0.98
if @res[r] == '2' then return b # WIN
if @res[r] == '1' then return b/2 # DRAW
0 # LOSS

explanation : (r) ->
if @opp[r] == g.BYE then return ""
if @opp[r] == g.PAUSE then return ""
if r >= @res.length then return ""
if @res[r] == '0' then return "0"
b = g.tournament.playersByID[@opp[r]].elo + g.OFFSET
color = @col[r]
if color == 'b' then faktor = 1 + 2 * g.BONUS/100 # t ex 1.02
if color == 'w' then faktor = 1 - 2 * g.BONUS/100 # t ex 0.98
if @res[r] == '2' then faktor /= 1 # WIN
if @res[r] == '1' then faktor /= 2 # DRAW
result = faktor * b
"#{result.toFixed(2)} = #{faktor} * (#{g.OFFSET} + #{g.tournament.playersByID[@opp[r]].elo})"

# performance : (r) ->
res = ['Loss','Draw','Win'][@res[r]]
opp = g.tournament.playersByID[@opp[r]]
col = if @col[r]=='w' then 'white' else 'black'
"#{res} against #{opp.elo} #{opp.name} as #{col}"
# if r >= @res.length then return ""
# if @res[r] == '0' then return "0"
# b = g.tournament.playersByID[@opp[r]].elo
# color = @col[r]
# # if color == 'b' then faktor = 1 + 2 * g.BONUS/100 # t ex 1.02
# # if color == 'w' then faktor = 1 - 2 * g.BONUS/100 # t ex 0.98
# if @res[r] == '2' then faktor = 1.0 # WIN
# if @res[r] == '1' then faktor = 0.5 # DRAW
# result = faktor * b
# "#{result.toFixed(2)} = #{faktor} * #{g.tournament.playersByID[@opp[r]].elo}"

# linear_performance : (r) ->
# if @opp[r] == g.BYE then return @elo + 400
# if @opp[r] == g.PAUSE then return 0
# if r >= @res.length then return 0
Expand All @@ -55,16 +59,42 @@ export class Player
# if @res[r] == '1' then return b # DRAW
# if @res[r] == '0' then return b - 400 # LOSS

expected_score : (ratings, own_rating) ->
g.sum(1 / (1 + 10**((rating - own_rating) / 400)) for rating in ratings)

performance_rating : (ratings, score) ->
lo = 0
hi = 4000
while hi - lo > 0.001
rating = (lo + hi) / 2
if score > @expected_score ratings, rating
lo = rating
else
hi = rating
rating

performance : ->
score = 0
ratings = []
for r in range @res.length
if @opp[r] == g.BYE then continue
if @opp[r] == g.PAUSE then continue
score += @res[r]/2
ratings.push g.tournament.playersByID[@opp[r]].elo
@performance_rating(ratings,score)

calcRound : (r) ->
# if g.FACTOR == 0 then @calcRound0 r else @calcRound1 r
1 * @calcRound1 r

change : (rounds) ->
if rounds of @cache then return @cache[rounds]
@cache[rounds] = g.sum (@calcRound r for r in range rounds)
# if rounds of @cache then return @cache[rounds]
# @cache[rounds] = g.sum (@calcRound r for r in range rounds)
# g.sum (@calcRound r for r in range rounds)

# perChg : (rounds) -> # https://en.wikipedia.org/wiki/Performance_rating_(chess)
# g.sum(@performance r for r in range rounds)/(rounds-1)
#g.sum(@performance r for r in range rounds)/(rounds-1)
@performance() # r for r in range rounds)/(rounds-1)

score : (rounds) -> g.sum (parseInt @res[r] for r in range rounds-1)
# result = 0
Expand Down
30 changes: 10 additions & 20 deletions coffee/tournament.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ KEYWORDS.PAUSED = '!-separated integers'
KEYWORDS.TPP = 'integer (Tables Per Page, default: 30)'
KEYWORDS.PPP = 'integer (Players Per Page, default: 60)'
# KEYWORDS.K = 'integer (default: 20)'
KEYWORDS.FACTOR = 'float, 0 or larger than 1.2 (default: 2)'
KEYWORDS.BONUS = 'integer (percent for black player)'

export class Tournament
constructor : ->
Expand Down Expand Up @@ -225,8 +223,6 @@ export class Tournament
print 'TPP',@tpp
print 'PPP',@ppp
print 'PAUSED',@paused
print 'FACTOR',g.FACTOR
print 'BONUS',g.BONUS
# print 'PLAYERS'
# for p in @persons
# print ' ', p.id, p.elo, p.name, p.opp, p.col, p.res
Expand All @@ -250,8 +246,6 @@ export class Tournament
hash.PPP = 60
hash.PAUSED = ""
#hash.K = 2
hash.FACTOR = 2 # default
hash.BONUS = 1 # %

for line,nr in data
line = line.trim()
Expand Down Expand Up @@ -286,8 +280,6 @@ export class Tournament
@ppp = parseInt hash.PPP # Players Per Page
@paused = hash.PAUSED # list of zero based ids
# g.K = parseInt hash.K # 40, 20 or 10 normally
g.FACTOR = parseFloat hash.FACTOR
g.BONUS = parseInt hash.BONUS

players = hash.PLAYERS
g.N = players.length
Expand Down Expand Up @@ -318,16 +310,16 @@ export class Tournament
if a.elo != b.elo then return b.elo - a.elo
if a.name > b.name then 1 else -1

if g.FACTOR == 0
g.OFFSET = 0
print 'g.OFFSET',g.OFFSET
else
if g.FACTOR < 1.2 then g.FACTOR = 1.2
XMAX = @playersByELO[0].elo
XMIN = _.last(@playersByELO).elo
g.OFFSET = (XMAX - XMIN) / (g.FACTOR - 1) - XMIN
g.OFFSET = Math.round g.OFFSET
print 'XMIN,XMAX,g.OFFSET',XMIN,XMAX,g.OFFSET
# if g.FACTOR == 0
# g.OFFSET = 0
# print 'g.OFFSET',g.OFFSET
# else
# if g.FACTOR < 1.2 then g.FACTOR = 1.2
# XMAX = @playersByELO[0].elo
# XMIN = _.last(@playersByELO).elo
# g.OFFSET = (XMAX - XMIN) / (g.FACTOR - 1) - XMIN
# g.OFFSET = Math.round g.OFFSET
# print 'XMIN,XMAX,g.OFFSET',XMIN,XMAX,g.OFFSET

print 'playersByELO', @playersByELO

Expand Down Expand Up @@ -380,8 +372,6 @@ export class Tournament
res.push "TITLE=" + @title
res.push "DATE=" + @datum
#res.push "K=" + g.K
res.push "FACTOR=" + g.FACTOR
res.push "BONUS=" + g.BONUS
res.push "TPP=" + @tpp
res.push "PPP=" + @ppp
res.push "PAUSED=" + @makePaused()
Expand Down
Loading

0 comments on commit 922445a

Please # to comment.