# Elo Rating Calculator

Related

Your task is to calculate the new Elo rating (FIDE rating system) for a player after winning, losing or drawing a game of chess.

To calculate the Elo rating two formulas are needed:

R' = R0 + K*(S - E)
E = 1 / (1 + 10 ^ ((R1 - R0) / 400))


where:

• R' is the new rating for player0,
• R0 is the current rating of player0 and R1 is the current rating of player1,
• S is the score of the game: 1 if player0 wins, 0 if player0 loses or 0.5 if player0 draws,
• K = 40 if the given history has a length < 30, even if it exceeds 2400
• K = 20 if the given history has a length >= 30 and never exceeds 2400 (<2400),
• K = 10 if the given history has a length >= 30 and exceeds 2400 at any point (>=2400)

(if history has a length < 30 but a max value >= 2400 K will equal 40)

## Input

• History of player0's ratings as an array of positive integers greater than 0, where the last item is the players current rating. If no history is given, the current rating will be 1000
• Rating of player1 as an integer
• Score, either 1, 0 or 0.5

## Output

R', A decimal integer of player0's new rating

## Examples

input: [], 1500, 1
K = 40 (length of history is less than 30)
E = 1 / (1 + 10 ^ ((1500 - 1000) / 400)) = 0.0532
R' = 1000 + 40*(1 - 0.0532) = 1038
output: 1038

input: [1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300, 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500, 1525, 1550, 1575, 1600, 1625, 1650, 1675, 1700, 1725], 1000, 0
K = 20 (length of history is more than 30 but never exceeds 2400)
E = 1 / (1 + 10 ^ ((1000 - 1725) / 400)) = 0.9848
R' = 1725 + 20*(0 - 0.9848) = 1705
output: 1705

input: [1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300, 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500, 1525, 1550, 1575, 1600, 1625, 1650, 1800, 2100, 2500], 2200, 0.5
K = 10 (length of history is more than 30 and exceeds 2400)
E = 1 / (1 + 10 ^ ((2200 - 2500) / 400)) = 0.8490
R' = 2500 + 10*(0.5 - 0.8490) = 2497
output: 2497


## Test cases:

[2256,25,1253,1278,443,789], 3999, 0.5 -> 809
[783,1779,495,1817,133,2194,1753,2169,834,521,734,1234,1901,637], 3291, 0.5 -> 657
[190,1267,567,2201,2079,1058,1694,112,780,1182,134,1077,1243,1119,1883,1837], 283, 1 -> 1837
[1665,718,85,1808,2546,2193,868,3514,436,2913,6,654,797,2564,3872,2250,2597,1208,1928,3478,2359,3909,581,3892,1556,1175,2221,3996,3346,238], 2080, 1 -> 248
[1543,2937,2267,556,445,2489,2741,3200,528,964,2346,3467,2831,3746,3824,2770,3207,613,2675,1692,2537,1715,3018,2760,2162,2038,2637,741,1849,41,193,458,2292,222,2997], 3814, 0.5 -> 3002


# Notes

You won't get an input with fewer scores than 30 and a maximum score higher than 2400

This is , so the shortest code in bytes wins!

• I'm assuming len(history)<30 and max(history)>2400 is either impossible, or results in K=40? – Stewie Griffin Jun 2 '17 at 10:03
• @StewieGriffin That is possible and does result in K=40 – Tom Jun 2 '17 at 10:07
• Is it though? In reality? – Stewie Griffin Jun 2 '17 at 10:26
• Can we assume that the player's history will never have 0? – Arjun Jun 2 '17 at 10:28
• @StewieGriffin From my understanding from the official FIDE website I believe it is. – Tom Jun 2 '17 at 10:32

# Octave, 106 bytes

This assumes the history never contains a zero.

@(h,r,s)(h=[1e3,h])(n=nnz(h))+((40-(20*(n>30)))-(10*((n>30)&(max(h)>2401))))*(s-(1/(1+10^((r-h(n))/400))))


This outputs (after some formatting):

Correct - Calculated
1038    - 1037.87
1705    - 1705.3
2497    - 2496.51


Try it online!

Note: According to the FIDE-scoring system: If you play 41 games against a player having a score of 5000 (more than 2000 points higher than the highest achieved score ever), you'll end up with a score of 2400. I believe that fewer scores than 30, and a maximum score higher than 2400 should be considered invalid input (but that's OP's decision).

Check it here

# Python 3, 106 bytes

def f(h,b,s):a=([1e3]+h)[-1];return round(a+((len(h)<30)*4or-~(max(h)<2400))*10*(s-1/(1+10**((b-a)/400))))


Try it online!

without rounding, 98 bytes:

def f(h,b,s):a=([1e3]+h)[-1];return((len(h)<30)*4or-~(max(h)<2400))*10*(s-1/(1+10**((b-a)/400)))+a


Try it online!

# R, 109 bytes

function(p,s,h=1000,l=length(h))round(h[l]+if(l<30,40,if(any(h>2400),10,20))*(s-1/(1+10^((p-h[l])/400))))


Returns an anonymous function that takes several arguments: p, player 1's rating, s, the score of the game, and h, the history. Technically takes another argument l but it defaults to the length of h. h defaults to 1000 so if there is no historical record of games, no value is provided for h.

Try it online!

# JavaScript (ES7), 124 bytes

This is a valid JavaScript anonymous function. Add f= at the beginning and invoke like f(arg1,arg2,arg3). Assumes that the first element of history is not 0 (which is allowed).

(a,r,s)=>(e=a.some(c=>c>2400),a=a[0]?a:[1e3],Math.round((y=a.pop())+((a.length<30?40:(e?20:10))*(s-1/(1+10**((r-y)/400))))))


let f=
(a,r,s)=>(e=a.some(c=>c>2400),a=a[0]?a:[1e3],Math.round((y=a.pop())+((a.length<30?40:(e?20:10))*(s-1/(1+10**((r-y)/400))))))

btnTrigger.onclick = () => {
let a = inputA.value.split,.map(c=>parseInt(c,10)),
r = parseInt(inputR.value,10),
s = parseFloat(inputS.value,10);

console.log(f(a,r,s));
}
<p>Add a Single Comma in History input box for empty History</p>

<input type="text" id="inputA" placeholder="Comma Separated History">
<input type="text" id="inputR" placeholder="Value for R1">
<input type="text" id="inputS" placeholder="Value for S">

<button type="button" id="btnTrigger">Submit</button>

• Your calculations seem to be wrong, for [2256,25,1253,1278,443,789], 3999, 0.5 the output should be 809 – Tom Jun 2 '17 at 11:21
• @Tom Fixed. It was something wrong with Test Snippet (it was parsing 0.5 as 0). And I have added the rounding part too. It really costed me a lot! – Arjun Jun 2 '17 at 11:40
• Nice, looks good now! – Tom Jun 2 '17 at 11:41

# JavaScript, 105 bytes

(h,r,s,u=h.length?h.pop():1e3,k=h.length>28?h.some(v=>v>2400)?10:20:40)=>u+k*s-k/(1+10**((r-u)/400))+.5|0


# oK, 66 bytes

{*|x+_.5+(40-(10*2+|/2399<x)*29<#x)*z-1%1+exp((y-*|x)%400)*log 10}


Try it online.

For regular k, add two bytes by turning log and exp into log and exp respectively.

g h o s=round\$p+k*(s-1/(1+10**((o-p)/400)))
where
p=if(l h<1)then 1000else last h
k=40-(if(l h)>=30then 20else 0)-if(any(>=2400)h)then 10else 0
l=length


g is a function that takes a list h representing the history of the player, o, the opponent points and,s the score of the game.