Last active
April 23, 2022 08:04
-
-
Save jchros/73ab7d6b5551639ab6f23c54e7ce5082 to your computer and use it in GitHub Desktop.
A Common Lisp library for the Elo ranking system.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defpackage :elo | |
(:use :cl)) | |
(in-package :elo) | |
(defvar *expected-outcome-fun* nil | |
"This is used to supply an alternative function for | |
calculating the expected outcome of a game (whose | |
arguments are the Elo scores of the two players, and | |
returns a number between 0 and 1). | |
You shouldn't change this unless you know what you're | |
doing.") | |
(declaim (type (or null (function (real real) (real 0 1))) | |
*expected-outcome-fun*)) | |
(defun expected-outcome (player-elo opponent-elo) | |
"The default function used for determining the expected | |
outcome of a game." | |
(check-type player-elo real) | |
(check-type opponent-elo real) | |
(the (real 0 1) | |
(/ (1+ (expt 10 (/ (- opponent-elo player-elo) 400)))))) | |
(defun elo-change (player opponent outcome magnitude) | |
"Calculates the number of Elo points gained or lost by | |
PLAYER after a game against OPPONENT with the given | |
OUTCOME." | |
(check-type player real) | |
(check-type opponent real) | |
(check-type outcome (real 0 1)) | |
(check-type magnitude (real (0))) | |
(* magnitude | |
(- outcome (funcall (or *expected-outcome-fun* #'expected-outcome) | |
player opponent)))) | |
(defun game | |
(player keyword opponent &key (outcome nil outcomep) (magnitude nil magnitudep)) | |
"Returns the Elo scores of PLAYER and OPPONENT after a game. | |
The KEYWORD argument can be any of :BEATS, :DRAWS, | |
:LOSES-TO, or :VS. | |
When the KEYWORD argument is :VS, the :OUTCOME is used to | |
specify the outcome of the game and must be provided, | |
otherwise it is ignored. | |
The :MAGNITUDE argument is mandatory." | |
(assert (shiftf magnitudep t) | |
(magnitude) "The :MAGNITUDE argument was not provided.") | |
(setf outcome | |
(ccase keyword | |
(:vs (assert (shiftf outcomep t) | |
(outcome) "The :OUTCOME argument was not provided.") | |
outcome) | |
(:beats 1) | |
(:draws 1/2) | |
(:loses-to 0))) | |
(when (and (not (eql keyword :vs)) outcomep) | |
(warn "The :OUTCOME argument has been ignored.")) | |
(let ((elo-change (elo-change player opponent outcome magnitude))) | |
(values (+ player elo-change) (- opponent elo-change)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment