diff --git a/src/Podium.UnitTests/Glicko2CalculatorUnitTests.cs b/src/Podium.UnitTests/Glicko2CalculatorUnitTests.cs new file mode 100644 index 0000000..444357c --- /dev/null +++ b/src/Podium.UnitTests/Glicko2CalculatorUnitTests.cs @@ -0,0 +1,147 @@ +using System; +using Xunit; +using Shouldly; +using System.Collections.Generic; +using Podium.RatingSystem.Glicko2; + +namespace Podium.UnitTests +{ + + public class Glicko2CalculatorUnitTests + { + [Fact] + public void Calculates_mu() + { + // Given + double opponentRating = 1215; + double factor = 173.7177928; + double offset = 1500; + + IGlicko2RatingCalculator glickoCalculator = new Glicko2RatingCalculator(); + + // When + var mu = glickoCalculator.CalculateMu(opponentRating, offset, factor); + + // Then + double expectedMu = -1.64059; + mu.ShouldBe(expectedMu, 0.01); + } + + [Fact] + public void Calculates_phi() + { + // Given + double opponentRD = 81; + double factor = 173.7177928; + + IGlicko2RatingCalculator glickoCalculator = new Glicko2RatingCalculator(); + + // When + var phi = glickoCalculator.CalculatePhi(opponentRD, factor); + + // Then + double expectedPhi = 0.466273481; + phi.ShouldBe(expectedPhi, 0.01); + } + + [Fact] + public void Calculates_g() + { + // Given + double phi = 0.466273481; + + IGlicko2RatingCalculator glickoCalculator = new Glicko2RatingCalculator(); + + // When + var g = glickoCalculator.CalculateG(phi); + + // Then + double expectedG = 0.96850994; + g.ShouldBe(expectedG, 0.01); + } + + + [Fact] + public void Calculates_E() + { + // Given + double playerMu = -4.0123; + double g = 0.9685; + double opponentMu = -1.6405; + + IGlicko2RatingCalculator glickoCalculator = new Glicko2RatingCalculator(); + + // When + var E = glickoCalculator.CalculateE(g, opponentMu, playerMu); + + // Then + double expectedE = 0.091373481; + E.ShouldBe(expectedE, 0.01); + } + + [Fact] + public void Calculates_G2E() + { + // Given + double E = 0.091373481; + double g = 0.9685; + IGlicko2RatingCalculator glickoCalculator = new Glicko2RatingCalculator(); + + // When + var G2E = glickoCalculator.CalculateG2E(g, E); + + // Then + double expectedG2E = 0.077877; + G2E.ShouldBe(expectedG2E, 0.01); + } + + + [Fact] + public void Calculates_GsE() + { + // Given + double E = 0.091373481; + double g = 0.9685; + double outcome = GameOutcome.Win; + IGlicko2RatingCalculator glickoCalculator = new Glicko2RatingCalculator(); + + // When + var GsE = glickoCalculator.CalculateGsE(g, E, outcome); + + // Then + double expectedGsE = 0.8800138; + GsE.ShouldBe(expectedGsE, 0.01); + } + + [Fact] + public void Calculates_Nu() + { + // Given + double g2ESum = 0.077877; + IGlicko2RatingCalculator glickoCalculator = new Glicko2RatingCalculator(); + + // When + var nu = glickoCalculator.CalculateNu(g2ESum); + + // Then + double expectedNu = 12.84062; + nu.ShouldBe(expectedNu, 0.01); + } + + [Fact] + public void Calculates_Delta() + { + // Given + double GsESum = 0.8800138; + double nu = 12.84062; + IGlicko2RatingCalculator glickoCalculator = new Glicko2RatingCalculator(); + + // When + var delta = glickoCalculator.CalculateDelta(GsESum, nu); + + // Then + double expectedDelta = 11.2999; + delta.ShouldBe(expectedDelta, 0.01); + } + } +} diff --git a/src/Podium/Calculators/Glicko/IGlicko2RatingCalculator.cs b/src/Podium/Calculators/Glicko/IGlicko2RatingCalculator.cs new file mode 100644 index 0000000..26c2707 --- /dev/null +++ b/src/Podium/Calculators/Glicko/IGlicko2RatingCalculator.cs @@ -0,0 +1,15 @@ +namespace Podium +{ + public interface IGlicko2RatingCalculator + { + public double CalculateMu(double opponentRating, double offset, double factor); + public double CalculatePhi(double opponentRD, double factor); + public double CalculateG(double phi); + public double CalculateE(double g, double opponentMu, double playerMu); + public double CalculateG2E(double phi, double e); + public double CalculateGsE(double phi, double e, double outcome); + public double CalculateNu(double g2ESum); + public double CalculateDelta(double gsESum, double nu); + } + +} diff --git a/src/Podium/Calculators/Glicko2/Glicko2RatingCalculator.cs b/src/Podium/Calculators/Glicko2/Glicko2RatingCalculator.cs new file mode 100644 index 0000000..ce6c8d1 --- /dev/null +++ b/src/Podium/Calculators/Glicko2/Glicko2RatingCalculator.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; + +namespace Podium.RatingSystem.Glicko2 +{ + public class Glicko2RatingCalculator : IGlicko2RatingCalculator + { + public double CalculatePhi(double opponentRD, double factor) + { + return opponentRD / factor; + } + + public double CalculateG(double phi) + { + var g = 1 + 3 * Math.Pow(phi, 2) * Math.Pow(Math.PI, -2); + + g = Math.Pow(g, -0.5); + + return g; + } + + public double CalculateMu(double opponentRating, double offset, double factor) + { + var mu = (opponentRating - offset) / factor; + + return mu; + } + + public double CalculateE(double g, double opponentMu, double playerMu) + { + double E = 1 + Math.Exp(g * (opponentMu - playerMu)); + E = Math.Pow(E, -1); + + return E; + } + + public double CalculateG2E(double g, double E) + { + var G2E = Math.Pow(g, 2) * E * (1 - E); + + return G2E; + } + + public double CalculateGsE(double g, double E, double outcome) + { + double GsE = g * (outcome - E); + + return GsE; + } + + public double CalculateNu(double g2ESum) + { + return 1 / g2ESum; + } + + public double CalculateDelta(double gsESum, double nu) + { + return nu * gsESum; + } + + } +} \ No newline at end of file