commit 63f8b0a13c846fb7ac1d7a81e64180bfde0622bc Author: swaits Date: Mon May 9 22:53:48 2005 +0000 did some renaming of main dirs (apps, libs, etc.) diff --git a/cpp/glicko2.cpp b/cpp/glicko2.cpp new file mode 100644 index 0000000..5f1e431 --- /dev/null +++ b/cpp/glicko2.cpp @@ -0,0 +1,351 @@ +/* + + Copyright (c) 2004 Stephen Waits + + This software is provided 'as-is', without any express or implied warranty. In + no event will the authors be held liable for any damages arising from the use + of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + + + +#include "glicko2.h" + +#include +#include + + + +class Glicko2_impl +{ +public: + + // constructors + Glicko2_impl(); + Glicko2_impl(const Glicko2_impl& rhs); + + // copy assignment + Glicko2_impl& operator=(const Glicko2_impl& rhs); + + // destructor + virtual ~Glicko2_impl(); + + // system constants + static const double dvolatility; + + // rating data + double rating; + double deviation; + double volatility; + + // result data (copy of each opponent and results, 0.0, 0.5, or 1.0) + std::vector opponents; + std::vector results; + + // utility functions + double g(const double& deviation); + double E(const double& rating, const double& rating_opponent, const double& deviation_opponent); +}; + + + +const double Glicko2_impl::dvolatility = 0.3; // should be [0.3,1.2] + + + +Glicko2_impl::Glicko2_impl() : + rating(0.0), + deviation(0.0), + volatility(0.0), + opponents(), + results() +{ +} + + + +Glicko2_impl::Glicko2_impl(const Glicko2_impl& rhs) : + rating(rhs.rating), + deviation(rhs.deviation), + volatility(rhs.volatility), + opponents(rhs.opponents), + results(rhs.results) +{ +} + + + +Glicko2_impl& Glicko2_impl::operator=(const Glicko2_impl& rhs) +{ + if ( this == &rhs ) + { + return *this; + } + + rating = rhs.rating; + deviation = rhs.deviation; + volatility = rhs.volatility; + opponents = rhs.opponents; + results = rhs.results; + + return *this; +} + + + +Glicko2_impl::~Glicko2_impl() +{ +} + + + +double Glicko2_impl::g(const double& deviation) +{ + #define PI_SQUARED (9.86960440108935861883) + return 1.0 / (sqrt(1.0 + 3.0 * deviation * deviation / PI_SQUARED)); +} + + + +double Glicko2_impl::E(const double& rating, const double& rating_opponent, const double& deviation_opponent) +{ + return 1.0 / (1.0 + exp(-g(deviation_opponent)*(rating - rating_opponent))); +} + + + + + + +Glicko2::Glicko2() : + pimpl(0) +{ + pimpl = new Glicko2_impl(); + + SetRating(1500.0); + SetDeviation(350.0); + SetVolatility(0.06); +} + + + +Glicko2::Glicko2(const Glicko2& rhs) : + pimpl(0) +{ + pimpl = new Glicko2_impl(*(rhs.pimpl)); +} + + + +Glicko2::Glicko2(double rating, double deviation, double volatility) : + pimpl(0) +{ + pimpl = new Glicko2_impl; + + SetRating(rating); + SetDeviation(deviation); + SetVolatility(volatility); +} + + + +Glicko2& Glicko2::operator=(const Glicko2& rhs) +{ + if ( this == &rhs ) + { + return *this; + } + + *pimpl = *rhs.pimpl; + + return *this; +} + + + +Glicko2::~Glicko2() +{ + delete pimpl; +} + + + +bool Glicko2::operator< (const Glicko2& rhs) +{ + return pimpl->rating < rhs.pimpl->rating; +} + + + +double Glicko2::GetRating() const +{ + return pimpl->rating * 173.7178 + 1500.0; +} + + + +double Glicko2::GetDeviation() const +{ + return pimpl->deviation * 173.7178; +} + + + +double Glicko2::GetVolatility() const +{ + return pimpl->volatility; +} + + + +void Glicko2::SetRating(double rating) +{ + pimpl->rating = (rating - 1500.0) / 173.7178; +} + + + +void Glicko2::SetDeviation(double deviation) +{ + pimpl->deviation = deviation / 173.7178; +} + + + +void Glicko2::SetVolatility(double volatility) +{ + pimpl->volatility = volatility; +} + + + + +void Glicko2::ClearResults() +{ + pimpl->opponents.clear(); + pimpl->results.clear(); +} + + + +void Glicko2::AddResult(const Glicko2& opponent, RESULT result) +{ + pimpl->opponents.push_back(opponent); + + switch ( result ) + { + case WIN: + pimpl->results.push_back(1.0); + break; + + case LOSS: + pimpl->results.push_back(0.0); + break; + + case DRAW: + pimpl->results.push_back(0.5); + break; + } +} + + + +void Glicko2::AddWin(const Glicko2& opponent) +{ + AddResult(opponent,WIN); +} + + + +void Glicko2::AddLoss(const Glicko2& opponent) +{ + AddResult(opponent,LOSS); +} + + + +void Glicko2::AddDraw(const Glicko2& opponent) +{ + AddResult(opponent,DRAW); +} + + + +void Glicko2::Update() +{ + // bail if no opponents set + if ( pimpl->opponents.size() == 0 ) + { + return; + } + + // compute variance + double variance = 0.0; + for ( unsigned int i=0;iopponents.size();i++ ) + { + double g_i = pimpl->g(pimpl->opponents[i].pimpl->deviation); + double E_i = pimpl->E(pimpl->rating,pimpl->opponents[i].pimpl->rating,pimpl->opponents[i].pimpl->deviation); + variance += g_i * g_i * E_i * (1.0 - E_i); + } + variance = 1.0 / variance; + + // compute delta + double delta = 0.0; + for ( unsigned int i=0;iopponents.size();i++ ) + { + delta += pimpl->g(pimpl->opponents[i].pimpl->deviation) * (pimpl->results[i] - pimpl->E(pimpl->rating,pimpl->opponents[i].pimpl->rating,pimpl->opponents[i].pimpl->deviation)); + } + delta *= variance; + + // determine new volatility + double new_volatility = 0.0; + double a = log(pimpl->volatility*pimpl->volatility); + double x = 0.0; + double x_new = a; + while ( abs(x - x_new) > 0.0000001 ) + { + x = x_new; + double d = pimpl->deviation*pimpl->deviation + variance + exp(x); + double h1 = -(x - a)/(Glicko2_impl::dvolatility*Glicko2_impl::dvolatility) - 0.5*exp(x)/d + 0.5*exp(x)*(delta/d)*(delta/d); + double h2 = -1.0/(Glicko2_impl::dvolatility*Glicko2_impl::dvolatility) - 0.5*exp(x)*(pimpl->deviation*pimpl->deviation+variance)/(d*d) + 0.5*(delta*delta)*exp(x)*((pimpl->deviation*pimpl->deviation) + variance - exp(x))/(d*d*d); + x_new = x - h1/h2; + } + new_volatility = exp(x_new / 2.0); + + // update the rating deviation to the new pre-rating period value + double pre_deviation = sqrt( pimpl->deviation*pimpl->deviation + new_volatility*new_volatility ); + + // update the rating and deviation + double new_deviation = 1.0 / (sqrt( 1.0/(pre_deviation*pre_deviation) + 1.0 / variance)); + double new_rating = 0.0; + for ( unsigned int i=0;iopponents.size();i++ ) + { + new_rating += pimpl->g(pimpl->opponents[i].pimpl->deviation) * (pimpl->results[i] - pimpl->E(pimpl->rating,pimpl->opponents[i].pimpl->rating,pimpl->opponents[i].pimpl->deviation)); + } + new_rating = new_rating * new_deviation * new_deviation; + new_rating += pimpl->rating; + + // copy new values + pimpl->rating = new_rating; + pimpl->deviation = new_deviation; + pimpl->volatility = new_volatility; + + // wipe our result lists + ClearResults(); +} + diff --git a/cpp/glicko2.h b/cpp/glicko2.h new file mode 100644 index 0000000..889fc77 --- /dev/null +++ b/cpp/glicko2.h @@ -0,0 +1,245 @@ +/* + + Copyright (c) 2004 Stephen Waits + + This software is provided 'as-is', without any express or implied warranty. In + no event will the authors be held liable for any damages arising from the use + of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + + + +#ifndef __glicko2_h__ +#define __glicko2_h__ + + + +class Glicko2_impl; + + + +/** + * Glicko-2 Rating calculator class. + * + * This class implements the Glicko-2 rating system algorithm written by + * Professor Mark E. Glickman. All rating inputs and outputs are Glicko ratings, + * but internally everything is converted to and considered Glicko-2 ratings. + * + * Glicko-2 is an improvement on the original Glicko, which was, in turn, an + * improvement on the ELO system. + * + * The Glicko-2 system is specified on http://www.glicko.com/ + */ +class Glicko2 +{ +public: + + + + /** + * Enumeration of possible results. + */ + enum RESULT + { + /** + * A win result, internally 1.0. + */ + WIN, + + /** + * A loss result, internally 0.0. + */ + LOSS, + + /** + * A draw result, internally 0.5. + */ + DRAW + }; + + + + /** + * Default constructor. Initializes to a rating of 1500, a rating deviation + * of 350, and a volatility of 0.06. + */ + Glicko2(); + + /** + * Copy constructor. + * + * @param rhs Object to copy. + */ + Glicko2(const Glicko2& rhs); + + /** + * Constructor with rating, rating deviation, and volatility specified. + * + * @param rating Initial rating. + * @param deviation Initial rating deviation. + * @param volatility Initial volatility. + */ + Glicko2(double rating, double deviation, double volatility); + + + + /** + * Copy assignment operator. + * + * @param rhs Object to copy. + * + * @return Reference to self after copy assignment. + */ + Glicko2& operator=(const Glicko2& rhs); + + + + /** + * Destructor. + */ + virtual ~Glicko2(); + + + + /** + * Less than comparison operator. + * + * @param rhs Object to compare against. + * + * @return true if this object's rating is less than rhs's rating; false otherwise. + */ + bool operator< (const Glicko2& rhs); + + + + /** + * Get the current rating. This rating is a Glicko rating, not a Glicko2 rating. + * For details, please see http://www.glicko.com/ + * + * @return + */ + double GetRating() const; + + /** + * Get the current rating deviation. This is a Glicko rating deviation, not a + * Glicko-2 RD. For details, please see http://www.glicko.com/ + * + * @return + */ + double GetDeviation() const; + + /** + * Get the current rating volatility. + * + * @return + */ + double GetVolatility() const; + + + + /** + * Set Glicko rating. Internally this is converted to a Glicko2 rating. + * For details, please see http://www.glicko.com/ + * + * @param rating Rating. + */ + void SetRating(double rating); + + /** + * Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD. + * For details, please see http://www.glicko.com/ + * + * @param deviation Rating deviation. + */ + void SetDeviation(double deviation); + + /** + * Set rating volatility. + * + * @param volatility Rating volatility. + */ + void SetVolatility(double volatility); + + + + /** + * Clear all results previously added via AddResult(), AddWin(), AddLoss(), + * and/or AddDraw(). This method is called automatically whenever Update() + * is called. + */ + void ClearResults(); + + + + /** + * Add a result to this rating. Note that no calculation is performed until + * Update() is called. + * + * @param opponent Other player in contest. + * @param result WIN, LOSS, or DRAW; from the point of view of this player. + */ + void AddResult(const Glicko2& opponent, RESULT result); + + + + /** + * Add a win result to this rating. Note that no calculation is performed until + * Update() is called. + * + * @param opponent Other (losing) player in contest. + */ + void AddWin(const Glicko2& opponent); + + /** + * Add a loss result to this rating. Note that no calculation is performed until + * Update() is called. + * + * @param opponent Other (winning) player in contest. + */ + void AddLoss(const Glicko2& opponent); + + /** + * Add a draw result to this rating. Note that no calculation is performed until + * Update() is called. + * + * @param opponent Other (drawing) player in contest. + */ + void AddDraw(const Glicko2& opponent); + + + + /** + * Update rating based on current results list, and clear results. + */ + void Update(); + + + +private: + + /** + * Private Implementation. + */ + Glicko2_impl* pimpl; + +}; + + + +#endif // __glicko2_h__ + diff --git a/cpp/main.cpp b/cpp/main.cpp new file mode 100644 index 0000000..c387f4f --- /dev/null +++ b/cpp/main.cpp @@ -0,0 +1,25 @@ +#include "glicko2.h" + +#include + +int main() +{ + // Glicko-2 Example + // calculation from http://www.glicko.com/glicko2.doc/example.html + + Glicko2 A(1500.0, 200.0, 0.06); + Glicko2 B(1400.0, 30.0, 0.06); + Glicko2 C(1550.0, 100.0, 0.06); + Glicko2 D(1700.0, 300.0, 0.06); + + A.AddWin(B); + A.AddLoss(C); + A.AddResult(D,Glicko2::LOSS); // alternative way to add a result + + A.Update(); + + printf("rating = %f, RD = %f\n", A.GetRating(), A.GetDeviation()); + + return 0; +} + diff --git a/py/data/amasuperbike-main.xml b/py/data/amasuperbike-main.xml new file mode 100644 index 0000000..982e87a --- /dev/null +++ b/py/data/amasuperbike-main.xml @@ -0,0 +1,464 @@ + + + + + + + Mathew Mladin + Jake P Zemke + Miguel Duhamel + Jack E Pfeifer + Lee Acree + Opie Caylor + Scott Jensen + Eric C Wood + Cory Denton West + John Haner + J J Roetlin + Geoff May + Aaron W Yates + Dean Mizdal + David Bell + Eric Bostrom + Larry Pegram + Ben D Bostrom + Joshua Kurt Hayes + Jason Curtis + Shawn M Higbee + + + + + + + + Mathew Mladin + Jake P Zemke + Miguel Duhamel + Jack E Pfeifer + Lee Acree + Opie Caylor + Scott Jensen + Eric C Wood + Cory Denton West + John Haner + J J Roetlin + Geoff May + David Bell + Aaron W Yates + Dean Mizdal + Eric Bostrom + Larry Pegram + Ben D Bostrom + Joshua Kurt Hayes + + + + + + + Mathew Mladin + Eric Bostrom + Miguel Duhamel + Jake P Zemke + Geoff May + Larry Pegram + John Haner + Opie Caylor + Lee Acree + Cory Denton West + Scott Jensen + Eric C Wood + Jason Curtis + Dean Mizdal + Joshua Kurt Hayes + J J Roetlin + David Bell + Jack E Pfeifer + Ben D Bostrom + Shawn M Higbee + + + + + + + Mathew Mladin + Eric Bostrom + Jake P Zemke + Miguel Duhamel + Ben D Bostrom + Geoff May + Larry Pegram + Lee Acree + Eric C Wood + Scott Jensen + Cory Denton West + John Haner + Jason Curtis + David Bell + Dean Mizdal + J J Roetlin + Jack E Pfeifer + Joshua Kurt Hayes + Shawn M Higbee + Opie Caylor + + + + + + + Mathew Mladin + Miguel Duhamel + Jake P Zemke + Ben D Bostrom + Eric Bostrom + Joshua Kurt Hayes + Larry Pegram + Geoff May + Aaron W Yates + Lee Acree + Opie Caylor + John Haner + Cory Denton West + Jack E Pfeifer + David Bell + Dean Mizdal + J J Roetlin + Scott Jensen + Shawn M Higbee + Jason Curtis + Eric C Wood + + + + + + + Miguel Duhamel + Jake P Zemke + Aaron W Yates + Mathew Mladin + Ben D Bostrom + Joshua Kurt Hayes + Larry Pegram + Geoff May + Shawn M Higbee + Lee Acree + Eric C Wood + John Haner + David Bell + Cory Denton West + Jack E Pfeifer + Jason Curtis + Dean Mizdal + J J Roetlin + Scott Jensen + Eric Bostrom + Opie Caylor + + + + + + + Mathew Mladin + Jake P Zemke + Miguel Duhamel + Eric Bostrom + Joshua Kurt Hayes + John Haner + Geoff May + Eric C Wood + Shawn M Higbee + Larry Pegram + Cory Denton West + David Bell + J J Roetlin + Lee Acree + Dean Mizdal + Jack E Pfeifer + Jason Curtis + Opie Caylor + Scott Jensen + Aaron W Yates + Ben D Bostrom + + + + + + + Miguel Duhamel + Jake P Zemke + Mathew Mladin + Aaron W Yates + Ben D Bostrom + Joshua Kurt Hayes + Eric Bostrom + John Haner + Geoff May + Shawn M Higbee + Lee Acree + Opie Caylor + Cory Denton West + J J Roetlin + David Bell + Jack E Pfeifer + Jason Curtis + Eric C Wood + Larry Pegram + Dean Mizdal + Scott Jensen + + + + + + + Eric Bostrom + Jake P Zemke + Aaron W Yates + Miguel Duhamel + Ben D Bostrom + Mathew Mladin + Joshua Kurt Hayes + Shawn M Higbee + Geoff May + Lee Acree + J J Roetlin + Eric C Wood + John Haner + Cory Denton West + Opie Caylor + David Bell + Dean Mizdal + Jack E Pfeifer + Larry Pegram + Jason Curtis + + + + + + + Miguel Duhamel + Mathew Mladin + Jake P Zemke + Ben D Bostrom + Eric Bostrom + Aaron W Yates + Geoff May + Shawn M Higbee + Eric C Wood + Opie Caylor + John Haner + Dean Mizdal + Cory Denton West + David Bell + Scott Jensen + Jack E Pfeifer + Jason Curtis + J J Roetlin + Larry Pegram + Joshua Kurt Hayes + Lee Acree + + + + + + + Miguel Duhamel + Jake P Zemke + Mathew Mladin + Aaron W Yates + Ben D Bostrom + Eric Bostrom + Larry Pegram + Geoff May + Eric C Wood + John Haner + Shawn M Higbee + Opie Caylor + J J Roetlin + Cory Denton West + Dean Mizdal + Jack E Pfeifer + Scott Jensen + Jason Curtis + Joshua Kurt Hayes + Lee Acree + David Bell + + + + + + + Jake P Zemke + Miguel Duhamel + Mathew Mladin + Eric Bostrom + Ben D Bostrom + Joshua Kurt Hayes + Aaron W Yates + Geoff May + Eric C Wood + Larry Pegram + Cory Denton West + John Haner + J J Roetlin + Dean Mizdal + Jason Curtis + Shawn M Higbee + Scott Jensen + David Bell + Jack E Pfeifer + + + + + + + Ben D Bostrom + Mathew Mladin + Miguel Duhamel + Aaron W Yates + Eric Bostrom + Joshua Kurt Hayes + Jake P Zemke + Larry Pegram + Geoff May + John Haner + Eric C Wood + Shawn M Higbee + Lee Acree + J J Roetlin + Scott Jensen + Opie Caylor + Cory Denton West + David Bell + Jason Curtis + Dean Mizdal + Jack E Pfeifer + + + + + + + Mathew Mladin + Miguel Duhamel + Aaron W Yates + Ben D Bostrom + Jake P Zemke + Joshua Kurt Hayes + Eric Bostrom + John Haner + Larry Pegram + Shawn M Higbee + Geoff May + Eric C Wood + Cory Denton West + Lee Acree + Opie Caylor + J J Roetlin + Jason Curtis + Scott Jensen + Dean Mizdal + Jack E Pfeifer + + + + + + + Jake P Zemke + Ben D Bostrom + Mathew Mladin + Aaron W Yates + Joshua Kurt Hayes + Eric Bostrom + Larry Pegram + Eric C Wood + John Haner + Lee Acree + Shawn M Higbee + Geoff May + Cory Denton West + J J Roetlin + Scott Jensen + Opie Caylor + Jason Curtis + Dean Mizdal + Jack E Pfeifer + Miguel Duhamel + + + + + + + Mathew Mladin + Miguel Duhamel + Ben D Bostrom + Jake P Zemke + Aaron W Yates + John Haner + Geoff May + Eric C Wood + Shawn M Higbee + Opie Caylor + J J Roetlin + Cory Denton West + Dean Mizdal + Joshua Kurt Hayes + Larry Pegram + Lee Acree + Eric Bostrom + Jason Curtis + + + + + + + Mathew Mladin + Miguel Duhamel + Ben D Bostrom + Jake P Zemke + Joshua Kurt Hayes + John Haner + Larry Pegram + Eric C Wood + Geoff May + Shawn M Higbee + Lee Acree + Opie Caylor + Cory Denton West + Dean Mizdal + Jason Curtis + J J Roetlin + Aaron W Yates + Eric Bostrom + + + + diff --git a/py/data/amasuperbike.xml b/py/data/amasuperbike.xml new file mode 100644 index 0000000..e7cf762 --- /dev/null +++ b/py/data/amasuperbike.xml @@ -0,0 +1,729 @@ + + + + + + + Mathew Mladin + Jake P Zemke + Miguel Duhamel + Jack E Pfeifer + Lee Acree + Ricky Orlando + Pascal Picotte + Opie Caylor + Scott Jensen + Eric C Wood + Marco Martinez + C R Gittere + Cory Denton West + Frank Trombino + John Haner + J J Roetlin + Geoff May + David Bell + Roger Bell + Craig Fitzpatrick + Byron Barbour + Rick R Narup + Aaron W Yates + Dean Mizdal + Anthony W Fania + Eric Bostrom + Larry Pegram + K Mark Crozier + Mike Sullivan + Ben D Bostrom + Ron McGill + Jeffrey S Tigert + Joshua Kurt Hayes + Michael A Hanley + Scott E Carpenter + Jordan M Szoke + John Dugan + Steve Crevier + Martin Craggill + Clint McBain + Jeremy Toye + Chris Siglin + Robert M Christman + Jason Curtis + Mark Ledesma + Garry Combs + Kim Nakashima + Corey Sarros + Kevin Hanson + Jeremiah J Johnson + Jeff Williams + Rob Mesa + Craig Connell + Shawn M Higbee + A J Ammann + David Stanton + Ken Hill + Mark C Foster + Brien K Whitlock + James Randolph + James J King + Andrew Nelson + John Scott Wilson + Thomas G Montano + Kenyon Kluge + Mike Krynock + Michael J Sanchez + John R Jacobi + Lenny Beckman + Michael A Smith + Christian Pistoni + Daniel L Turner + Darren Luck + Andrew J Deatherage + Reuben Frankenfield + Chris M Voelker + Brian Boyd + James Kerker + Paul Ducato + Dave Ebben + David Weber + Jacob L Holden + Jason R Pridmore + Mark Vandal + Roger M Hendricks + Greg J Fryer + Corey D Eaton + Dave Kunzelman + + Fritz Kling + Jeff Muskopf + Kevin W Pate + Chuck Allen + Charles Sorensen + Jesse A Janisch + + + + + + + Mathew Mladin + Jake P Zemke + Miguel Duhamel + Jack E Pfeifer + Lee Acree + Ricky Orlando + Pascal Picotte + Opie Caylor + Scott Jensen + Eric C Wood + Marco Martinez + C R Gittere + Cory Denton West + Frank Trombino + John Haner + J J Roetlin + Geoff May + David Bell + Roger Bell + Craig Fitzpatrick + Byron Barbour + Rick R Narup + Aaron W Yates + Dean Mizdal + Anthony W Fania + Eric Bostrom + Larry Pegram + K Mark Crozier + Mike Sullivan + Ben D Bostrom + Ron McGill + Jeffrey S Tigert + Joshua Kurt Hayes + Michael A Hanley + Scott E Carpenter + Jordan M Szoke + John Dugan + + + + + + + Mathew Mladin + Eric Bostrom + Miguel Duhamel + Jake P Zemke + Geoff May + Steve Crevier + Larry Pegram + Martin Craggill + John Haner + Opie Caylor + Clint McBain + Lee Acree + Jeremy Toye + Cory Denton West + Scott Jensen + Eric C Wood + Chris Siglin + Robert M Christman + Jason Curtis + Dean Mizdal + Joshua Kurt Hayes + J J Roetlin + Mark Ledesma + David Bell + Garry Combs + Jack E Pfeifer + Kim Nakashima + Corey Sarros + Kevin Hanson + Jeremiah J Johnson + Ben D Bostrom + Jeff Williams + Rob Mesa + Craig Connell + Shawn M Higbee + A J Ammann + + + + + + + Mathew Mladin + Eric Bostrom + Jake P Zemke + Miguel Duhamel + Ben D Bostrom + Steve Crevier + Geoff May + Larry Pegram + Lee Acree + Eric C Wood + Jeremy Toye + Scott Jensen + Clint McBain + Cory Denton West + Jeff Williams + John Haner + Jason Curtis + David Bell + Dean Mizdal + Rob Mesa + Mark Ledesma + J J Roetlin + Corey Sarros + Chris Siglin + Garry Combs + Jack E Pfeifer + Jeremiah J Johnson + Kevin Hanson + Joshua Kurt Hayes + Shawn M Higbee + Craig Connell + Kim Nakashima + Martin Craggill + Robert M Christman + Opie Caylor + A J Ammann + + + + + + + Mathew Mladin + Miguel Duhamel + Jake P Zemke + Ben D Bostrom + Eric Bostrom + Joshua Kurt Hayes + David Stanton + Larry Pegram + Geoff May + Aaron W Yates + Ken Hill + Clint McBain + Lee Acree + Opie Caylor + John Haner + Robert M Christman + Mark C Foster + Brien K Whitlock + Cory Denton West + James Randolph + Jack E Pfeifer + Garry Combs + Rob Mesa + Jeff Williams + David Bell + Andrew Nelson + Dean Mizdal + J J Roetlin + John Scott Wilson + Jeremiah J Johnson + Steve Crevier + James J King + Kim Nakashima + Chris Siglin + Craig Connell + Scott Jensen + Shawn M Higbee + Thomas G Montano + Jason Curtis + Kenyon Kluge + Kevin Hanson + Eric C Wood + Jeremy Toye + Mike Krynock + + + + + + + Miguel Duhamel + Jake P Zemke + Aaron W Yates + Mathew Mladin + Ben D Bostrom + Steve Crevier + Joshua Kurt Hayes + Larry Pegram + Geoff May + Shawn M Higbee + Lee Acree + Clint McBain + Ken Hill + Eric C Wood + Robert M Christman + John Haner + David Bell + Cory Denton West + Jack E Pfeifer + Thomas G Montano + Brien K Whitlock + Jason Curtis + Kim Nakashima + Jeremiah J Johnson + Dean Mizdal + J J Roetlin + John Scott Wilson + Andrew Nelson + Garry Combs + James J King + Kevin Hanson + Scott Jensen + Chris Siglin + David Stanton + Eric Bostrom + James Randolph + Jeff Williams + Kenyon Kluge + Jeremy Toye + Mark C Foster + Opie Caylor + Craig Connell + Rob Mesa + Mike Krynock + + + + + + + Mathew Mladin + Jake P Zemke + Miguel Duhamel + Eric Bostrom + Joshua Kurt Hayes + John Haner + Geoff May + Jeremy Toye + Eric C Wood + Shawn M Higbee + Larry Pegram + Byron Barbour + Michael J Sanchez + Cory Denton West + David Bell + J J Roetlin + Lee Acree + Mike Sullivan + Dean Mizdal + Jack E Pfeifer + John R Jacobi + Jason Curtis + Opie Caylor + Marco Martinez + Mark Ledesma + Scott Jensen + Scott E Carpenter + Lenny Beckman + C R Gittere + Kevin Hanson + Rick R Narup + Michael A Smith + Aaron W Yates + Jeremiah J Johnson + Ben D Bostrom + Christian Pistoni + + + + + + + Miguel Duhamel + Jake P Zemke + Mathew Mladin + Aaron W Yates + Ben D Bostrom + Joshua Kurt Hayes + Eric Bostrom + John Haner + Geoff May + Jeremy Toye + Shawn M Higbee + Michael A Smith + Byron Barbour + Lee Acree + Opie Caylor + Cory Denton West + J J Roetlin + Mike Sullivan + David Bell + Marco Martinez + Mark Ledesma + John R Jacobi + Jack E Pfeifer + Jason Curtis + Eric C Wood + Lenny Beckman + Scott E Carpenter + Kevin Hanson + Rick R Narup + C R Gittere + Larry Pegram + Jeremiah J Johnson + Dean Mizdal + Scott Jensen + Michael J Sanchez + Christian Pistoni + + + + + + + Eric Bostrom + Jake P Zemke + Aaron W Yates + Miguel Duhamel + Ben D Bostrom + Mathew Mladin + Joshua Kurt Hayes + Shawn M Higbee + Ricky Orlando + Geoff May + Lee Acree + J J Roetlin + Daniel L Turner + Eric C Wood + John Haner + Darren Luck + Mike Sullivan + Cory Denton West + Opie Caylor + Robert M Christman + Mark Ledesma + David Bell + Dean Mizdal + Lenny Beckman + Jack E Pfeifer + Kevin Hanson + Larry Pegram + Jason Curtis + + + + + + + Miguel Duhamel + Mathew Mladin + Jake P Zemke + Ben D Bostrom + Eric Bostrom + Steve Crevier + Aaron W Yates + Geoff May + Shawn M Higbee + Eric C Wood + Jeremy Toye + Opie Caylor + Andrew J Deatherage + Pascal Picotte + John Haner + Roger Bell + Dean Mizdal + Cory Denton West + John R Jacobi + David Bell + Mike Sullivan + Reuben Frankenfield + Chris M Voelker + Robert M Christman + C R Gittere + Scott Jensen + Jack E Pfeifer + Jason Curtis + Jeremiah J Johnson + Brian Boyd + James Kerker + Lenny Beckman + Kevin Hanson + Paul Ducato + J J Roetlin + Dave Ebben + David Weber + Larry Pegram + Joshua Kurt Hayes + Lee Acree + Marco Martinez + Jacob L Holden + Jason R Pridmore + + + + + + + Miguel Duhamel + Jake P Zemke + Mathew Mladin + Aaron W Yates + Ben D Bostrom + Eric Bostrom + Steve Crevier + Larry Pegram + Geoff May + Eric C Wood + Jeremy Toye + John Haner + Shawn M Higbee + Opie Caylor + Andrew J Deatherage + J J Roetlin + Cory Denton West + David Weber + Dean Mizdal + Reuben Frankenfield + John R Jacobi + Jack E Pfeifer + Scott Jensen + Chris M Voelker + Robert M Christman + C R Gittere + Jason Curtis + Kevin Hanson + Dave Ebben + Lenny Beckman + Brian Boyd + James Kerker + Joshua Kurt Hayes + Roger Bell + Jacob L Holden + Pascal Picotte + Lee Acree + David Bell + Mike Sullivan + Jeremiah J Johnson + Jason R Pridmore + Marco Martinez + Paul Ducato + + + + + + + Jake P Zemke + Miguel Duhamel + Mathew Mladin + Eric Bostrom + Ben D Bostrom + Joshua Kurt Hayes + Aaron W Yates + Pascal Picotte + Jacob L Holden + Geoff May + Eric C Wood + Larry Pegram + Cory Denton West + John Haner + J J Roetlin + Andrew J Deatherage + Dean Mizdal + Jason Curtis + Shawn M Higbee + Mark Vandal + James Kerker + Scott Jensen + David Bell + Roger M Hendricks + Jeremy Toye + Greg J Fryer + Jack E Pfeifer + Jason R Pridmore + C R Gittere + A J Ammann + + + + + + + Ben D Bostrom + Mathew Mladin + Miguel Duhamel + Aaron W Yates + Eric Bostrom + Joshua Kurt Hayes + Jake P Zemke + Jacob L Holden + Larry Pegram + Geoff May + Jeremy Toye + John Haner + Eric C Wood + Shawn M Higbee + Martin Craggill + Chris Siglin + Lee Acree + J J Roetlin + Mike Sullivan + Mark Ledesma + Ken Hill + Marco Martinez + Ricky Orlando + Scott Jensen + Opie Caylor + Thomas G Montano + Brien K Whitlock + Kim Nakashima + Corey D Eaton + Rob Mesa + Dave Kunzelman + Garry Combs + Cory Denton West + Jeffrey S Tigert + David Bell + Jason Curtis + Dean Mizdal + Jack E Pfeifer + + + + + + + Mathew Mladin + Miguel Duhamel + Aaron W Yates + Ben D Bostrom + Jake P Zemke + Joshua Kurt Hayes + Eric Bostrom + Jacob L Holden + John Haner + Larry Pegram + Shawn M Higbee + Geoff May + Eric C Wood + Martin Craggill + Cory Denton West + Lee Acree + Opie Caylor + J J Roetlin + Andrew J Deatherage + Jason Curtis + Scott Jensen + Dean Mizdal + Fritz Kling + Paul Ducato + Byron Barbour + Jack E Pfeifer + Jeff Muskopf + Kevin W Pate + Roger Bell + Chuck Allen + C R Gittere + Charles Sorensen + David Weber + James Kerker + Jesse A Janisch + John R Jacobi + Jason R Pridmore + + + + + + + Jake P Zemke + Ben D Bostrom + Mathew Mladin + Aaron W Yates + Joshua Kurt Hayes + Jacob L Holden + Eric Bostrom + Larry Pegram + Eric C Wood + John Haner + Lee Acree + Shawn M Higbee + Geoff May + Andrew J Deatherage + Cory Denton West + David Weber + J J Roetlin + Jesse A Janisch + Scott Jensen + Opie Caylor + Jason Curtis + Byron Barbour + Dean Mizdal + C R Gittere + Paul Ducato + Jeff Muskopf + Jack E Pfeifer + Charles Sorensen + Miguel Duhamel + Fritz Kling + Chuck Allen + James Kerker + Roger Bell + Martin Craggill + John R Jacobi + Kevin W Pate + Jason R Pridmore + + + + diff --git a/py/data/hockey-1.xml b/py/data/hockey-1.xml new file mode 100644 index 0000000..ffb1d2d --- /dev/null +++ b/py/data/hockey-1.xml @@ -0,0 +1,403 @@ + + + + + + penguins + + + + no ties + + + + zzyzzx + + + + spitfires + + + + red liners + + + + space monkeys + + + + phantoms + + + + jinx + + + + plug uglys + + + + shockers + + + + + + jinx + space monkeys + win + + + + zzyzzx + no ties + loss + + + + plug uglys + red liners + loss + + + + phantoms + penguins + loss + + + + + + no ties + spitfires + loss + + + + jinx + phantoms + loss + + + + space monkeys + shockers + win + + + + zzyzzx + plug uglys + win + + + + penguins + red liners + win + + + + + + penguins + zzyzzx + loss + + + + spitfires + plug uglys + win + + + + no ties + shockers + win + + + + red liners + jinx + win + + + + space monkeys + phantoms + draw + + + + + + + jinx + zzyzzx + loss + + + + red liners + space monkeys + loss + + + + plug uglys + no ties + loss + + + + spitfires + penguins + win + + + + phantoms + shockers + win + + + + + + + + shockers + plug uglys + draw + + + + spitfires + jinx + win + + + + penguins + no ties + loss + + + + red liners + phantoms + win + + + + space monkeys + zzyzzx + loss + + + + + + + + plug uglys + penguins + draw + + + + spitfires + space monkeys + draw + + + + shockers + red liners + loss + + + + phantoms + zzyzzx + loss + + + + no ties + jinx + loss + + + + + + + + phantoms + spitfires + loss + + + + plug uglys + jinx + win + + + + no ties + space monkeys + loss + + + + shockers + penguins + loss + + + + zzyzzx + red liners + loss + + + + + + + + space monkeys + plug uglys + win + + + + penguins + jinx + win + + + + red liners + spitfires + loss + + + + zzyzzx + shockers + win + + + + phantoms + no ties + draw + + + + + + + + red liners + no ties + win + + + + zzyzzx + spitfires + win + + + + space monkeys + penguins + win + + + + plug uglys + phantoms + loss + + + + jinx + shockers + draw + + + + + + + + spitfires + shockers + win + + + + no ties + zzyzzx + draw + + + + space monkeys + jinx + win + + + + red liners + plug uglys + win + + + + + + + + spitfires + shockers + win + + + + + + + + red liners + phantoms + win + + + + red liners + space monkeys + win + + + + spitfires + phantoms + loss + + + + + + + + diff --git a/py/data/hockey-2.xml b/py/data/hockey-2.xml new file mode 100644 index 0000000..780bb47 --- /dev/null +++ b/py/data/hockey-2.xml @@ -0,0 +1,422 @@ + + + + + + penguins + 1458.9 + 145.9 + 0.06 + + + + no ties + 1474.4 + 141.2 + 0.06 + + + + zzyzzx + 1747.3 + 146.8 + 0.06 + + + + spitfires + 1688.8 + 152.5 + 0.06 + + + + space monkeys + 1666.4 + 132.5 + 0.06 + + + + plug uglys + 1247.4 + 149.5 + 0.06 + + + + shockers + 1117.6 + 162.1 + 0.06 + + + + phantoms + 1527.9 + 134.9 + 0.06 + + + + buk hockey + + + + battle apples + + + + + + + shockers + plug uglys + win + + + + spitfires + penguins + win + + + + zzyzzx + space monkeys + win + + + + no ties + buk hockey + loss + + + + + + + buk hockey + space monkeys + draw + + + + spitfires + zzyzzx + win + + + + plug uglys + phantoms + loss + + + + shockers + no ties + loss + + + + penguins + battle apples + win + + + + + + + buk hockey + spitfires + loss + + + + plug uglys + no ties + loss + + + + space monkeys + shockers + win + + + + battle apples + zzyzzx + loss + + + + penguins + phantoms + loss + + + + + + + zzyzzx + penguins + win + + + + battle apples + buk hockey + loss + + + + shockers + spitfires + loss + + + + space monkeys + plug uglys + win + + + + no ties + phantoms + loss + + + + + + + battle apples + plug uglys + loss + + + + penguins + shockers + loss + + + + no ties + spitfires + win + + + + phantoms + space monkeys + draw + + + + zzyzzx + buk hockey + win + + + + + + + phantoms + buk hockey + draw + + + + zzyzzx + shockers + win + + + + penguins + plug uglys + win + + + + no ties + battle apples + win + + + + spitfires + space monkeys + win + + + + + + + shockers + phantoms + loss + + + + zzyzzx + no ties + win + + + + plug uglys + buk hockey + loss + + + + spitfires + battle apples + win + + + + space monkeys + penguins + win + + + + + + + penguins + spitfires + loss + + + + buk hockey + no ties + win + + + + space monkeys + zzyzzx + win + + + + battle apples + phantoms + loss + + + + plug uglys + shockers + loss + + + + + + + plug uglys + spitfires + loss + + + + phantoms + zzyzzx + win + + + + battle apples + shockers + win + + + + space monkeys + no ties + win + + + + buk hockey + penguins + loss + + + + + + + space monkeys + battle apples + loss + + + + no ties + penguins + win + + + + buk hockey + shockers + win + + + + spitfires + phantoms + win + + + + plug uglys + zzyzzx + loss + + + + + + + phantoms + no ties + win + + + + space monkeys + buk hockey + win + + + + spitfires + battle apples + win + + + + zzyzzx + penguins + loss + + + + + diff --git a/py/data/hockey-2005.xml b/py/data/hockey-2005.xml new file mode 100644 index 0000000..1b98c1e --- /dev/null +++ b/py/data/hockey-2005.xml @@ -0,0 +1,503 @@ + + + + + + no ties + 1472.6 + 100.3 + 0.06 + + + + zzyzzx + 1657.5 + 106.0 + 0.06 + + + + penguins + 1436.3 + 103.2 + 0.06 + + + + spitfires + 1801.5 + 112.3 + 0.06 + + + + space monkeys + 1659.8 + 98.1 + 0.06 + + + + plug uglys + 1125.4 + 113.1 + 0.06 + + + + shockers + 1196.3 + 117.0 + 0.06 + + + + phantoms + 1652.6 + 98.6 + 0.06 + + + + battle apples + 1302.6 + 156.9 + 0.06 + + + + blackouts + 1531.3 + 121.4 + 0.06 + + + + coaches team + + + + old school + + + + ballistic + + + + velocity + + + + puck heads + + + + demons + + + + section 8 + + + + who's your daddy + + + + blackhawks + + + + tiger sharks + + + + + phantoms + spitfires + loss + + + battle apples + blackouts + loss + + + zzyzzx + penguins + loss + + + plug uglys + space monkeys + loss + + + no ties + coaches team + win + + + + + + spitfires + no ties + draw + + + penguins + blackouts + win + + + zzyzzx + plug uglys + win + + + space monkeys + old school + win + + + coaches team + shockers + win + + + phantoms + battle apples + win + + + + + + space monkeys + zzyzzx + win + + + coaches team + battle apples + win + + + plug uglys + phantoms + loss + + + shockers + spitfires + loss + + + blackouts + old school + win + + + no ties + penguins + loss + + + + + + old school + phantoms + loss + + + plug uglys + no ties + loss + + + shockers + zzyzzx + loss + + + blackouts + space monkeys + win + + + coaches team + penguins + loss + + + spitfires + battle apples + win + + + + + + battle apples + shockers + loss + + + zzyzzx + blackouts + win + + + space monkeys + phantoms + loss + + + no ties + old school + win + + + penguins + spitfires + loss + + + coaches team + plug uglys + win + + + + + + no ties + space monkeys + win + + + old school + coaches team + win + + + penguins + battle apples + win + + + plug uglys + spitfires + draw + + + phantoms + zzyzzx + loss + + + shockers + blackouts + win + + + + + + blackouts + phantoms + loss + + + penguins + shockers + win + + + zzyzzx + no ties + win + + + space monkeys + coaches team + loss + + + battle apples + plug uglys + loss + + + spitfires + old school + win + + + + + + phantoms + shockers + win + + + blackouts + no ties + loss + + + battle apples + old school + win + + + plug uglys + penguins + loss + + + space monkeys + spitfires + draw + + + zzyzzx + coaches team + win + + + + + + spitfires + zzyzzx + loss + + + space monkeys + battle apples + win + + + no ties + phantoms + loss + + + coaches team + blackouts + loss + + + shockers + plug uglys + loss + + + old school + penguins + loss + + + + + + old school + shockers + win + + + + + + + spitfires + coaches team + win + + + battle apples + no ties + loss + + + old school + zzyzzx + loss + + + penguins + phantoms + loss + + + blackouts + plug uglys + win + + + shockers + space monkeys + loss + + + + + + + shockers + ballistic + loss + + + blackhawks + puck heads + win + + + section 8 + velocity + draw + + + demons + spitfires + draw + + + who's your daddy + tiger sharks + draw + + + + + + + no ties + penguins + loss + + + phantoms + space monkeys + loss + + + zzyzzx + coaches team + loss + + + spitfires + blackouts + win + + + + + diff --git a/py/example.xml b/py/example.xml new file mode 100644 index 0000000..8c8ec61 --- /dev/null +++ b/py/example.xml @@ -0,0 +1,56 @@ + + + + + + + A + 1500 + 200 + 0.06 + + + + B + 1400 + 30 + 0.06 + + + + C + 1550 + 100 + 0.06 + + + + D + 1700 + 300 + 0.06 + + + + + + A + B + win + + + + A + C + loss + + + + A + D + loss + + + + + diff --git a/py/glicko2.py b/py/glicko2.py new file mode 100644 index 0000000..b47061d --- /dev/null +++ b/py/glicko2.py @@ -0,0 +1,240 @@ +# +# +# Copyright (c) 2004 Stephen Waits +# +# This software is provided 'as-is', without any express or implied warranty. In +# no event will the authors be held liable for any damages arising from the use +# of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it freely, +# subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not claim +# that you wrote the original software. If you use this software in a +# product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# +# 3. This notice may not be removed or altered from any source distribution. +# +# + + +import math +import copy + + +class Glicko2: + """ + Glicko-2 Rating calculator class. + + This class implements the Glicko-2 rating system algorithm written by + Professor Mark E. Glickman. All rating inputs and outputs are Glicko ratings, + but internally everything is converted to and considered Glicko-2 ratings. + + Glicko-2 is an improvement on the original Glicko, which was, in turn, an + improvement on the ELO system. + + The Glicko-2 system is specified on http://www.glicko.com/ + """ + + WIN = 1.0 + LOSS = 0.0 + DRAW = 0.5 + + dvolatility = 0.3 + + def __init__ (self, rating = 1500.0, deviation = 350.0, volatility = 0.06): + """ + Constructor with rating, rating deviation, and volatility optionally + specified. If nothing specified, nitializes to a rating of 1500, a + rating deviation of 350, and a volatility of 0.06. + """ + + # creat state vars + self.__rating = 0.0 + self.__deviation = 0.0 + self.__volatility = 0.0 + + # create empty result lists + self.__opponents = [] + self.__results = [] + + # initialize state + self.SetRating(rating) + self.SetDeviation(deviation) + self.SetVolatility(volatility) + + def __cmp__(self, other): + """Comparison operator""" + return cmp( self.__rating, other.__rating ) + + + def GetRating (self): + """ + Get the current rating. This rating is a Glicko rating, not a Glicko2 rating. + For details, please see http://www.glicko.com/ + """ + return (self.__rating * 173.7178) + 1500.0 + + def GetDeviation (self): + """ + Get the current rating deviation. This is a Glicko rating deviation, not a + Glicko-2 RD. For details, please see http://www.glicko.com/ + """ + return self.__deviation * 173.7178 + + def GetVolatility (self): + """ + Get the current rating volatility. + """ + return self.__volatility + + def SetRating (self, rating): + """ + Set Glicko rating. Internally this is converted to a Glicko2 rating. + For details, please see http://www.glicko.com/ + """ + self.__rating = (rating - 1500.0) / 173.7178 + + def SetDeviation(self, deviation): + """ + Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD. + For details, please see http://www.glicko.com/ + """ + self.__deviation = deviation / 173.7178 + + def SetVolatility(self, volatility): + """ + Set rating volatility. + """ + self.__volatility = volatility + + def ClearResults (self): + """ + Clear all results previously added via AddResult(), AddWin(), AddLoss(), + and/or AddDraw(). This method is called automatically whenever Update() + is called. + """ + self.__opponents = [] + self.__results = [] + + def AddResult (self, opponent, result): + """ + Add a result to this rating. Note that no calculation is performed until + Update() is called. + """ + self.__opponents.append(copy.copy(opponent)) + self.__results.append(copy.copy(result)) # copy probably not needed + + def AddWin (self, opponent): + """ + Add a win result to this rating. Note that no calculation is performed until + Update() is called. + """ + self.AddResult(opponent, self.WIN) + + def AddLoss (self, opponent): + """ + Add a loss result to this rating. Note that no calculation is performed until + Update() is called. + """ + self.AddResult(opponent, self.LOSS) + + def AddDraw (self, opponent): + """ + Add a draw result to this rating. Note that no calculation is performed until + Update() is called. + """ + self.AddResult(opponent, self.DRAW) + + def Update (self): + """ + Update rating based on current results list, and clear results. + """ + + # util func + def g (deviation): + """ + Internal utility function. + """ + return 1.0 / (math.sqrt(1.0 + 3.0 * deviation * deviation / (math.pi * math.pi))); + + # util func + def E (rating, rating_opponent, deviation_opponent): + """ + Internal utility function. + """ + return 1.0 / (1.0 + math.exp(-g(deviation_opponent)*(rating - rating_opponent))); + + # bail if no opponents set + if len(self.__opponents) == 0: + return + + # compute variance + variance = 0.0 + for opp in self.__opponents: + g_i = g(opp.__deviation) + E_i = E(self.__rating,opp.__rating,opp.__deviation) + variance += g_i * g_i * E_i * (1.0 - E_i) + variance = 1.0 / variance + + # compute delta + delta = 0.0 + for i in range(len(self.__opponents)): + delta += g(self.__opponents[i].__deviation) * (self.__results[i] - E(self.__rating, self.__opponents[i].__rating, self.__opponents[i].__deviation)) + delta *= variance + + # determine new volatility + new_volatility = 0.0 + a = math.log( math.pow(self.__volatility,2.0) ) + x = 0.0 + x_new = a + while ( abs(x - x_new) > 0.0000001 ): + x = x_new; + d = math.pow(self.__deviation,2.0) + variance + math.exp(x) + h1 = -(x - a)/(math.pow(self.dvolatility,2.0)) - 0.5*math.exp(x)/d + 0.5*math.exp(x)*(delta/d)*(delta/d) + h2 = -1.0/(math.pow(self.dvolatility,2.0)) - 0.5*math.exp(x)*(math.pow(self.__deviation,2.0)+variance)/(pow(d,2.0)) + 0.5*(math.pow(delta,2.0))*math.exp(x)*((math.pow(self.__deviation,2.0)) + variance - math.exp(x))/(pow(d,3.0)) + x_new = x - h1/h2 + new_volatility = math.exp(x_new / 2.0) + + # update the rating deviation to the new pre-rating period value + pre_deviation = math.sqrt( math.pow(self.__deviation,2.0) + math.pow(new_volatility,2.0) ) + + # update the rating and deviation + new_deviation = 1.0 / (math.sqrt( 1.0/(pow(pre_deviation,2.0)) + 1.0 / variance)) + new_rating = 0.0 + for i in range(len(self.__opponents)): + new_rating += g(self.__opponents[i].__deviation) * (self.__results[i] - E(self.__rating, self.__opponents[i].__rating, self.__opponents[i].__deviation)) + new_rating = new_rating * pow(new_deviation,2.0) + new_rating += self.__rating + + # copy new values + self.__rating = new_rating + self.__deviation = new_deviation + self.__volatility = new_volatility + + # wipe our result lists + self.ClearResults() + + + +# test code +if __name__ == "__main__": + + A = Glicko2(1500.0, 200.0, 0.06) + B = Glicko2(1400.0, 30.0, 0.06) + C = Glicko2(1550.0, 100.0, 0.06) + D = Glicko2(1700.0, 300.0, 0.06) + + A.AddWin(B) + A.AddLoss(C) + A.AddResult(D,Glicko2.LOSS) + + A.Update(); + + print "rating",A.GetRating(), "deviation",A.GetDeviation() + diff --git a/py/xmlrate.py b/py/xmlrate.py new file mode 100755 index 0000000..ded2bcf --- /dev/null +++ b/py/xmlrate.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python + +import sys +import glicko2 +import xml.dom.minidom + +# globals +gPlayers = dict() +gTeams = dict() + + +def getText(nodelist): + rc = "" + for node in nodelist: + if node.nodeType == node.TEXT_NODE: + rc = rc + node.data + return rc + + +def parsePlayers (docroot): + + # get players + playerelements = docroot.getElementsByTagName("player") + + # parse each player + for player in playerelements: + + # get the name + namenode = player.getElementsByTagName("name") + assert namenode + name = getText(namenode[0].childNodes) + + # setup default rating data + g2 = glicko2.Glicko2() + + # look for rating + ratingnode = player.getElementsByTagName("rating") + if ratingnode: + g2.SetRating( float(getText(ratingnode[0].childNodes)) ) + + # look for deviation + deviationnode = player.getElementsByTagName("deviation") + if deviationnode: + g2.SetDeviation( float(getText(deviationnode[0].childNodes)) ) + + # look for volatility + volatilitynode = player.getElementsByTagName("volatility") + if volatilitynode: + g2.SetVolatility( float(getText(volatilitynode[0].childNodes)) ) + + # add to dictionary + gPlayers[name] = g2 + + +def parseTeams (docroot): + + # get teams + teamelements = docroot.getElementsByTagName("team") + + # parse each team + for team in teamelements: + + # get the name + namenode = team.getElementsByTagName("name") + assert namenode + name = getText(namenode[0].childNodes) + + # add name to our dictionary + gTeams[name] = [] + + # look for members, add to dictionary + membernode = team.getElementsByTagName("member") + assert membernode # makes certain team is not empty + for member in membernode: + gTeams[name].append( getText(member.childNodes) ) + + +def outputPlayers(): + sorted = [(v, k) for k, v in gPlayers.items()] + sorted.sort() + sorted.reverse() # so largest is first + sorted = [(k, v) for v, k in sorted] + i = 0 + for k,v in sorted: + i += 1 + print "%3d%30s: %6.1f +/- %5.1f %0.05f" % \ + ( i, k, v.GetRating(), v.GetDeviation(), v.GetVolatility() ) + + + + +def handlePeriods (docroot): + + # our period count + periodcount = 0 + + # get all periods + periodelements = docroot.getElementsByTagName("period") + + # handle each period + for period in periodelements: + + # track period id + periodcount += 1 + print + print "after rating period",periodcount + + # deal with either a race or a game node + for node in period.childNodes: + if node.nodeType == node.ELEMENT_NODE: + if node.tagName == "race": + handleRace(node) + elif node.tagName == "game": + handleGame(node) + + # all games entered for this period, now update all players ratings + for k,v in gPlayers.items(): + v.Update() + + # sort players for output + outputPlayers() + + +def handleRace (race): + + # get competitors + competitornodes = race.getElementsByTagName("competitor") + + # a list of our racers + racelist = [] + + for competitor in competitornodes: + + # get name from XML + competitorname = getText(competitor.childNodes) + + # look name up in our team list + if gTeams.has_key(competitorname): + competitornames = gTeams[competitorname] + else: + competitornames = [competitorname] + + # make all of these competitors losers to everyone already in the list + for plist in racelist: + for pl in plist: + for co in competitornames: + gPlayers[pl].AddWin(gPlayers[co]) + gPlayers[co].AddLoss(gPlayers[pl]) + + # now let's add this list to our race list + racelist.append( competitornames ) + + + +def handleGame (game): + + # get home + homenode = game.getElementsByTagName("home") + assert homenode + home = getText(homenode[0].childNodes) + + # get away + awaynode = game.getElementsByTagName("away") + assert awaynode + away = getText(awaynode[0].childNodes) + + # get result + resultnode = game.getElementsByTagName("result") + assert resultnode + result = getText(resultnode[0].childNodes) + assert result == "win" or result == "loss" or result == "draw" + + # see if home is a team + if gTeams.has_key(home): + homeplayernames = gTeams[home] + else: + homeplayernames = [home] + + # see if away is a team + if gTeams.has_key(away): + awayplayernames = gTeams[away] + else: + awayplayernames = [away] + + # determine each team's result + if result == "win": + homeresult = glicko2.Glicko2.WIN + awayresult = glicko2.Glicko2.LOSS + elif result == "loss": + homeresult = glicko2.Glicko2.LOSS + awayresult = glicko2.Glicko2.WIN + elif result == "draw": + homeresult = glicko2.Glicko2.DRAW + awayresult = glicko2.Glicko2.DRAW + + # now let's add all the results to the Glicko2 objects + for hname in homeplayernames: + for aname in awayplayernames: + gPlayers[hname].AddResult( gPlayers[aname], homeresult ) + for aname in awayplayernames: + for hname in homeplayernames: + gPlayers[aname].AddResult( gPlayers[hname], awayresult ) + + + + + + +def main (): + + # parse input document + docroot = xml.dom.minidom.parse(sys.argv[1]) + + # build player & team list + parsePlayers(docroot) + parseTeams(docroot) + + # show initial ratings + print "initial ratings" + outputPlayers() + + # process all the periods + handlePeriods(docroot) + + +if __name__ == '__main__': + main() + diff --git a/py/xmlrate.xsd b/py/xmlrate.xsd new file mode 100644 index 0000000..d01a440 --- /dev/null +++ b/py/xmlrate.xsd @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rb/glicko2.rb b/rb/glicko2.rb new file mode 100755 index 0000000..8fc2f83 --- /dev/null +++ b/rb/glicko2.rb @@ -0,0 +1,230 @@ +#!/usr/bin/env ruby + +# +# +# Copyright (c) 2004 Stephen Waits +# +# This software is provided 'as-is', without any express or implied warranty. In +# no event will the authors be held liable for any damages arising from the use +# of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it freely, +# subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not claim +# that you wrote the original software. If you use this software in a +# product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# +# 3. This notice may not be removed or altered from any source distribution. +# +# + + +# Glicko-2 Rating calculator class. +# +# This class implements the Glicko-2 rating system algorithm written by +# Professor Mark E. Glickman. All rating inputs and outputs are Glicko ratings, +# but internally everything is converted to and considered Glicko-2 ratings. +# +# Glicko-2 is an improvement on the original Glicko, which was, in turn, an +# improvement on the ELO system. +# +# The Glicko-2 system is specified on http://www.glicko.com/ +# +class Glicko2 + + protected + + # Glicko2 reprensentation (Glicko2 uses different scales from Glicko) + attr_accessor :g2rating, :g2deviation, :g2volatility + + # a nested Class to hold a single result + Result = Struct.new(:opponent, :result) + + public + + WIN = 1.0 + LOSS = 0.0 + DRAW = 0.5 + + # Constructor with rating, rating deviation, and volatility optionally + # specified. If nothing specified, nitializes to a rating of 1500, a + # rating deviation of 350, and a volatility of 0.06. + def initialize(rating = 1500.0, deviation = 350.0, volatility = 0.06) + @dvolatility = 0.3 + self.rating = rating + self.deviation = deviation + self.volatility = volatility + clear_results + end + + include Comparable + + # Comparison operator + def <=>(other) + self.rating <=> other.rating + end + + # Get the current rating. This rating is a Glicko rating, not a Glicko2 rating. + # For details, please see http://www.glicko.com/ + def rating + (@g2rating * 173.7178) + 1500.0 + end + + # Set Glicko rating. Internally this is converted to a Glicko2 rating. + # For details, please see http://www.glicko.com/ + def rating=(r) + @g2rating = (r - 1500.0) / 173.7178 + end + + # Get the current rating deviation. This is a Glicko rating deviation, not a + # Glicko-2 RD. For details, please see http://www.glicko.com/ + def deviation + @g2deviation * 173.7178 + end + + # Set Glicko rating deviation. Internally this is converted to a Glicko-2 RD. + # For details, please see http://www.glicko.com/ + def deviation=(d) + @g2deviation = d / 173.7178 + end + + # Get the current rating volatility. + def volatility + @g2volatility + end + + # Set rating volatility. + def volatility=(v) + @g2volatility = v + end + + # Clear all results previously added via add_result(), add_win(), add_loss(), + # and/or add_draw(). This method is called automatically whenever update() + # is called. + def clear_results + @opponents = [] + @results = [] + end + + # Add a result to this rating. Note that no calculation is performed until + # update() is called. + def add_result(opponent,result) + @results.push( Result.new(opponent.clone, result) ) + end + + # Add a win result to this rating. Note that no calculation is performed until + # update() is called. + def add_win(opponent) + add_result(opponent,Glicko2::WIN) + end + + # Add a loss result to this rating. Note that no calculation is performed until + # update() is called. + def add_loss(opponent) + add_result(opponent,Glicko2::LOSS) + end + + # Add a draw result to this rating. Note that no calculation is performed until + # update() is called. + def add_draw(opponent) + add_result(opponent,Glicko2::DRAW) + end + + # Update rating based on current results list, and clear results. + def update + + # util func + def Glicko2.g(deviation) + 1.0 / (Math.sqrt(1.0 + 3.0 * deviation ** 2.0 / (Math::PI ** 2.0))) + end + + # util func + def Glicko2.E(rating, rating_opponent, deviation_opponent) + 1.0 / (1.0 + Math.exp(-Glicko2.g(deviation_opponent)*(rating - rating_opponent))); + end + + # bail if no opponents set + if @results.empty? + return nil + end + + # compute variance + + #variance = 0.0 + #@results.inject(0.0) do |variance,r| + # g_i = Glicko2.g(r.opponent.g2deviation) + # e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation) + # g_i ** 2.0 * e_i * (1.0 - e_i) + #end + + variance = 0.0 + @results.each do |r| + g_i = Glicko2.g(r.opponent.g2deviation) + e_i = Glicko2.E(@g2rating,r.opponent.g2rating,r.opponent.g2deviation) + variance += g_i ** 2.0 * e_i * (1.0 - e_i) + end + variance = 1.0 / variance + + # compute delta + delta = 0.0 + @results.each do |r| + delta += Glicko2.g(r.opponent.g2deviation) * (r.result - Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation)) + end + delta *= variance + + # determine new volatility + new_volatility = 0.0 + a = Math.log( @g2volatility**2.0 ) + x = 0.0 + x_new = a + while ( (x - x_new).abs > 0.0000001 ) + x = x_new + d = @g2deviation**2.0 + variance + Math.exp(x) + h1 = -(x - a)/(@dvolatility**2.0) - 0.5*Math.exp(x)/d + 0.5*Math.exp(x)*(delta/d)*(delta/d) + h2 = -1.0/(@dvolatility**2.0) - 0.5*Math.exp(x)*(@g2deviation**2.0+variance)/(d**2.0) + 0.5*(delta**2.0)*Math.exp(x)*((@g2deviation**2.0) + variance - Math.exp(x))/(d**3.0) + x_new = x - h1/h2 + end + new_volatility = Math.exp(x_new / 2.0) + + # update the rating deviation to the new pre-rating period value + pre_deviation = Math.sqrt( @g2deviation**2.0 + new_volatility**2.0 ) + + # update the rating and deviation + new_deviation = 1.0 / (Math.sqrt( 1.0/(pre_deviation**2.0) + 1.0 / variance)) + new_rating = 0.0 + @results.each do |r| + new_rating += Glicko2.g(r.opponent.g2deviation) * (r.result - Glicko2.E(@g2rating, r.opponent.g2rating, r.opponent.g2deviation)) + end + new_rating = new_rating * new_deviation**2.0 + new_rating += @g2rating + + # wipe our result lists + clear_results + + # copy new values + @g2deviation = new_deviation + @g2volatility = new_volatility + @g2rating = new_rating + end +end + + +A = Glicko2.new(1500.0, 200.0, 0.06) +B = Glicko2.new(1400.0, 30.0, 0.06) +C = Glicko2.new(1550.0, 100.0, 0.06) +D = Glicko2.new(1700.0, 300.0, 0.06) + +A.add_win(B) +A.add_loss(C) +A.add_result(D,0.0) + +A.update() + +print "rating ", A.rating, " deviation ", A.deviation + diff --git a/rb/xmlrate.rb b/rb/xmlrate.rb new file mode 100644 index 0000000..6909c22 --- /dev/null +++ b/rb/xmlrate.rb @@ -0,0 +1,57 @@ + +require "rexml/document" + +file = File.new( "../py/data/hockey-2005.xml" ) + +doc = REXML::Document.new(file) + +# each rating doc +doc.elements.each("ratingdoc") do |root| + + # each player + root.elements.each("player") do |player| + + name = player.elements["name"].text + rating = 1500.0 + deviation = 350.0 + variance = 0.06 + + # TODO: find better "Ruby-esqe" way of doing these + if player.elements["rating"] + rating = player.elements["rating"].text + end + if player.elements["deviation"] + deviation = player.elements["deviation"].text + end + if player.elements["variance"] + variance = player.elements["variance"].text + end + + puts name,rating,deviation,variance + puts + + end + + # each rating period + root.elements.each("period") do |period| + + # each regular game + period.elements.each("game") do |game| + puts "home",game.elements["home"].text + puts "away",game.elements["away"].text + puts "result",game.elements["result"].text + puts + end + + # each race + period.elements.each("race") do |race| + race.elements.each("competitor") do |c| + puts c.text + end + puts + end + + end + +end +