コード例 #1
0
        private static PPv2 CalcPP(BeatmapBase beatmap, LegacyMods mods = LegacyMods.NM, double acc = -1, int combo = -1, int misses = 0, bool cache = false)
        {
            try
            {
                var mapCachePath = Path.Combine(cache_path, $"{beatmap.Id}_{(int)(mods & LegacyMods.DifficultyChanging)}.json");
                var ppParams     = GetCachedPPv2Parameters(mapCachePath);
                if (ppParams != null)
                {
                    ppParams.Accuracy  = acc / 100;
                    ppParams.Combo     = combo;
                    ppParams.CountMiss = misses;
                    ppParams.Mods      = (Mods)mods;
                    return(new PPv2(ppParams));
                }

                using (var stream = new MemoryStream(beatmap.FileBytes, false))
                    using (var reader = new StreamReader(stream, true))
                    {
                        Beatmap  map  = Beatmap.Read(reader);
                        DiffCalc diff = new DiffCalc().Calc(map, (Mods)mods);
                        ppParams = new PPv2Parameters(map, diff, acc / 100, misses, combo, (Mods)mods);

                        if (cache)
                        {
                            CachePPv2Parameters(mapCachePath, ppParams);
                        }

                        return(new PPv2(ppParams));
                    }
            }
            catch (Exception e)
            {
                Log.Error($"CalcPP failed, ${e.InnerMessageIfAny()}");
            }

            return(null);
        }
コード例 #2
0
        public static double GetBeatmapPP(BeatmapBase map, LegacyMods mods, double acc)
        {
            var pp = CalcPP(map, mods, acc, cache: map.Ranked);

            return(pp?.Total ?? -1);
        }
コード例 #3
0
        public static double GetBeatmapPP(BeatmapBase map, ScoreBase score)
        {
            var pp = CalcPP(map, score.LegacyMods ?? LegacyMods.NM, score.Accuracy, (int)score.Combo, (int)score.Misses, map.Ranked);

            return(pp?.Total ?? -1);
        }
コード例 #4
0
        protected override void RunAllRules(List <HitObjectBase> hitObjects)
        {
            BeatmapBase Beatmap = OsuHelper.GetCurrentBeatmap();

            // Mods are not yet supported. TODO


            // Fill our custom tpHitObject class, that carries additional information
            tpHitObjects = new List <tpHitObject>(hitObjects.Count);
            float CircleRadius = (PLAYFIELD_WIDTH / 16.0f) * (1.0f - 0.7f * ((float)Beatmap.DifficultyCircleSize - 5.0f) / 5.0f);

            foreach (HitObjectBase hitObject in hitObjects)
            {
                tpHitObjects.Add(new tpHitObject(hitObject, CircleRadius));
            }

            // Sort tpHitObjects by StartTime of the HitObjects - just to make sure. Not using CompareTo, since it results in a crash (HitObjectBase inherits MarshalByRefObject)
            tpHitObjects.Sort((a, b) => a.BaseHitObject.StartTime - b.BaseHitObject.StartTime);


            if (CalculateStrainValues() == false)
            {
                Reports.Add(new AiReport(Severity.Error, "Could not compute strain values. Aborting difficulty calculation."));
                return;
            }


            double SpeedDifficulty = CalculateDifficulty(DifficultyType.Speed);
            double AimDifficulty   = CalculateDifficulty(DifficultyType.Aim);

            // OverallDifficulty is not considered in this algorithm and neither is HpDrainRate. That means, that in this form the algorithm determines how hard it physically is
            // to play the map, assuming, that too much of an error will not lead to a death.
            // It might be desirable to include OverallDifficulty into map difficulty, but in my personal opinion it belongs more to the weighting of the actual peformance
            // and is superfluous in the beatmap difficulty rating.
            // If it were to be considered, then I would look at the hit window of normal HitCircles only, since Sliders and Spinners are (almost) "free" 300s and take map length
            // into account as well.

            Reports.Add(new AiReport(Severity.Info, "Speed difficulty: " + SpeedDifficulty + " | Aim difficulty: " + AimDifficulty));

            // The difficulty can be scaled by any desired metric.
            // In osu!tp it gets squared to account for the rapid increase in difficulty as the limit of a human is approached. (Of course it also gets scaled afterwards.)
            // It would not be suitable for a star rating, therefore:

            // The following is a proposal to forge a star rating from 0 to 5. It consists of taking the square root of the difficulty, since by simply scaling the easier
            // 5-star maps would end up with one star.
            double SpeedStars = Math.Sqrt(SpeedDifficulty) * STAR_SCALING_FACTOR;
            double AimStars   = Math.Sqrt(AimDifficulty) * STAR_SCALING_FACTOR;

            Reports.Add(new AiReport(Severity.Info, "Speed stars: " + SpeedStars + " | Aim stars: " + AimStars));

            // Again, from own observations and from the general opinion of the community a map with high speed and low aim (or vice versa) difficulty is harder,
            // than a map with mediocre difficulty in both. Therefore we can not just add both difficulties together, but will introduce a scaling that favors extremes.
            double StarRating = SpeedStars + AimStars + Math.Abs(SpeedStars - AimStars) * EXTREME_SCALING_FACTOR;

            // Another approach to this would be taking Speed and Aim separately to a chosen power, which again would be equivalent. This would be more convenient if
            // the hit window size is to be considered as well.

            // Note: The star rating is tuned extremely tight! Airman (/b/104229) and Freedom Dive (/b/126645), two of the hardest ranked maps, both score ~4.66 stars.
            // Expect the easier kind of maps that officially get 5 stars to obtain around 2 by this metric. The tutorial still scores about half a star.
            // Tune by yourself as you please. ;)
            Reports.Add(new AiReport(Severity.Info, "Total star rating: " + StarRating));
        }