Skip to content

Commit

Permalink
Merge pull request #408 from tra/fix-elo
Browse files Browse the repository at this point in the history
decrease Elo adjustment as num players increase
  • Loading branch information
tra authored Jun 11, 2024
2 parents cdedd16 + 6d417b7 commit b77e0f3
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 7 deletions.
39 changes: 32 additions & 7 deletions src/game/PlayerRatingsSimpleElo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

#include "PlayerRatingsSimpleElo.h"

#include "CAvaraGame.h"
#include "CAvaraApp.h"

#include <SDL2/SDL.h>
#include <fstream>
#include <iostream>
Expand All @@ -23,9 +26,11 @@ PlayerRatingsSimpleElo::PlayerRatingsSimpleElo() :
// this macro tells nholmann::json how to serialize/deserialize the Rating struct
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Rating, count, rating);

inline float kFactor(int count) {
// for first 40 games kFactor is higher then it sticks at 24 after that
return std::max(30.0, 60.0 - count/2);
inline float kFactor(int ratingsCount, size_t playerCount) {
// kFactor starts at 60/sqrt(N-1) then gradually reduces to 30/sqrt(N-1)
// the numerator helps the rating get in the right ballpark quickly for new players
// and the denomiator helps to reduce the effect of many players on the score
return std::max(30.0, 60.0 - ratingsCount/2) / sqrt(playerCount - 1.0);
}

// expected probability for player1 to beat player2
Expand Down Expand Up @@ -59,24 +64,44 @@ void PlayerRatingsSimpleElo::UpdateRatings(std::vector<PlayerResult> &playerResu
float delta = (1.0 - expectation(ratingsMap[currId].rating, ratingsMap[prevId].rating));

// all adjustments are made all at once after exiting this loop
adjustments[currId].rating += kFactor(ratingsMap[currId].count) * delta;
adjustments[currId].rating += kFactor(ratingsMap[currId].count, playerResults.size()) * delta;
if (delta > 0.05) {
// don't increase count if prohibitive favorite (e.g. playing bots)
adjustments[currId].count++;
}
adjustments[prevId].rating -= kFactor(ratingsMap[prevId].count) * delta;
adjustments[prevId].rating -= kFactor(ratingsMap[prevId].count, playerResults.size()) * delta;
adjustments[prevId].count++;
}
prevPlayer = currPlayer;
}
// apply all the adjustments
std::ostringstream oss;
int outCount = 0;
for (auto result: playerResults) {
auto playerId = result.playerId;
ratingsMap[playerId].rating += adjustments[playerId].rating;
ratingsMap[playerId].count += adjustments[playerId].count > 0 ? 1 : 0; // don't count game more than once
ratingsMap[playerId].rating = std::roundf(ratingsMap[playerId].rating*4)/4;
DBG_Log("elo", "rating[%d] for %s = %.0f (%+.1f)\n",
ratingsMap[playerId].count, playerId.c_str(), ratingsMap[playerId].rating, adjustments[playerId].rating);

// output updated Elo ratings if "showElow" pref set to true
if (Debug::IsEnabled("elo") || gApplication->Boolean(kShowElo)) {
if (result == playerResults.front()) {
gCurrentGame->itsApp->AddMessageLine("Updated Elo Ratings:");
}
// format results like this:
// PlayerName[111] = 1622(+22)
// LongerPlayerName[22] = 1477( -6)
oss << std::right << std::setw(21)
<< (playerId + "[" + std::to_string(ratingsMap[playerId].count) + "] = ")
<< std::fixed << std::setprecision(0)
<< std::setw(4) << ratingsMap[playerId].rating
<< "(" << std::showpos << std::setw(3) << adjustments[playerId].rating << std::noshowpos << ")" ;
// display 2 results on each message line
if ((++outCount % 2) == 0 || result == playerResults.back()) {
gCurrentGame->itsApp->AddMessageLine(oss.str());
oss.str("");
}
}
}

// remove blank playerId from ratings (could happen if someone disonnects before game is recorded)
Expand Down
3 changes: 3 additions & 0 deletions src/game/PlayerRatingsSimpleElo.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ struct PlayerResult {
int teamId;
// int lives;
// long score;
bool operator==(const PlayerResult &rhs) {
return playerId == rhs.playerId;
}
};

struct Rating {
Expand Down
2 changes: 2 additions & 0 deletions src/gui/Preferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ using json = nlohmann::json;

// other
#define kGoodGamePhrases "ggs"
#define kShowElo "showElo"

// Key names are from https://wiki.libsdl.org/SDL_Scancode
static json defaultPrefs = {
Expand Down Expand Up @@ -194,6 +195,7 @@ static json defaultPrefs = {
{kIgnoreCustomGoodySound, false},
{kThrottle, 0},
{kGoodGamePhrases, {}},
{kShowElo, false},
};


Expand Down

0 comments on commit b77e0f3

Please sign in to comment.