1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 12:17:46 +08:00

Reorder members for better readability

This commit is contained in:
Bartłomiej Dach 2020-08-22 17:51:35 +02:00
parent 7e2bef3b9f
commit 8ace7df0fd
6 changed files with 158 additions and 160 deletions

View File

@ -13,11 +13,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
private const int roll_min_repetitions = 12; private const int roll_min_repetitions = 12;
private const int tl_min_repetitions = 16; 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(3);
findRolls(4); findRolls(4);
findTlTap(0, HitType.Rim); findTlTap(0, HitType.Rim);

View File

@ -13,11 +13,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
{ {
public readonly TaikoDifficultyHitObjectRhythm Rhythm; public readonly TaikoDifficultyHitObjectRhythm Rhythm;
public readonly HitType? HitType; public readonly HitType? HitType;
public readonly int ObjectIndex;
public bool StaminaCheese; public bool StaminaCheese;
public readonly int ObjectIndex;
public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, int objectIndex) public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, int objectIndex)
: base(hitObject, lastObject, clockRate) : base(hitObject, lastObject, clockRate)
{ {
@ -45,8 +44,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
}; };
private TaikoDifficultyHitObjectRhythm getClosestRhythm(double ratio) private TaikoDifficultyHitObjectRhythm getClosestRhythm(double ratio)
{ => common_rhythms.OrderBy(x => Math.Abs(x.Ratio - ratio)).First();
return common_rhythms.OrderBy(x => Math.Abs(x.Ratio - ratio)).First();
}
} }
} }

View File

@ -12,11 +12,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
{ {
public class Colour : Skill public class Colour : Skill
{ {
private const int mono_history_max_length = 5;
protected override double SkillMultiplier => 1; protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 0.4; 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; private HitType? previousHitType;
/// <summary> /// <summary>
@ -24,11 +29,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
/// </summary> /// </summary>
private int currentMonoLength = 1; 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) protected override double StrainValueOf(DifficultyHitObject current)
{ {
if (!(current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000)) if (!(current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000))

View File

@ -14,17 +14,43 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
{ {
protected override double SkillMultiplier => 10; protected override double SkillMultiplier => 10;
protected override double StrainDecayBase => 0; 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 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 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 // Finds repetitions and applies penalties
@ -61,6 +87,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
return true; return true;
} }
private double repetitionPenalty(int notesSince) => Math.Min(1.0, 0.032 * notesSince);
private double patternLengthPenalty(int patternLength) private double patternLengthPenalty(int patternLength)
{ {
double shortPatternPenalty = Math.Min(0.15 * patternLength, 1.0); double shortPatternPenalty = Math.Min(0.15 * patternLength, 1.0);
@ -78,36 +106,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
return 0.0; 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() private void resetRhythmStrain()
{ {
currentStrain = 0.0; currentStrain = 0.0;

View File

@ -12,32 +12,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
{ {
public class Stamina : Skill public class Stamina : Skill
{ {
private readonly int hand;
protected override double SkillMultiplier => 1; protected override double SkillMultiplier => 1;
protected override double StrainDecayBase => 0.4; protected override double StrainDecayBase => 0.4;
private const int max_history_length = 2; private const int max_history_length = 2;
private readonly int hand;
private readonly LimitedCapacityQueue<double> notePairDurationHistory = new LimitedCapacityQueue<double>(max_history_length); private readonly LimitedCapacityQueue<double> notePairDurationHistory = new LimitedCapacityQueue<double>(max_history_length);
private double offhandObjectDuration = double.MaxValue; private double offhandObjectDuration = double.MaxValue;
// Penalty for tl tap or roll public Stamina(bool rightHand)
private double cheesePenalty(double notePairDuration)
{ {
if (notePairDuration > 125) return 1; hand = rightHand ? 1 : 0;
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;
} }
protected override double StrainValueOf(DifficultyHitObject current) protected override double StrainValueOf(DifficultyHitObject current)
@ -71,9 +58,22 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
return 0; 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;
} }
} }
} }

View File

@ -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; new Colour(),
new Rhythm(),
return 0.79 - Math.Atan(staminaDifficulty / colorDifficulty - 12) / Math.PI / 2; new Stamina(true),
} new Stamina(false),
};
private double norm(double p, params double[] values)
{ protected override Mod[] DifficultyAdjustmentMods => new Mod[]
return Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p); {
} new TaikoModDoubleTime(),
new TaikoModHalfTime(),
private double rescale(double sr) new TaikoModEasy(),
{ new TaikoModHardRock(),
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
}; };
}
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) 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; return taikoDifficultyHitObjects;
} }
protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
{ {
new Colour(), if (beatmap.HitObjects.Count == 0)
new Rhythm(), return new TaikoDifficultyAttributes { Mods = mods, Skills = skills };
new Stamina(true),
new Stamina(false),
};
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(), StarRating = starRating,
new TaikoModHalfTime(), Mods = mods,
new TaikoModEasy(), StaminaStrain = staminaRating,
new TaikoModHardRock(), 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;
}
}
} }