# ELO Ratings in CoffeeScript

ELO is a method of assigning a relative proficiency rating to a team or individual after recording the result of set of matches. This link explains the process in the context of a soccer league.

Below is code to update the ELO rating of a team based on a game result and a short sample random tournament.

# the expected value of a result for team is
# calculated by taking the difference in elo ratings
# applying a home field advantage
expected = (home_rating, away_rating) ->
dr = (home_rating + 100) - away_rating
1 / (Math.pow(10, -1 * (dr / 400)) + 1)

# the weight of the match is increased for lop-sided wins and losses
modified_weight = (weight, gf, ga) ->
gd = Math.abs gf - ga
return weight * 1.5 if gd is 2
return weight * 1.75 if gd is 3
return weight * 1.75 + (gd - 3) / 8 if gd > 3
weight

# actual result defined as 1 for a win, 0 for a loss and 0.5 for a draw
result = (gf, ga) ->
return 1 if gf > ga
return 0 if gf < ga
0.5

# return a two element list containing the modified elo
# for the home and away team
elo = (home_rating, away_rating, weight, gf, ga) ->
weight = modified_weight weight, gf, ga
home_exp = expected home_rating, away_rating
away_exp = 1 - home_exp
home_delta = parseInt weight * (result(gf, ga) - home_exp)
away_delta = parseInt weight * (result(ga, gf) - away_exp)
[home_rating + home_delta, away_rating + away_delta]

Tournament

# game result description
description = (us, them) ->
return "Won #{us}-#{them}" if us > them
return "Lost #{us}-#{them}" if us < them
"Draw #{us}-#{them}"

# construct a team
team = (idx) ->
initial_elo = parseInt(Math.random() * 200 + 1400)
t =
name: "team #{idx}"
elo: initial_elo
history: [initial_elo]
games: []

# build a set of teams
teams = (team(idx) for idx in [0..2])

console.log teams

# run a tournament
for team1, i1 in teams
for team2, i2 in teams
if i1 isnt i2
t1s = parseInt(Math.random() * 4)
t2s = parseInt(Math.random() * 4)
[team1_elo, team2_elo] = elo team1.elo, team2.elo, 30, t1s, t2s
team1.history.push team1_elo
team1.games.push description t1s, t2s
team1.elo = team1_elo
team2.history.push team2_elo
team2.games.push description t2s, t1s
team2.elo = team2_elo

console.log teams

Output

# initial random teams
[ { name: 'team 0', elo: 1478, history: [ 1478 ], games: [] },
{ name: 'team 1', elo: 1563, history: [ 1563 ], games: [] },
{ name: 'team 2', elo: 1468, history: [ 1468 ], games: [] } ]

# after the tournament
[ { name: 'team 0',
elo: 1468,
history: [ 1478, 1478, 1474, 1463, 1468 ],
games: [ 'Draw 3-3', 'Draw 0-0', 'Lost 1-3', 'Draw 2-2' ] },
{ name: 'team 1',
elo: 1532,
history: [ 1563, 1563, 1574, 1552, 1532 ],
games: [ 'Draw 3-3', 'Won 3-1', 'Lost 0-1', 'Lost 0-2' ] },
{ name: 'team 2',
elo: 1509,
history: [ 1468, 1472, 1494, 1489, 1509 ],
games: [ 'Draw 0-0', 'Won 1-0', 'Draw 2-2', 'Won 2-0' ] } ]