public void TestMissViaEarlyHit()
        {
            var beatmap = new Beatmap
            {
                HitObjects = { new HitCircle {
                                   Position = new Vector2(256, 192)
                               } }
            };

            var hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);

            CreateModTest(new ModTestData
            {
                Autoplay = false,
                Mod      = new TestAutoMod(),
                Beatmap  = new Beatmap
                {
                    HitObjects = { new HitCircle {
                                       Position = new Vector2(256, 192)
                                   } }
                },
                PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset < -hitWindows.WindowFor(HitResult.Meh) && !Player.Results[0].IsHit
            });
        }
示例#2
0
        public OsuPlayfield()
        {
            InternalChildren = new Drawable[]
            {
                playfieldBorder = new PlayfieldBorder
                {
                    RelativeSizeAxes = Axes.Both,
                    Depth            = 3
                },
                spinnerProxies = new ProxyContainer
                {
                    RelativeSizeAxes = Axes.Both
                },
                followPoints = new FollowPointRenderer
                {
                    RelativeSizeAxes = Axes.Both,
                    Depth            = 2,
                },
                judgementLayer = new JudgementContainer <DrawableOsuJudgement>
                {
                    RelativeSizeAxes = Axes.Both,
                    Depth            = 1,
                },
                // Todo: This should not exist, but currently helps to reduce LOH allocations due to unbinding skin source events on judgement disposal
                // Todo: Remove when hitobjects are properly pooled
                new SkinProvidingContainer(null)
                {
                    Child = HitObjectContainer,
                },
                approachCircles = new ProxyContainer
                {
                    RelativeSizeAxes = Axes.Both,
                    Depth            = -1,
                },
            };

            hitPolicy     = new OrderedHitPolicy(HitObjectContainer);
            CheckHittable = hitPolicy.IsHittable;

            var hitWindows = new OsuHitWindows();

            foreach (var result in Enum.GetValues(typeof(HitResult)).OfType <HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
            {
                poolDictionary.Add(result, new DrawableJudgementPool(result));
            }

            AddRangeInternal(poolDictionary.Values);

            NewResult += onNewResult;
        }
        protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
        {
            HitWindows hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);

            hitWindowGreat = hitWindows.WindowFor(HitResult.Great) / clockRate;

            return(new Skill[]
            {
                new Aim(mods),
                new Speed(mods, hitWindowGreat),
                new Flashlight(mods)
            });
        }
示例#4
0
        protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
        {
            if (beatmap.HitObjects.Count == 0)
            {
                return new OsuDifficultyAttributes {
                           Mods = mods, Skills = skills
                }
            }
            ;

            double aimRating   = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
            double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;

            double baseAimPerformance   = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000;
            double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000;
            double basePerformance      = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1);
            double starRating           = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0;

            HitWindows hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);

            // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
            double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate;
            double preempt        = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;

            int maxCombo = beatmap.HitObjects.Count;

            // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
            maxCombo += beatmap.HitObjects.OfType <Slider>().Sum(s => s.NestedHitObjects.Count - 1);

            int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
            int spinnerCount    = beatmap.HitObjects.Count(h => h is Spinner);

            return(new OsuDifficultyAttributes
            {
                StarRating = starRating,
                Mods = mods,
                AimStrain = aimRating,
                SpeedStrain = speedRating,
                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo,
                HitCircleCount = hitCirclesCount,
                SpinnerCount = spinnerCount,
                Skills = skills
            });
        }
示例#5
0
        protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
        {
            HitWindows hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);

            // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
            hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate;

            return(new Skill[]
            {
                new Aim(mods),
                new Speed(mods, hitWindowGreat),
                new Flashlight(mods)
            });
        }
        protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate, double upTo = double.PositiveInfinity)
        {
            var hitObjects = double.IsPositiveInfinity(upTo)
                ? beatmap.HitObjects
                : beatmap.HitObjects.Where(h => h.StartTime <= upTo).ToList();

            if (hitObjects.Count == 0)
            {
                return new OsuDifficultyAttributes {
                           Mods = mods, Skills = skills
                }
            }
            ;

            double aimRating   = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
            double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
            double starRating  = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;

            HitWindows hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);

            // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
            double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate;
            double preempt        = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;

            int maxCombo = hitObjects.Count;

            // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
            maxCombo += hitObjects.OfType <Slider>().Sum(s => s.NestedHitObjects.Count - 1);

            int hitCircles = hitObjects.OfType <HitCircle>().Count();

            return(new OsuDifficultyAttributes
            {
                StarRating = starRating,
                Mods = mods,
                AimStrain = aimRating,
                SpeedStrain = speedRating,
                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo,
                CountHitCircles = hitCircles,
                Skills = skills
            });
        }
示例#7
0
        public void TestMissViaNotHitting()
        {
            var beatmap = new Beatmap
            {
                HitObjects = { new HitCircle {
                                   Position = new Vector2(256, 192)
                               } }
            };

            var hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);

            CreateModTest(new ModTestData
            {
                Autoplay      = false,
                Beatmap       = beatmap,
                PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset >= hitWindows.WindowFor(HitResult.Meh) && Player.Results[0].Type == HitResult.Miss
            });
        }
