Skip to content

Commit 98a920d

Browse files
add nelo statistic and annotate WDL result in fastchess output (#226)
1 parent 2088490 commit 98a920d

File tree

3 files changed

+114
-3
lines changed

3 files changed

+114
-3
lines changed

src/matchmaking/elo/elo.cpp

+92
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,29 @@ namespace fast_chess {
99
Elo::Elo(int wins, int losses, int draws) {
1010
diff_ = getDiff(wins, losses, draws);
1111
error_ = getError(wins, losses, draws);
12+
nelodiff_ = getneloDiff(wins, losses, draws);
13+
neloerror_ = getneloError(wins, losses, draws);
1214
}
1315

1416
Elo::Elo(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) {
1517
diff_ = getDiff(penta_WW, penta_WD, penta_WL, penta_DD, penta_LD, penta_LL);
1618
error_ = getError(penta_WW, penta_WD, penta_WL, penta_DD, penta_LD, penta_LL);
19+
nelodiff_ = getneloDiff(penta_WW, penta_WD, penta_WL, penta_DD, penta_LD, penta_LL);
20+
neloerror_ = getneloError(penta_WW, penta_WD, penta_WL, penta_DD, penta_LD, penta_LL);
1721
}
1822

1923
double Elo::percToEloDiff(double percentage) noexcept {
2024
return -400.0 * std::log10(1.0 / percentage - 1.0);
2125
}
2226

27+
double Elo::percToNeloDiff(double percentage, double stdev) noexcept {
28+
return (percentage - 0.5) / (std::sqrt(2) * stdev) * (800 / std::log(10));
29+
}
30+
31+
double Elo::percToNeloDiffWDL(double percentage, double stdev) noexcept {
32+
return (percentage - 0.5) / stdev * (800 / std::log(10));
33+
}
34+
2335
double Elo::getError(int wins, int losses, int draws) noexcept {
2436
const double n = wins + losses + draws;
2537
const double w = wins / n;
@@ -37,6 +49,23 @@ double Elo::getError(int wins, int losses, int draws) noexcept {
3749
return (percToEloDiff(devMax) - percToEloDiff(devMin)) / 2.0;
3850
}
3951

52+
double Elo::getneloError(int wins, int losses, int draws) noexcept {
53+
const double n = wins + losses + draws;
54+
const double w = wins / n;
55+
const double l = losses / n;
56+
const double d = draws / n;
57+
const double perc = w + d / 2.0;
58+
59+
const double devW = w * std::pow(1.0 - perc, 2.0);
60+
const double devL = l * std::pow(0.0 - perc, 2.0);
61+
const double devD = d * std::pow(0.5 - perc, 2.0);
62+
const double stdev = std::sqrt(devW + devL + devD) / std::sqrt(n);
63+
64+
const double devMin = perc - 1.959963984540054 * stdev;
65+
const double devMax = perc + 1.959963984540054 * stdev;
66+
return (percToNeloDiffWDL(devMax, stdev * std::sqrt(n)) - percToNeloDiffWDL(devMin, stdev * std::sqrt(n))) / 2.0;
67+
}
68+
4069
double Elo::getError(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) noexcept {
4170
const double pairs = penta_WW + penta_WD + penta_WL + penta_DD + penta_LD + penta_LL;
4271
const double WW = double(penta_WW) / pairs;
@@ -58,6 +87,27 @@ double Elo::getError(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int
5887
return (percToEloDiff(devMax) - percToEloDiff(devMin)) / 2.0;
5988
}
6089

90+
double Elo::getneloError(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) noexcept {
91+
const double pairs = penta_WW + penta_WD + penta_WL + penta_DD + penta_LD + penta_LL;
92+
const double WW = double(penta_WW) / pairs;
93+
const double WD = double(penta_WD) / pairs;
94+
const double WL = double(penta_WL) / pairs;
95+
const double DD = double(penta_DD) / pairs;
96+
const double LD = double(penta_LD) / pairs;
97+
const double LL = double(penta_LL) / pairs;
98+
const double a = WW + 0.75 * WD + 0.5 * (WL + DD) + 0.25 * LD;
99+
const double WW_dev = WW * std::pow((1 - a), 2);
100+
const double WD_dev = WD * std::pow((0.75 - a), 2);
101+
const double WLDD_dev = (WL + DD) * std::pow((0.5 - a), 2);
102+
const double LD_dev = LD * std::pow((0.25 - a), 2);
103+
const double LL_dev = LL * std::pow((0 - a), 2);
104+
const double stdev = std::sqrt(WW_dev + WD_dev + WLDD_dev + LD_dev + LL_dev) / std::sqrt(pairs);
105+
106+
const double devMin = a - 1.959963984540054 * stdev;
107+
const double devMax = a + 1.959963984540054 * stdev;
108+
return (percToNeloDiff(devMax, stdev * std::sqrt(pairs)) - percToNeloDiff(devMin, stdev * std::sqrt(pairs))) / 2.0;
109+
}
110+
61111
double Elo::getDiff(int wins, int losses, int draws) noexcept {
62112
const double n = wins + losses + draws;
63113
const double score = wins + draws / 2.0;
@@ -66,6 +116,20 @@ double Elo::getDiff(int wins, int losses, int draws) noexcept {
66116
return percToEloDiff(percentage);
67117
}
68118

119+
double Elo::getneloDiff(int wins, int losses, int draws) noexcept {
120+
const double n = wins + losses + draws;
121+
const double w = wins / n;
122+
const double l = losses / n;
123+
const double d = draws / n;
124+
const double perc = w + d / 2.0;
125+
126+
const double devW = w * std::pow(1.0 - perc, 2.0);
127+
const double devL = l * std::pow(0.0 - perc, 2.0);
128+
const double devD = d * std::pow(0.5 - perc, 2.0);
129+
const double stdev = std::sqrt(devW + devL + devD) / std::sqrt(n);
130+
return percToNeloDiffWDL(perc, stdev * std::sqrt(n));
131+
}
132+
69133
double Elo::getDiff(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) noexcept {
70134
const double pairs = penta_WW + penta_WD + penta_WL + penta_DD + penta_LD + penta_LL;
71135
const double WW = double(penta_WW) / pairs;
@@ -78,6 +142,25 @@ double Elo::getDiff(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int
78142
return percToEloDiff(percentage);
79143
}
80144

145+
double Elo::getneloDiff(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) noexcept {
146+
const double pairs = penta_WW + penta_WD + penta_WL + penta_DD + penta_LD + penta_LL;
147+
const double WW = double(penta_WW) / pairs;
148+
const double WD = double(penta_WD) / pairs;
149+
const double WL = double(penta_WL) / pairs;
150+
const double DD = double(penta_DD) / pairs;
151+
const double LD = double(penta_LD) / pairs;
152+
const double LL = double(penta_LL) / pairs;
153+
const double a = WW + 0.75 * WD + 0.5 * (WL + DD) + 0.25 * LD;
154+
const double WW_dev = WW * std::pow((1 - a), 2);
155+
const double WD_dev = WD * std::pow((0.75 - a), 2);
156+
const double WLDD_dev = (WL + DD) * std::pow((0.5 - a), 2);
157+
const double LD_dev = LD * std::pow((0.25 - a), 2);
158+
const double LL_dev = LL * std::pow((0 - a), 2);
159+
const double stdev = std::sqrt(WW_dev + WD_dev + WLDD_dev + LD_dev + LL_dev) / std::sqrt(pairs);
160+
161+
return percToNeloDiff(a, stdev * std::sqrt(pairs));
162+
}
163+
81164
std::string Elo::getElo() const noexcept {
82165
std::stringstream ss;
83166

@@ -87,6 +170,15 @@ std::string Elo::getElo() const noexcept {
87170
return ss.str();
88171
}
89172

173+
std::string Elo::getnElo() const noexcept {
174+
std::stringstream ss;
175+
176+
ss << std::fixed << std::setprecision(2) << nelodiff_;
177+
ss << " +/- ";
178+
ss << std::fixed << std::setprecision(2) << neloerror_;
179+
return ss.str();
180+
}
181+
90182
std::string Elo::getLos(int wins, int losses, int draws) noexcept {
91183
const double games = wins + losses + draws;
92184
const double W = double(wins) / games;

src/matchmaking/elo/elo.hpp

+16
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,30 @@ class Elo {
1111

1212
[[nodiscard]] static double percToEloDiff(double percentage) noexcept;
1313

14+
[[nodiscard]] static double percToNeloDiff(double percentage, double stdev) noexcept;
15+
16+
[[nodiscard]] static double percToNeloDiffWDL(double percentage, double stdev) noexcept;
17+
1418
[[nodiscard]] static double getDiff(int wins, int losses, int draws) noexcept;
1519

1620
[[nodiscard]] static double getDiff(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) noexcept;
1721

22+
[[nodiscard]] static double getneloDiff(int wins, int losses, int draws) noexcept;
23+
24+
[[nodiscard]] static double getneloDiff(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) noexcept;
25+
1826
[[nodiscard]] static double getError(int wins, int losses, int draws) noexcept;
1927

2028
[[nodiscard]] static double getError(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) noexcept;
2129

30+
[[nodiscard]] static double getneloError(int wins, int losses, int draws) noexcept;
31+
32+
[[nodiscard]] static double getneloError(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) noexcept;
33+
2234
[[nodiscard]] std::string getElo() const noexcept;
2335

36+
[[nodiscard]] std::string getnElo() const noexcept;
37+
2438
[[nodiscard]] static std::string getLos(int wins, int losses, int draws) noexcept;
2539

2640
[[nodiscard]] static std::string getLos(int penta_WW, int penta_WD, int penta_WL, int penta_DD, int penta_LD, int penta_LL) noexcept;
@@ -36,6 +50,8 @@ class Elo {
3650
private:
3751
double diff_;
3852
double error_;
53+
double nelodiff_;
54+
double neloerror_;
3955
};
4056

4157
} // namespace fast_chess

src/matchmaking/output/output_fastchess.hpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ class Fastchess : public IOutput {
2828
<< second //
2929
<< ": " //
3030
<< stats.wins //
31-
<< " - " //
31+
<< "W - " //
3232
<< stats.losses //
33-
<< " - " //
33+
<< "L - " //
3434
<< stats.draws //
35-
<< " [" //
35+
<< "D [" //
3636
<< Elo::getScoreRatio(stats.penta_WW, stats.penta_WD, stats.penta_WL, stats.penta_DD, stats.penta_LD, stats.penta_LL) //
3737
<< "] " //
3838
<< current_game_count //
@@ -41,6 +41,9 @@ class Fastchess : public IOutput {
4141
ss << "Elo difference: " //
4242
<< elo.getElo() //
4343
<< ", " //
44+
<< "nElo difference: " //
45+
<< elo.getnElo() //
46+
<< ", " //
4447
<< "LOS: " //
4548
<< Elo::getLos(stats.penta_WW, stats.penta_WD, stats.penta_WL, stats.penta_DD, stats.penta_LD, stats.penta_LL) //
4649
<< ", " //

0 commit comments

Comments
 (0)