mirror of
https://github.com/ppy/osu.git
synced 2024-11-15 17:57:29 +08:00
Reformat everything to be simpler
This commit is contained in:
parent
45e8d18b1b
commit
334f60f528
@ -12,7 +12,6 @@ using osu.Game.Rulesets.Scoring;
|
|||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using MathNet.Numerics;
|
using MathNet.Numerics;
|
||||||
using MathNet.Numerics.RootFinding;
|
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
private int countOk;
|
private int countOk;
|
||||||
private int countMeh;
|
private int countMeh;
|
||||||
private int countMiss;
|
private int countMiss;
|
||||||
private double? estimatedDeviation;
|
private double? estimatedUr;
|
||||||
|
|
||||||
private double effectiveMissCount;
|
private double effectiveMissCount;
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
|
countOk = score.Statistics.GetValueOrDefault(HitResult.Ok);
|
||||||
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
|
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
|
||||||
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
||||||
estimatedDeviation = computeEstimatedDeviation(score, taikoAttributes);
|
estimatedUr = computeEstimatedUr(score, taikoAttributes);
|
||||||
|
|
||||||
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
|
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
|
||||||
if (totalSuccessfulHits > 0)
|
if (totalSuccessfulHits > 0)
|
||||||
@ -71,7 +70,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
Difficulty = difficultyValue,
|
Difficulty = difficultyValue,
|
||||||
Accuracy = accuracyValue,
|
Accuracy = accuracyValue,
|
||||||
EffectiveMissCount = effectiveMissCount,
|
EffectiveMissCount = effectiveMissCount,
|
||||||
EstimatedUr = estimatedDeviation * 10,
|
EstimatedUr = estimatedUr,
|
||||||
Total = totalValue
|
Total = totalValue
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -97,18 +96,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>))
|
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>))
|
||||||
difficultyValue *= 1.050 * lengthBonus;
|
difficultyValue *= 1.050 * lengthBonus;
|
||||||
|
|
||||||
if (estimatedDeviation == null)
|
if (estimatedUr == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return difficultyValue * Math.Pow(SpecialFunctions.Erf(40 / (Math.Sqrt(2) * estimatedDeviation.Value)), 2.0);
|
return difficultyValue * Math.Pow(SpecialFunctions.Erf(400 / (Math.Sqrt(2) * estimatedUr.Value)), 2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes, bool isConvert)
|
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes, bool isConvert)
|
||||||
{
|
{
|
||||||
if (attributes.GreatHitWindow <= 0 || estimatedDeviation == null)
|
if (attributes.GreatHitWindow <= 0 || estimatedUr == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
double accuracyValue = Math.Pow(7.5 / estimatedDeviation.Value, 1.1) * Math.Pow(attributes.StarRating / 2.7, 0.8) * 100.0;
|
double accuracyValue = Math.Pow(75 / estimatedUr.Value, 1.1) * Math.Pow(attributes.StarRating / 2.7, 0.8) * 100.0;
|
||||||
|
|
||||||
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
||||||
|
|
||||||
@ -124,7 +123,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
/// assuming the player's mean hit error is 0. The estimation is consistent in that two SS scores on the same map with the same settings
|
/// assuming the player's mean hit error is 0. The estimation is consistent in that two SS scores on the same map with the same settings
|
||||||
/// will always return the same deviation. See: https://www.desmos.com/calculator/qlr946netu
|
/// will always return the same deviation. See: https://www.desmos.com/calculator/qlr946netu
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private double? computeEstimatedDeviation(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
private double? computeEstimatedUr(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
||||||
{
|
{
|
||||||
if (totalSuccessfulHits == 0 || attributes.GreatHitWindow == 0)
|
if (totalSuccessfulHits == 0 || attributes.GreatHitWindow == 0)
|
||||||
return null;
|
return null;
|
||||||
@ -135,52 +134,55 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
double clockRate = track.Rate;
|
double clockRate = track.Rate;
|
||||||
|
|
||||||
double overallDifficulty = (50 - attributes.GreatHitWindow * clockRate) / 3;
|
double overallDifficulty = (50 - attributes.GreatHitWindow * clockRate) / 3;
|
||||||
double goodHitWindow;
|
double h300 = attributes.GreatHitWindow;
|
||||||
if (overallDifficulty <= 5)
|
double h100 = overallDifficulty <= 5 ? (120 - 8 * overallDifficulty) / clockRate : (80 - 6 * (overallDifficulty - 5)) / clockRate;
|
||||||
goodHitWindow = (120 - 8 * overallDifficulty) / clockRate;
|
|
||||||
else
|
|
||||||
goodHitWindow = (80 - 6 * (overallDifficulty - 5)) / clockRate;
|
|
||||||
|
|
||||||
double root2 = Math.Sqrt(2);
|
// Returns the likelihood of a deviation resulting in the score's hit judgements. The peak of the curve is the most likely deviation.
|
||||||
|
double likelihoodGradient(double d)
|
||||||
// Log of the most likely deviation resulting in the score's hit judgements, differentiated such that 1 over the most likely deviation returns 0.
|
|
||||||
double logLikelihoodGradient(double d)
|
|
||||||
{
|
{
|
||||||
if (d <= 0)
|
if (d <= 0)
|
||||||
return 1;
|
return 0;
|
||||||
|
|
||||||
double p300 = SpecialFunctions.Erf(attributes.GreatHitWindow / root2 * d);
|
double p300 = logDiff(0, logPcHit(h300, d));
|
||||||
double lnP300 = lnErfcApprox(attributes.GreatHitWindow / root2 * d);
|
double p100 = logDiff(logPcHit(h300, d), logPcHit(h100, d));
|
||||||
double lnP100 = lnErfcApprox(goodHitWindow / root2 * d);
|
double p0 = logPcHit(h100, d);
|
||||||
|
|
||||||
double t1 = countGreat * (Math.Exp(Math.Log(attributes.GreatHitWindow) + -Math.Pow(attributes.GreatHitWindow / root2 * d, 2)) / p300);
|
double gradient = Math.Exp(
|
||||||
|
(countGreat * p300
|
||||||
|
+ (countOk + 0.5) * p100
|
||||||
|
+ countMiss * p0) / totalHits
|
||||||
|
);
|
||||||
|
|
||||||
double t2A = Math.Exp(Math.Log(goodHitWindow) + -Math.Pow(goodHitWindow / root2 * d, 2) - logDiff(lnP300, lnP100));
|
return -gradient;
|
||||||
double t2B = Math.Exp(Math.Log(attributes.GreatHitWindow) + -Math.Pow(attributes.GreatHitWindow / root2 * d, 2) - logDiff(lnP300, lnP100));
|
|
||||||
double t2 = (countOk + 1) * (t2A - t2B);
|
|
||||||
|
|
||||||
double t3 = countMiss * Math.Exp(Math.Log(goodHitWindow) + -Math.Pow(goodHitWindow / root2 * d, 2) - lnP100);
|
|
||||||
|
|
||||||
return t1 + t2 - t3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double root = Brent.FindRootExpand(logLikelihoodGradient, 0.02, 0.3);
|
double deviation = FindMinimum.OfScalarFunction(likelihoodGradient, 30);
|
||||||
|
|
||||||
return 1 / root;
|
return deviation * 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
||||||
|
|
||||||
private int totalSuccessfulHits => countGreat + countOk + countMeh;
|
private int totalSuccessfulHits => countGreat + countOk + countMeh;
|
||||||
|
|
||||||
private double lnErfcApprox(double x)
|
private double logPcHit(double x, double deviation) => logErfcApprox(x / (deviation * Math.Sqrt(2)));
|
||||||
|
|
||||||
|
// There is a numerical approximation to increase how far you can calculate Erfc(x).
|
||||||
|
private double logErfcApprox(double x) => x <= 5 ? Math.Log(SpecialFunctions.Erfc(x)) : -Math.Pow(x, 2) - Math.Log(x) - Math.Log(Math.Sqrt(Math.PI));
|
||||||
|
|
||||||
|
// Log rules make subtraction of the non-log value non-trivial, this method simply subtracts the base value of 2 logs.
|
||||||
|
private double logDiff(double firstLog, double secondLog)
|
||||||
{
|
{
|
||||||
if (x <= 5)
|
double maxVal = Math.Max(firstLog, secondLog);
|
||||||
return Math.Log(SpecialFunctions.Erfc(x));
|
|
||||||
|
|
||||||
return -Math.Pow(x, 2) - Math.Log(x) - Math.Log(Math.Sqrt(Math.PI));
|
// Avoid negative infinity - negative infinity (NaN) by checking if the higher value is negative infinity.
|
||||||
|
// Shouldn't ever happen, but good for redundancy purposes.
|
||||||
|
if (double.IsNegativeInfinity(maxVal))
|
||||||
|
{
|
||||||
|
return maxVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstLog + SpecialFunctions.Log1p(-Math.Exp(-(firstLog - secondLog)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private double logDiff(double l1, double l2) => l1 + SpecialFunctions.Log1p(-Math.Exp(-(l1 - l2)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user