示例#8
0
        public OsuPlayfield()
        {
            Anchor = Anchor.Centre;
            Origin = Anchor.Centre;

            InternalChildren = new Drawable[]
            {
                playfieldBorder = new PlayfieldBorder {
                    RelativeSizeAxes = Axes.Both
                },
                spinnerProxies = new ProxyContainer {
                    RelativeSizeAxes = Axes.Both
                },
                FollowPoints = new FollowPointRenderer {
                    RelativeSizeAxes = Axes.Both
                },
                judgementLayer = new JudgementContainer <DrawableOsuJudgement> {
                    RelativeSizeAxes = Axes.Both
                },
                HitObjectContainer,
                judgementAboveHitObjectLayer = new Container {
                    RelativeSizeAxes = Axes.Both
                },
                approachCircles = new ProxyContainer {
                    RelativeSizeAxes = Axes.Both
                },
            };

            HitPolicy = new StartTimeOrderedHitPolicy();

            var hitWindows = new OsuHitWindows();

            foreach (var result in Enum.GetValues(typeof(HitResult)).OfType <HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
            {
                poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgementLoaded));
            }

            AddRangeInternal(poolDictionary.Values);

            NewResult += onNewResult;
        }
示例#9
0
        protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
        {
            var hitObjects = beatmap.HitObjects as List <OsuHitObject>;

            double mapLength = 0;

            if (beatmap.HitObjects.Count > 0)
            {
                mapLength = (beatmap.HitObjects.Last().StartTime - beatmap.HitObjects.First().StartTime) / 1000 / clockRate;
            }

            double preemptNoClockRate = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
            var    noteDensities      = NoteDensity.CalculateNoteDensities(hitObjects, preemptNoClockRate);

            // Tap
            var tapAttributes = Tap.CalculateTapAttributes(hitObjects, clockRate);

            // Finger Control
            double fingerControlDiff = FingerControl.CalculateFingerControlDiff(hitObjects, clockRate);

            // Aim
            var aimAttributes = Aim.CalculateAimAttributes(hitObjects, clockRate, tapAttributes.StrainHistory, noteDensities);

            double tapSr           = tap_multiplier * Math.Pow(tapAttributes.TapDifficulty, sr_exponent);
            double aimSr           = aim_multiplier * Math.Pow(aimAttributes.FcProbabilityThroughput, sr_exponent);
            double fingerControlSr = finger_control_multiplier * Math.Pow(fingerControlDiff, sr_exponent);
            double sr = Mean.PowerMean(new[] { tapSr, aimSr, fingerControlSr }, 7) * 1.131;

            HitWindows hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);

            // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
            double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate;
            double preempt        = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;

            int maxCombo = beatmap.HitObjects.Count;

            // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
            maxCombo += beatmap.HitObjects.OfType <Slider>().Sum(s => s.NestedHitObjects.Count - 1);

            return(new OsuDifficultyAttributes
            {
                StarRating = sr,
                Mods = mods,
                Length = mapLength,

                TapSr = tapSr,
                TapDiff = tapAttributes.TapDifficulty,
                StreamNoteCount = tapAttributes.StreamNoteCount,
                MashTapDiff = tapAttributes.MashedTapDifficulty,

                FingerControlSr = fingerControlSr,
                FingerControlDiff = fingerControlDiff,

                AimSr = aimSr,
                AimDiff = aimAttributes.FcProbabilityThroughput,
                AimHiddenFactor = aimAttributes.HiddenFactor,
                ComboTps = aimAttributes.ComboThroughputs,
                MissTps = aimAttributes.MissThroughputs,
                MissCounts = aimAttributes.MissCounts,
                CheeseNoteCount = aimAttributes.CheeseNoteCount,
                CheeseLevels = aimAttributes.CheeseLevels,
                CheeseFactors = aimAttributes.CheeseFactors,

                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo
            });
        }
