mirror of
https://github.com/ppy/osu-tools.git
synced 2025-06-09 09:35:15 +09:00
Merge pull request #198 from Givikap120/better_accuracy_estimator
Reworked hit result generator based on accuracy
This commit is contained in:
commit
085a853461
2 changed files with 120 additions and 22 deletions
|
@ -62,19 +62,68 @@ namespace PerformanceCalculator.Simulate
|
|||
}
|
||||
else
|
||||
{
|
||||
// Let Great=6, Good=2, Meh=1, Miss=0. The total should be this.
|
||||
var targetTotal = (int)Math.Round(accuracy * totalResultCount * 6);
|
||||
// Total result count excluding countMiss
|
||||
int relevantResultCount = totalResultCount - countMiss;
|
||||
|
||||
// Start by assuming every non miss is a meh
|
||||
// This is how much increase is needed by greats and goods
|
||||
var delta = targetTotal - (totalResultCount - countMiss);
|
||||
// Accuracy excluding countMiss. We need that because we're trying to achieve target accuracy without touching countMiss
|
||||
// So it's better to pretened that there were 0 misses in the 1st place
|
||||
double relevantAccuracy = accuracy * totalResultCount / relevantResultCount;
|
||||
|
||||
// Each great increases total by 5 (great-meh=5)
|
||||
countGreat = delta / 5;
|
||||
// Each good increases total by 1 (good-meh=1). Covers remaining difference.
|
||||
countGood = delta % 5;
|
||||
// Mehs are left over. Could be negative if impossible value of amountMiss chosen
|
||||
countMeh = totalResultCount - countGreat - countGood - countMiss;
|
||||
// Clamp accuracy to account for user trying to break the algorithm by inputting impossible values
|
||||
relevantAccuracy = Math.Clamp(relevantAccuracy, 0, 1);
|
||||
|
||||
// Main curve for accuracy > 25%, the closer accuracy is to 25% - the more 50s it adds
|
||||
if (relevantAccuracy >= 0.25)
|
||||
{
|
||||
// Main curve. Zero 50s if accuracy is 100%, one 50 per 9 100s if accuracy is 75% (excluding misses), 4 50s per 9 100s if accuracy is 50%
|
||||
double ratio50To100 = Math.Pow(1 - (relevantAccuracy - 0.25) / 0.75, 2);
|
||||
|
||||
// Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c50 = c100 * ratio50to100
|
||||
double count100Estimate = 6 * relevantResultCount * (1 - relevantAccuracy) / (5 * ratio50To100 + 4);
|
||||
|
||||
// Get count50 according to c50 = c100 * ratio50to100
|
||||
double count50Estimate = count100Estimate * ratio50To100;
|
||||
|
||||
// Round it to get int number of 100s
|
||||
countGood = (int?)Math.Round(count100Estimate);
|
||||
|
||||
// Get number of 50s as difference between total mistimed hits and count100
|
||||
countMeh = (int?)(Math.Round(count100Estimate + count50Estimate) - countGood);
|
||||
}
|
||||
// If accuracy is between 16.67% and 25% - we assume that we have no 300s
|
||||
else if (relevantAccuracy >= 1.0 / 6)
|
||||
{
|
||||
// Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c300 = 0
|
||||
double count100Estimate = 6 * relevantResultCount * relevantAccuracy - relevantResultCount;
|
||||
|
||||
// We only had 100s and 50s in that scenario so rest of the hits are 50s
|
||||
double count50Estimate = relevantResultCount - count100Estimate;
|
||||
|
||||
// Round it to get int number of 100s
|
||||
countGood = (int?)Math.Round(count100Estimate);
|
||||
|
||||
// Get number of 50s as difference between total mistimed hits and count100
|
||||
countMeh = (int?)(Math.Round(count100Estimate + count50Estimate) - countGood);
|
||||
}
|
||||
// If accuracy is less than 16.67% - it means that we have only 50s or misses
|
||||
// Assuming that we removed misses in the 1st place - that means that we need to add additional misses to achieve target accuracy
|
||||
else
|
||||
{
|
||||
// Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c300 = c100 = 0
|
||||
double count50Estimate = 6 * relevantResultCount * relevantAccuracy;
|
||||
|
||||
// We have 0 100s, because we can't start adding 100s again after reaching "only 50s" point
|
||||
countGood = 0;
|
||||
|
||||
// Round it to get int number of 50s
|
||||
countMeh = (int?)Math.Round(count50Estimate);
|
||||
|
||||
// Fill the rest results with misses overwriting initial countMiss
|
||||
countMiss = (int)(totalResultCount - countMeh);
|
||||
}
|
||||
|
||||
// Rest of the hits are 300s
|
||||
countGreat = (int)(totalResultCount - countGood - countMeh - countMiss);
|
||||
}
|
||||
|
||||
return new Dictionary<HitResult, int>
|
||||
|
|
|
@ -127,19 +127,68 @@ namespace PerformanceCalculatorGUI
|
|||
}
|
||||
else
|
||||
{
|
||||
// Let Great=6, Good=2, Meh=1, Miss=0. The total should be this.
|
||||
var targetTotal = (int)Math.Round(accuracy * totalResultCount * 6);
|
||||
// Total result count excluding countMiss
|
||||
int relevantResultCount = totalResultCount - countMiss;
|
||||
|
||||
// Start by assuming every non miss is a meh
|
||||
// This is how much increase is needed by greats and goods
|
||||
var delta = targetTotal - (totalResultCount - countMiss);
|
||||
// Accuracy excluding countMiss. We need that because we're trying to achieve target accuracy without touching countMiss
|
||||
// So it's better to pretened that there were 0 misses in the 1st place
|
||||
double relevantAccuracy = accuracy * totalResultCount / relevantResultCount;
|
||||
|
||||
// Each great increases total by 5 (great-meh=5)
|
||||
countGreat = delta / 5;
|
||||
// Each good increases total by 1 (good-meh=1). Covers remaining difference.
|
||||
countGood = delta % 5;
|
||||
// Mehs are left over. Could be negative if impossible value of amountMiss chosen
|
||||
countMeh = totalResultCount - countGreat - countGood - countMiss;
|
||||
// Clamp accuracy to account for user trying to break the algorithm by inputting impossible values
|
||||
relevantAccuracy = Math.Clamp(relevantAccuracy, 0, 1);
|
||||
|
||||
// Main curve for accuracy > 25%, the closer accuracy is to 25% - the more 50s it adds
|
||||
if (relevantAccuracy >= 0.25)
|
||||
{
|
||||
// Main curve. Zero 50s if accuracy is 100%, one 50 per 9 100s if accuracy is 75% (excluding misses), 4 50s per 9 100s if accuracy is 50%
|
||||
double ratio50To100 = Math.Pow(1 - (relevantAccuracy - 0.25) / 0.75, 2);
|
||||
|
||||
// Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c50 = c100 * ratio50to100
|
||||
double count100Estimate = 6 * relevantResultCount * (1 - relevantAccuracy) / (5 * ratio50To100 + 4);
|
||||
|
||||
// Get count50 according to c50 = c100 * ratio50to100
|
||||
double count50Estimate = count100Estimate * ratio50To100;
|
||||
|
||||
// Round it to get int number of 100s
|
||||
countGood = (int?)Math.Round(count100Estimate);
|
||||
|
||||
// Get number of 50s as difference between total mistimed hits and count100
|
||||
countMeh = (int?)(Math.Round(count100Estimate + count50Estimate) - countGood);
|
||||
}
|
||||
// If accuracy is between 16.67% and 25% - we assume that we have no 300s
|
||||
else if (relevantAccuracy >= 1.0 / 6)
|
||||
{
|
||||
// Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c300 = 0
|
||||
double count100Estimate = 6 * relevantResultCount * relevantAccuracy - relevantResultCount;
|
||||
|
||||
// We only had 100s and 50s in that scenario so rest of the hits are 50s
|
||||
double count50Estimate = relevantResultCount - count100Estimate;
|
||||
|
||||
// Round it to get int number of 100s
|
||||
countGood = (int?)Math.Round(count100Estimate);
|
||||
|
||||
// Get number of 50s as difference between total mistimed hits and count100
|
||||
countMeh = (int?)(Math.Round(count100Estimate + count50Estimate) - countGood);
|
||||
}
|
||||
// If accuracy is less than 16.67% - it means that we have only 50s or misses
|
||||
// Assuming that we removed misses in the 1st place - that means that we need to add additional misses to achieve target accuracy
|
||||
else
|
||||
{
|
||||
// Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c300 = c100 = 0
|
||||
double count50Estimate = 6 * relevantResultCount * relevantAccuracy;
|
||||
|
||||
// We have 0 100s, because we can't start adding 100s again after reaching "only 50s" point
|
||||
countGood = 0;
|
||||
|
||||
// Round it to get int number of 50s
|
||||
countMeh = (int?)Math.Round(count50Estimate);
|
||||
|
||||
// Fill the rest results with misses overwriting initial countMiss
|
||||
countMiss = (int)(totalResultCount - countMeh);
|
||||
}
|
||||
|
||||
// Rest of the hits are 300s
|
||||
countGreat = (int)(totalResultCount - countGood - countMeh - countMiss);
|
||||
}
|
||||
|
||||
return new Dictionary<HitResult, int>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue