// subtracts totalEloError from expectedElo public static double CalculateGoodness( Player sideToMove, EnumArray <GameLevel, AggregatedEntry> aggregatedEntries, ChessDBCNScore score, Options options ) { const double maxAllowedPlayerEloDiff = 400; const double maxCalculatedEloDiff = 800; bool useEval = options.EvalWeight > 0.0; bool useGames = options.GamesWeight > 0.0; AggregatedEntry totalEntry = new AggregatedEntry(); foreach (KeyValuePair <GameLevel, AggregatedEntry> e in aggregatedEntries) { totalEntry.Combine(e.Value); } if (useGames && totalEntry.Count == 0) { return(0.0); } if (useEval && score == null && Math.Abs(totalEntry.TotalEloDiff / (double)totalEntry.Count) > maxAllowedPlayerEloDiff) { return(0.0); } double gamesWeight = options.GamesWeight; double evalWeight = options.EvalWeight; double calculateAdjustedPerf(AggregatedEntry e) { if (e.Count > 0) { ulong? lowNThreshold = options.IncreaseErrorBarForLowN ? (ulong?)options.LowN : null; ulong totalWins = e.WinCount; ulong totalDraws = e.DrawCount; ulong totalLosses = e.Count - totalWins - totalDraws; double totalEloError = EloCalculator.Clamp( EloCalculator.EloError99pct(totalWins, totalDraws, totalLosses, lowNThreshold), 2 * maxCalculatedEloDiff ); double totalPerf = (totalWins + totalDraws * options.DrawScore) / e.Count; double expectedTotalPerf = EloCalculator.GetExpectedPerformance(e.TotalEloDiff / (double)e.Count); if (sideToMove == Player.Black) { totalPerf = 1.0 - totalPerf; expectedTotalPerf = 1.0 - expectedTotalPerf; } double adjustedPerf = EloCalculator.GetAdjustedPerformance(totalPerf, expectedTotalPerf, maxCalculatedEloDiff); double expectedElo = EloCalculator.Clamp( EloCalculator.Clamp( EloCalculator.GetEloFromPerformance(adjustedPerf), maxCalculatedEloDiff ) - totalEloError, maxCalculatedEloDiff); adjustedPerf = EloCalculator.GetExpectedPerformance(expectedElo); return(adjustedPerf); } else { return(0.0); } } double adjustedGamesPerf = calculateAdjustedPerf(totalEntry); double gamesGoodness = EloCalculator.Clamp( EloCalculator.GetEloFromPerformance(adjustedGamesPerf), maxCalculatedEloDiff ) * gamesWeight; // If eval is not present then assume 0.5 but reduce it for moves with low game count. // The idea is that we don't want missing eval to penalize common moves. double evalGoodness = ( score != null ? EloCalculator.GetEloFromPerformance(score.Perf) : -EloCalculator.EloError99pct( totalEntry.WinCount, totalEntry.DrawCount, totalEntry.LossCount ) ) * evalWeight; double weightSum = gamesWeight + evalWeight; double goodness = EloCalculator.GetExpectedPerformance((gamesGoodness + evalGoodness) / weightSum); return(goodness); }
// multiplies games weight by e^-(elo_error/EloErrorHalfWeight) public static double CalculateGoodnessPrioritizeEval( Player sideToMove, EnumArray <GameLevel, AggregatedEntry> aggregatedEntries, ChessDBCNScore score, Options options ) { const double eloErrorHalfWeight = 100; const double maxAllowedPlayerEloDiff = 400; const double maxCalculatedEloDiff = 800; AggregatedEntry totalEntry = new AggregatedEntry(); foreach (KeyValuePair <GameLevel, AggregatedEntry> e in aggregatedEntries) { totalEntry.Combine(e.Value); } if (score == null && Math.Abs(totalEntry.TotalEloDiff / (double)totalEntry.Count) > maxAllowedPlayerEloDiff) { return(0.0); } double evalWeight = options.EvalWeight; Tuple <double, double> calculateAdjustedPerf(AggregatedEntry e) { ulong? lowNThreshold = options.IncreaseErrorBarForLowN ? (ulong?)options.LowN : null; ulong totalWins = e.WinCount; ulong totalDraws = e.DrawCount; ulong totalLosses = e.Count - totalWins - totalDraws; double totalEloError = EloCalculator.Clamp( EloCalculator.EloError99pct(totalWins, totalDraws, totalLosses, lowNThreshold), 2 * maxCalculatedEloDiff ); double gw = options.GamesWeight * Math.Exp(-(totalEloError / eloErrorHalfWeight)); if (e.Count > 0) { double totalPerf = (totalWins + totalDraws * options.DrawScore) / e.Count; double expectedTotalPerf = EloCalculator.GetExpectedPerformance(e.TotalEloDiff / (double)e.Count); if (sideToMove == Player.Black) { totalPerf = 1.0 - totalPerf; expectedTotalPerf = 1.0 - expectedTotalPerf; } double adjustedPerf = EloCalculator.GetAdjustedPerformance(totalPerf, expectedTotalPerf, maxCalculatedEloDiff); if (score == null) { // If the score is null there's nothing to moderate the bad empirical data. // So we apply the old method to reduce the QI of this move based on elo error. double expectedElo = EloCalculator.Clamp( EloCalculator.Clamp( EloCalculator.GetEloFromPerformance(adjustedPerf), maxCalculatedEloDiff ) - totalEloError, maxCalculatedEloDiff); adjustedPerf = EloCalculator.GetExpectedPerformance(expectedElo); } return(new Tuple <double, double>(gw, adjustedPerf)); } else { return(new Tuple <double, double>(0, 0)); } } (double gamesWeight, double adjustedGamesPerf) = calculateAdjustedPerf(totalEntry); double gamesGoodness = EloCalculator.Clamp( EloCalculator.GetEloFromPerformance(adjustedGamesPerf), maxCalculatedEloDiff ) * gamesWeight; // If eval is not present then assume 0.5 but reduce it for moves with low game count. // The idea is that we don't want missing eval to penalize common moves. double evalGoodness = ( score != null ? EloCalculator.GetEloFromPerformance(score.Perf) : -EloCalculator.EloError99pct( totalEntry.WinCount, totalEntry.DrawCount, totalEntry.LossCount ) ) * evalWeight; double weightSum = gamesWeight + evalWeight; double goodness = EloCalculator.GetExpectedPerformance((gamesGoodness + evalGoodness) / weightSum); return(goodness); }