示例#10
0
        protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
        {
            if (beatmap.HitObjects.Count == 0)
            {
                return new OsuDifficultyAttributes {
                           Mods = mods, Skills = skills
                }
            }
            ;

            List <double> combinedBonuses = calculateCombinedBonuses(skills);
            int           totalHits       = beatmap.HitObjects.Count(h => h is HitCircle || h is Slider);

            Aim   aimSkill   = skills[0] as Aim;
            Speed speedSkill = skills[1] as Speed;

            //minor discrepancy here because the combined bonus is not taken into account for the length value
            double aimTotal   = aimSkill.LengthValue(totalHits) * 2;
            double speedTotal = speedSkill.LengthValue(totalHits) * 2;

            aimSkill.AddCombinedCorrection(combinedBonuses);
            speedSkill.AddCombinedCorrection(combinedBonuses, skills[4]);

            double aimDifficulty   = aimSkill.CombinedDifficultyValue();
            double speedDifficulty = speedSkill.CombinedDifficultyValue();

            double aimRating   = Math.Sqrt(aimDifficulty);
            double speedRating = Math.Sqrt(speedDifficulty);

            //consistency
            double totalAimBonus = Math.Pow(aimSkill.ConsistencyValue(aimDifficulty), 0.7) * 0.045
                                   + calculateMixedAimBonus(skills[2] as SnapAim, skills[3] as FlowAim);
            double totalSpeedBonus = Math.Pow(speedSkill.ConsistencyValue(speedDifficulty), 0.7) * 0.045;

            //calculate the SR first to avoid unnecessary inflation, this should pose no problems as it is just a display value
            double displayAim   = aimRating * (1.0 + totalAimBonus) * difficulty_multiplier;
            double displaySpeed = speedRating * (1.0 + totalSpeedBonus) * difficulty_multiplier;

            double starRating = displayAim + displaySpeed + Math.Abs(displayAim - displaySpeed) / 2;

            //length bonus
            double aimDiffMultiplier = 1.0 + Math.Pow(aimRating, 1.5) / 15000;

            aimTotal = Math.Pow(aimTotal, aimDiffMultiplier);

            double aimLengthBonus = 1.0 + 0.36 * Math.Min(1.0, aimTotal / 2000.0) +
                                    (aimTotal > 2000 ? Math.Log10(aimTotal / 2000.0) * 0.48 : 0.0);
            double speedLengthBonus = 1.0 + 0.1 * Math.Min(1.0, speedTotal / 2000.0) +
                                      (speedTotal > 2000 ? Math.Log10(speedTotal / 2000.0) * 0.2 : 0.0);

            totalAimBonus   += Math.Pow(aimLengthBonus, 0.33);
            totalSpeedBonus += Math.Pow(speedLengthBonus, 0.33);

            aimRating   *= totalAimBonus * difficulty_multiplier;
            speedRating *= totalSpeedBonus * difficulty_multiplier;

            HitWindows hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);

            // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
            double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate;
            double preempt        = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;

            int maxCombo = beatmap.HitObjects.Count;

            // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
            maxCombo += beatmap.HitObjects.OfType <Slider>().Sum(s => s.NestedHitObjects.Count - 1);

            int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
            int spinnerCount    = beatmap.HitObjects.Count(h => h is Spinner);

            return(new OsuDifficultyAttributes
            {
                StarRating = starRating,
                Mods = mods,
                AimStrain = aimRating,
                SpeedStrain = speedRating,
                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo,
                HitCircleCount = hitCirclesCount,
                SpinnerCount = spinnerCount,
                Skills = skills
            });
        }
示例#11
0
        protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
        {
            var hitObjects = beatmap.HitObjects as List <OsuHitObject>;

            if (beatmap.HitObjects.Count == 0)
            {
                return new OsuDifficultyAttributes {
                           Mods = mods
                }
            }
            ;

            double mapLength = (beatmap.HitObjects.Last().StartTime - beatmap.HitObjects.First().StartTime) / 1000 / clockRate;

            // Tap
            (var tapDiff, var streamNoteCount, var mashLevels, var tapSkills, var strainHistory) =
                Tap.CalculateTapAttributes(hitObjects, clockRate);

            // Finger Control
            double fingerControlDiff = FingerControl.CalculateFingerControlDiff(hitObjects, clockRate);

            // Aim
            (var aimDiff, var fcTimeTP, var comboTPs, var missTPs, var missCounts,
             var cheeseNoteCount, var cheeseLevels, var cheeseFactors, var graphText) =
                Aim.CalculateAimAttributes(hitObjects, clockRate, strainHistory);

            // graph for aim
            string graphFilePath = Path.Combine("cache", $"graph_{beatmap.BeatmapInfo.OnlineBeatmapID}.txt");

            File.WriteAllText(graphFilePath, graphText);

            double tapSR           = tapMultiplier * Math.Pow(tapDiff, srExponent);
            double aimSR           = aimMultiplier * Math.Pow(aimDiff, srExponent);
            double fingerControlSR = fingerControlMultiplier * Math.Pow(fingerControlDiff, srExponent);
            double sr = Mean.PowerMean(new double[] { tapSR, aimSR, fingerControlSR }, 7) * 1.131;

            HitWindows hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);

            // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
            double hitWindowGreat = (int)(hitWindows.Great / 2) / clockRate;
            double preempt        = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;

            int maxCombo = beatmap.HitObjects.Count;

            // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
            maxCombo += beatmap.HitObjects.OfType <Slider>().Sum(s => s.NestedHitObjects.Count - 1);

            return(new OsuDifficultyAttributes
            {
                StarRating = sr,
                Mods = mods,
                Length = mapLength,

                TapSR = tapSR,
                TapDiff = tapDiff,
                StreamNoteCount = streamNoteCount,
                MashLevels = mashLevels,
                TapSkills = tapSkills,

                FingerControlSR = fingerControlSR,
                FingerControlDiff = fingerControlDiff,

                AimSR = aimSR,
                AimDiff = aimDiff,
                ComboTPs = comboTPs,
                MissTPs = missTPs,
                MissCounts = missCounts,
                CheeseNoteCount = cheeseNoteCount,
                CheeseLevels = cheeseLevels,
                CheeseFactors = cheeseFactors,

                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo
            });
        }