Skip to content

Commit

Permalink
Added formula to the non GUI too
Browse files Browse the repository at this point in the history
and changed the order a bit to be more readable
  • Loading branch information
Givikap120 committed Aug 2, 2024
1 parent 1d462b2 commit 07e3180
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 37 deletions.
75 changes: 62 additions & 13 deletions PerformanceCalculator/Simulate/OsuSimulateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,68 @@ protected override Dictionary<HitResult, int> GenerateHitResults(double accuracy
}
else
{
// Let Great=6, Good=2, Meh=1, Miss=0. The total should be this.
var targetTotal = (int)Math.Round(accuracy * totalResultCount * 6);

// 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);

// 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;
// Total result count excluding countMiss
int relevantResultCount = 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;

// 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>
Expand Down
48 changes: 24 additions & 24 deletions PerformanceCalculatorGUI/RulesetHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,24 +137,26 @@ private static Dictionary<HitResult, int> generateOsuHitResults(double accuracy,
// Clamp accuracy to account for user trying to break the algorithm by inputting impossible values
relevantAccuracy = Math.Clamp(relevantAccuracy, 0, 1);

// 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
if (relevantAccuracy < 1.0 / 6)
// Main curve for accuracy > 25%, the closer accuracy is to 25% - the more 50s it adds
if (relevantAccuracy >= 0.25)
{
// Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c300 = c100 = 0
double count50estimate = 6 * relevantResultCount * relevantAccuracy;
// 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);

// We have 0 100s, because we can't start adding 100s again after reaching "only 50s" point
countGood = 0;
// 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);

// Round it to get int number of 50s
countMeh = (int?)Math.Round(count50estimate);
// Get count50 according to c50 = c100 * ratio50to100
double count50estimate = count100estimate * ratio50to100;

// Fill the rest results with misses overwriting initial countMiss
countMiss = (int)(totalResultCount - countMeh);
// 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 < 0.25)
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;
Expand All @@ -168,23 +170,21 @@ private static Dictionary<HitResult, int> generateOsuHitResults(double accuracy,
// Get number of 50s as difference between total mistimed hits and count100
countMeh = (int?)(Math.Round(count100estimate + count50estimate) - countGood);
}
// Main curve for accuracy > 25%, the closer accuracy is to 25% - the more 50s it adds
// 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
{
// 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);
// Derived from the formula: Accuracy = (6 * c300 + 2 * c100 + c50) / (6 * totalHits), assuming that c300 = c100 = 0
double count50estimate = 6 * relevantResultCount * relevantAccuracy;

// Get count50 according to c50 = c100 * ratio50to100
double count50estimate = count100estimate * ratio50to100;
// 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 100s
countGood = (int?)Math.Round(count100estimate);
// Round it to get int number of 50s
countMeh = (int?)Math.Round(count50estimate);

// Get number of 50s as difference between total mistimed hits and count100
countMeh = (int?)(Math.Round(count100estimate + count50estimate) - countGood);
// Fill the rest results with misses overwriting initial countMiss
countMiss = (int)(totalResultCount - countMeh);
}

// Rest of the hits are 300s
Expand Down

0 comments on commit 07e3180

Please sign in to comment.