mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 09:58:21 +08:00
Reorder members for better readability
This commit is contained in:
parent
7e2bef3b9f
commit
8ace7df0fd
@ -13,11 +13,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
||||
private const int roll_min_repetitions = 12;
|
||||
private const int tl_min_repetitions = 16;
|
||||
|
||||
private List<TaikoDifficultyHitObject> hitObjects;
|
||||
private readonly List<TaikoDifficultyHitObject> hitObjects;
|
||||
|
||||
public void FindCheese(List<TaikoDifficultyHitObject> difficultyHitObjects)
|
||||
public StaminaCheeseDetector(List<TaikoDifficultyHitObject> hitObjects)
|
||||
{
|
||||
this.hitObjects = hitObjects;
|
||||
}
|
||||
|
||||
public void FindCheese()
|
||||
{
|
||||
hitObjects = difficultyHitObjects;
|
||||
findRolls(3);
|
||||
findRolls(4);
|
||||
findTlTap(0, HitType.Rim);
|
||||
|
@ -13,11 +13,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
||||
{
|
||||
public readonly TaikoDifficultyHitObjectRhythm Rhythm;
|
||||
public readonly HitType? HitType;
|
||||
public readonly int ObjectIndex;
|
||||
|
||||
public bool StaminaCheese;
|
||||
|
||||
public readonly int ObjectIndex;
|
||||
|
||||
public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, int objectIndex)
|
||||
: base(hitObject, lastObject, clockRate)
|
||||
{
|
||||
@ -45,8 +44,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
||||
};
|
||||
|
||||
private TaikoDifficultyHitObjectRhythm getClosestRhythm(double ratio)
|
||||
{
|
||||
return common_rhythms.OrderBy(x => Math.Abs(x.Ratio - ratio)).First();
|
||||
}
|
||||
=> common_rhythms.OrderBy(x => Math.Abs(x.Ratio - ratio)).First();
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
{
|
||||
public class Colour : Skill
|
||||
{
|
||||
private const int mono_history_max_length = 5;
|
||||
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 0.4;
|
||||
|
||||
private const int mono_history_max_length = 5;
|
||||
|
||||
/// <summary>
|
||||
/// List of the last <see cref="mono_history_max_length"/> most recent mono patterns, with the most recent at the end of the list.
|
||||
/// </summary>
|
||||
private readonly LimitedCapacityQueue<int> monoHistory = new LimitedCapacityQueue<int>(mono_history_max_length);
|
||||
|
||||
private HitType? previousHitType;
|
||||
|
||||
/// <summary>
|
||||
@ -24,11 +29,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// </summary>
|
||||
private int currentMonoLength = 1;
|
||||
|
||||
/// <summary>
|
||||
/// List of the last <see cref="mono_history_max_length"/> most recent mono patterns, with the most recent at the end of the list.
|
||||
/// </summary>
|
||||
private readonly LimitedCapacityQueue<int> monoHistory = new LimitedCapacityQueue<int>(mono_history_max_length);
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
if (!(current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000))
|
||||
|
@ -14,17 +14,43 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
{
|
||||
protected override double SkillMultiplier => 10;
|
||||
protected override double StrainDecayBase => 0;
|
||||
private const double strain_decay = 0.96;
|
||||
private double currentStrain;
|
||||
|
||||
private readonly LimitedCapacityQueue<TaikoDifficultyHitObject> rhythmHistory = new LimitedCapacityQueue<TaikoDifficultyHitObject>(rhythm_history_max_length);
|
||||
private const double strain_decay = 0.96;
|
||||
private const int rhythm_history_max_length = 8;
|
||||
|
||||
private readonly LimitedCapacityQueue<TaikoDifficultyHitObject> rhythmHistory = new LimitedCapacityQueue<TaikoDifficultyHitObject>(rhythm_history_max_length);
|
||||
|
||||
private double currentStrain;
|
||||
private int notesSinceRhythmChange;
|
||||
|
||||
private double repetitionPenalty(int notesSince)
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
return Math.Min(1.0, 0.032 * notesSince);
|
||||
if (!(current.BaseObject is Hit))
|
||||
{
|
||||
resetRhythmStrain();
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
currentStrain *= strain_decay;
|
||||
|
||||
TaikoDifficultyHitObject hitobject = (TaikoDifficultyHitObject)current;
|
||||
notesSinceRhythmChange += 1;
|
||||
|
||||
if (hitobject.Rhythm.Difficulty == 0.0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double objectStrain = hitobject.Rhythm.Difficulty;
|
||||
|
||||
objectStrain *= repetitionPenalties(hitobject);
|
||||
objectStrain *= patternLengthPenalty(notesSinceRhythmChange);
|
||||
objectStrain *= speedPenalty(hitobject.DeltaTime);
|
||||
|
||||
notesSinceRhythmChange = 0;
|
||||
|
||||
currentStrain += objectStrain;
|
||||
return currentStrain;
|
||||
}
|
||||
|
||||
// Finds repetitions and applies penalties
|
||||
@ -61,6 +87,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
return true;
|
||||
}
|
||||
|
||||
private double repetitionPenalty(int notesSince) => Math.Min(1.0, 0.032 * notesSince);
|
||||
|
||||
private double patternLengthPenalty(int patternLength)
|
||||
{
|
||||
double shortPatternPenalty = Math.Min(0.15 * patternLength, 1.0);
|
||||
@ -78,36 +106,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
if (!(current.BaseObject is Hit))
|
||||
{
|
||||
resetRhythmStrain();
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
currentStrain *= strain_decay;
|
||||
|
||||
TaikoDifficultyHitObject hitobject = (TaikoDifficultyHitObject)current;
|
||||
notesSinceRhythmChange += 1;
|
||||
|
||||
if (hitobject.Rhythm.Difficulty == 0.0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double objectStrain = hitobject.Rhythm.Difficulty;
|
||||
|
||||
objectStrain *= repetitionPenalties(hitobject);
|
||||
objectStrain *= patternLengthPenalty(notesSinceRhythmChange);
|
||||
objectStrain *= speedPenalty(hitobject.DeltaTime);
|
||||
|
||||
notesSinceRhythmChange = 0;
|
||||
|
||||
currentStrain += objectStrain;
|
||||
return currentStrain;
|
||||
}
|
||||
|
||||
private void resetRhythmStrain()
|
||||
{
|
||||
currentStrain = 0.0;
|
||||
|
@ -12,32 +12,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
{
|
||||
public class Stamina : Skill
|
||||
{
|
||||
private readonly int hand;
|
||||
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 0.4;
|
||||
|
||||
private const int max_history_length = 2;
|
||||
|
||||
private readonly int hand;
|
||||
private readonly LimitedCapacityQueue<double> notePairDurationHistory = new LimitedCapacityQueue<double>(max_history_length);
|
||||
|
||||
private double offhandObjectDuration = double.MaxValue;
|
||||
|
||||
// Penalty for tl tap or roll
|
||||
private double cheesePenalty(double notePairDuration)
|
||||
public Stamina(bool rightHand)
|
||||
{
|
||||
if (notePairDuration > 125) return 1;
|
||||
if (notePairDuration < 100) return 0.6;
|
||||
|
||||
return 0.6 + (notePairDuration - 100) * 0.016;
|
||||
}
|
||||
|
||||
private double speedBonus(double notePairDuration)
|
||||
{
|
||||
if (notePairDuration >= 200) return 0;
|
||||
|
||||
double bonus = 200 - notePairDuration;
|
||||
bonus *= bonus;
|
||||
return bonus / 100000;
|
||||
hand = rightHand ? 1 : 0;
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
@ -71,9 +58,22 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Stamina(bool rightHand)
|
||||
// Penalty for tl tap or roll
|
||||
private double cheesePenalty(double notePairDuration)
|
||||
{
|
||||
hand = rightHand ? 1 : 0;
|
||||
if (notePairDuration > 125) return 1;
|
||||
if (notePairDuration < 100) return 0.6;
|
||||
|
||||
return 0.6 + (notePairDuration - 100) * 0.016;
|
||||
}
|
||||
|
||||
private double speedBonus(double notePairDuration)
|
||||
{
|
||||
if (notePairDuration >= 200) return 0;
|
||||
|
||||
double bonus = 200 - notePairDuration;
|
||||
bonus *= bonus;
|
||||
return bonus / 100000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,87 +29,21 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
{
|
||||
}
|
||||
|
||||
private double simpleColourPenalty(double staminaDifficulty, double colorDifficulty)
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
|
||||
{
|
||||
if (colorDifficulty <= 0) return 0.79 - 0.25;
|
||||
|
||||
return 0.79 - Math.Atan(staminaDifficulty / colorDifficulty - 12) / Math.PI / 2;
|
||||
}
|
||||
|
||||
private double norm(double p, params double[] values)
|
||||
{
|
||||
return Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p);
|
||||
}
|
||||
|
||||
private double rescale(double sr)
|
||||
{
|
||||
if (sr < 0) return sr;
|
||||
|
||||
return 10.43 * Math.Log(sr / 8 + 1);
|
||||
}
|
||||
|
||||
private double locallyCombinedDifficulty(
|
||||
double staminaPenalty, Colour colour, Rhythm rhythm, Stamina staminaRight, Stamina staminaLeft)
|
||||
{
|
||||
double difficulty = 0;
|
||||
double weight = 1;
|
||||
List<double> peaks = new List<double>();
|
||||
|
||||
for (int i = 0; i < colour.StrainPeaks.Count; i++)
|
||||
{
|
||||
double colourPeak = colour.StrainPeaks[i] * colour_skill_multiplier;
|
||||
double rhythmPeak = rhythm.StrainPeaks[i] * rhythm_skill_multiplier;
|
||||
double staminaPeak = (staminaRight.StrainPeaks[i] + staminaLeft.StrainPeaks[i]) * stamina_skill_multiplier * staminaPenalty;
|
||||
peaks.Add(norm(2, colourPeak, rhythmPeak, staminaPeak));
|
||||
}
|
||||
|
||||
foreach (double strain in peaks.OrderByDescending(d => d))
|
||||
{
|
||||
difficulty += strain * weight;
|
||||
weight *= 0.9;
|
||||
}
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
if (beatmap.HitObjects.Count == 0)
|
||||
return new TaikoDifficultyAttributes { Mods = mods, Skills = skills };
|
||||
|
||||
var colour = (Colour)skills[0];
|
||||
var rhythm = (Rhythm)skills[1];
|
||||
var staminaRight = (Stamina)skills[2];
|
||||
var staminaLeft = (Stamina)skills[3];
|
||||
|
||||
double colourRating = colour.DifficultyValue() * colour_skill_multiplier;
|
||||
double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier;
|
||||
double staminaRating = (staminaRight.DifficultyValue() + staminaLeft.DifficultyValue()) * stamina_skill_multiplier;
|
||||
|
||||
double staminaPenalty = simpleColourPenalty(staminaRating, colourRating);
|
||||
staminaRating *= staminaPenalty;
|
||||
|
||||
double combinedRating = locallyCombinedDifficulty(staminaPenalty, colour, rhythm, staminaRight, staminaLeft);
|
||||
double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating);
|
||||
double starRating = 1.4 * separatedRating + 0.5 * combinedRating;
|
||||
starRating = rescale(starRating);
|
||||
|
||||
HitWindows hitWindows = new TaikoHitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||
|
||||
return new TaikoDifficultyAttributes
|
||||
{
|
||||
StarRating = starRating,
|
||||
Mods = mods,
|
||||
StaminaStrain = staminaRating,
|
||||
RhythmStrain = rhythmRating,
|
||||
ColourStrain = colourRating,
|
||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||
GreatHitWindow = (int)hitWindows.WindowFor(HitResult.Great) / clockRate,
|
||||
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
|
||||
Skills = skills
|
||||
new Colour(),
|
||||
new Rhythm(),
|
||||
new Stamina(true),
|
||||
new Stamina(false),
|
||||
};
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||
{
|
||||
new TaikoModDoubleTime(),
|
||||
new TaikoModHalfTime(),
|
||||
new TaikoModEasy(),
|
||||
new TaikoModHardRock(),
|
||||
};
|
||||
}
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||
{
|
||||
@ -131,24 +65,89 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
new StaminaCheeseDetector().FindCheese(taikoDifficultyHitObjects);
|
||||
new StaminaCheeseDetector(taikoDifficultyHitObjects).FindCheese();
|
||||
return taikoDifficultyHitObjects;
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
new Colour(),
|
||||
new Rhythm(),
|
||||
new Stamina(true),
|
||||
new Stamina(false),
|
||||
};
|
||||
if (beatmap.HitObjects.Count == 0)
|
||||
return new TaikoDifficultyAttributes { Mods = mods, Skills = skills };
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||
var colour = (Colour)skills[0];
|
||||
var rhythm = (Rhythm)skills[1];
|
||||
var staminaRight = (Stamina)skills[2];
|
||||
var staminaLeft = (Stamina)skills[3];
|
||||
|
||||
double colourRating = colour.DifficultyValue() * colour_skill_multiplier;
|
||||
double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier;
|
||||
double staminaRating = (staminaRight.DifficultyValue() + staminaLeft.DifficultyValue()) * stamina_skill_multiplier;
|
||||
|
||||
double staminaPenalty = simpleColourPenalty(staminaRating, colourRating);
|
||||
staminaRating *= staminaPenalty;
|
||||
|
||||
double combinedRating = locallyCombinedDifficulty(colour, rhythm, staminaRight, staminaLeft, staminaPenalty);
|
||||
double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating);
|
||||
double starRating = 1.4 * separatedRating + 0.5 * combinedRating;
|
||||
starRating = rescale(starRating);
|
||||
|
||||
HitWindows hitWindows = new TaikoHitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||
|
||||
return new TaikoDifficultyAttributes
|
||||
{
|
||||
new TaikoModDoubleTime(),
|
||||
new TaikoModHalfTime(),
|
||||
new TaikoModEasy(),
|
||||
new TaikoModHardRock(),
|
||||
StarRating = starRating,
|
||||
Mods = mods,
|
||||
StaminaStrain = staminaRating,
|
||||
RhythmStrain = rhythmRating,
|
||||
ColourStrain = colourRating,
|
||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||
GreatHitWindow = (int)hitWindows.WindowFor(HitResult.Great) / clockRate,
|
||||
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
|
||||
Skills = skills
|
||||
};
|
||||
}
|
||||
|
||||
private double simpleColourPenalty(double staminaDifficulty, double colorDifficulty)
|
||||
{
|
||||
if (colorDifficulty <= 0) return 0.79 - 0.25;
|
||||
|
||||
return 0.79 - Math.Atan(staminaDifficulty / colorDifficulty - 12) / Math.PI / 2;
|
||||
}
|
||||
|
||||
private double norm(double p, params double[] values)
|
||||
{
|
||||
return Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p);
|
||||
}
|
||||
|
||||
private double rescale(double sr)
|
||||
{
|
||||
if (sr < 0) return sr;
|
||||
|
||||
return 10.43 * Math.Log(sr / 8 + 1);
|
||||
}
|
||||
|
||||
private double locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina staminaRight, Stamina staminaLeft, double staminaPenalty)
|
||||
{
|
||||
double difficulty = 0;
|
||||
double weight = 1;
|
||||
List<double> peaks = new List<double>();
|
||||
|
||||
for (int i = 0; i < colour.StrainPeaks.Count; i++)
|
||||
{
|
||||
double colourPeak = colour.StrainPeaks[i] * colour_skill_multiplier;
|
||||
double rhythmPeak = rhythm.StrainPeaks[i] * rhythm_skill_multiplier;
|
||||
double staminaPeak = (staminaRight.StrainPeaks[i] + staminaLeft.StrainPeaks[i]) * stamina_skill_multiplier * staminaPenalty;
|
||||
peaks.Add(norm(2, colourPeak, rhythmPeak, staminaPeak));
|
||||
}
|
||||
|
||||
foreach (double strain in peaks.OrderByDescending(d => d))
|
||||
{
|
||||
difficulty += strain * weight;
|
||||
weight *= 0.9;
|
||||
}
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user