private double calculateDifficulty() { double actualStrainStep = strain_step * TimeRate; // Find the highest strain value within each strain step List <double> highestStrains = new List <double>(); double intervalEndTime = actualStrainStep; double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval ManiaHitObjectDifficulty previousHitObject = null; foreach (var hitObject in difficultyHitObjects) { // While we are beyond the current interval push the currently available maximum to our strain list while (hitObject.BaseHitObject.StartTime > intervalEndTime) { highestStrains.Add(maximumStrain); // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay // until the beginning of the next interval. if (previousHitObject == null) { maximumStrain = 0; } else { double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay; } // Go to the next time interval intervalEndTime += actualStrainStep; } // Obtain maximum strain double strain = hitObject.IndividualStrain + hitObject.OverallStrain; maximumStrain = Math.Max(strain, maximumStrain); previousHitObject = hitObject; } // Build the weighted sum over the highest strains for each interval double difficulty = 0; double weight = 1; highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. foreach (double strain in highestStrains) { difficulty += weight * strain; weight *= decay_weight; } return(difficulty); }
private bool calculateStrainValues() { // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. using (List <ManiaHitObjectDifficulty> .Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator()) { if (!hitObjectsEnumerator.MoveNext()) { return(false); } ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current; // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. while (hitObjectsEnumerator.MoveNext()) { var next = hitObjectsEnumerator.Current; next?.CalculateStrains(current, TimeRate); current = next; } return(true); } }
private double calculateDifficulty(List <ManiaHitObjectDifficulty> objects, double timeRate) { double actualStrainStep = strain_step * timeRate; // Find the highest strain value within each strain step List <double> highestStrains = new List <double>(); double intervalEndTime = actualStrainStep; double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval // Strain summation variables List <double> summedStrains = new List <double>(); summedStrains.Add(0); double summedStrainsAvg = 0, summedStrainsMax = 0; ManiaHitObjectDifficulty previousHitObject = null; foreach (var hitObject in objects) { // While we are beyond the current interval push the currently available maximum to our strain list while (hitObject.BaseHitObject.StartTime > intervalEndTime) { highestStrains.Add(maximumStrain); summedStrains.Add(0); // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay // until the beginning of the next interval. if (previousHitObject == null) { maximumStrain = 0; } else { double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay; } // Go to the next time interval intervalEndTime += actualStrainStep; } // Obtain maximum strain double strain = hitObject.IndividualStrain + hitObject.OverallStrain; summedStrains[summedStrains.Count - 1] += strain; maximumStrain = Math.Max(strain, maximumStrain); previousHitObject = hitObject; } // Build the weighted sum over the highest strains for each interval double difficulty = 0; double weight = 1; highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. foreach (double strain in highestStrains) { difficulty += weight * strain; weight *= decay_weight; } difficulty *= 0.8; difficulty *= star_scaling_factor; // Compute summed strains average and max foreach (double strain in summedStrains) { summedStrainsAvg += strain; if (strain > summedStrainsMax) { summedStrainsMax = strain; } } summedStrainsAvg /= summedStrains.Count; // compute bonus or penalty double error = 0.0; foreach (double strain in summedStrains) { error += Math.Pow((summedStrainsMax - strain) / summedStrainsAvg, 2.25); } error /= summedStrains.Count; error /= 100.0; double rating_weight = Math.Min(difficulty / 2.5, 1.0); double star_rating = (1 - (.025 * rating_weight)) * difficulty; if (error <= 0.13) { double bonus = 1 + ((Math.Pow((0.13 - error) / 0.13, 1.80) * 0.065) * rating_weight); star_rating *= bonus; } else { double penalty = 1 - ((0.11 - Math.Pow(Math.Max(0.15 - (error - 0.13), 0.0) / 0.15, 1.45) * 0.11) * rating_weight); star_rating *= penalty; } return(star_rating); }