// apply any extra hits as points according to the match configuration options private void UpdateScores(Section section, Bed bed, ScoringMode scoringMode) { // examine how many points have been scored this throw // (hits on score board + hits this throw - 3) * section value int playerHits = (from score in scores where score.player == currentPlayer from state in score.states where state.section == section select state.count).FirstOrDefault(); // check that any points count, that is that there is at least one other player who hasn't checked off this section Boolean sectionClosed = 3 == scores.Where(score => score.player != currentPlayer) .Min(score => score.states.Where(state => state.section == section) .Min(state => state.count)); int pointsScored = sectionClosed ? 0 : Math.Max(0, (playerHits + (int)bed - 3) * (int)section); // Review all player scores // update section states for the current player only // update the section state for the section which was hit and only up to the maximum number of hits (3) scores = ( from score in scores select score.player == currentPlayer ? new GameScore(score.player , score.order , (from state in score.states select state.section == section ? new GameScoreSectionState(state.section , state.count + (int)bed > 3 ? 3 : state.count + (int)bed) : state ).ToList() , (scoringMode == ScoringMode.Standard) ? score.points + pointsScored : score.points , score.ranking) : new GameScore(score.player , score.order , score.states , points: (scoringMode == ScoringMode.CutThroat) ? score.points + pointsScored : score.points , ranking: score.ranking) ).ToList(); // Calculate the new ranking now that the scores have been updated // These rankings must consider the effective points, bearing in mind states and scoring mode // Standard: effective points = SUM(state.section * state.count) + points // CutThroat: effective points = SUM(state.section * state.count) - points scores = ( from score in scores let hiddenPoints = score.states.Sum(s => s.count * (int)s.section) let effectivePoints = (scoringMode == ScoringMode.CutThroat) ? hiddenPoints - score.points : hiddenPoints + score.points orderby effectivePoints descending select score )//.ToList() .Select((s, i) => new GameScore(s.player , s.order , s.states , s.points , ranking: i + 1)) .OrderBy(s => s.order) .ToList(); }
public async Task <long> GetTotalScoreAsync([NotNull] ScoreInfo score, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { // TODO: This is required for playlist aggregate scores. They should likely not be getting here in the first place. if (string.IsNullOrEmpty(score.BeatmapInfo.MD5Hash)) { return(score.TotalScore); } int?beatmapMaxCombo = await GetMaximumAchievableComboAsync(score, cancellationToken).ConfigureAwait(false); if (beatmapMaxCombo == null) { return(score.TotalScore); } if (beatmapMaxCombo == 0) { return(0); } var ruleset = score.Ruleset.CreateInstance(); var scoreProcessor = ruleset.CreateScoreProcessor(); scoreProcessor.Mods.Value = score.Mods; return((long)Math.Round(scoreProcessor.ComputeFinalLegacyScore(mode, score, beatmapMaxCombo.Value))); }
public Boggle(ScoringMode score_mode = ScoringMode.SCRABBLE, BoardSize board_size = BoardSize.BIGBOGGLE, BoundaryMode bound_mode = BoundaryMode.STANDARD, int time_limit = 180000, int min_length = 3) { status = GameStatus.PRE_GAME; score = 0; mode = score_mode; size = board_size; minLength = min_length; gameLength = time_limit; bound = bound_mode; dice = new string[(int)size, 6]; //Create the array to hold dice values position = new int[(int)size]; //Array of positions on board board = new string[(int)size]; //Array containing the layout of the board gameTimer.Enabled = false; gameTimer.Interval = (double)gameLength; gameTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); current_word_list = "../../enable2k.txt"; if (size == BoardSize.STANDARD) { current_dice_set = "../../BoggleDice.txt"; } else { current_dice_set = "../../BigBoggleDice.txt"; } LoadDice(); LoadWordList(); ShuffleBoard(); }
internal void Throw(Section?section, Bed?bed, ScoringMode scoringMode, int maxRounds = 0) { if (section.HasValue && bed.HasValue) { // A valid throw has been made and needs to be applied to the current game UpdateScores(section.Value, bed.Value, scoringMode); UpdateGameCompletionStatus(scoringMode, maxRounds); } // increment the current dart every throw currentDart = NextDart(currentDart); // rotate player every 3 darts if (currentDart == 1) { currentPlayer = NextValueFromRotatingList(currentPlayer , (from s in scores orderby s.order select s.player).ToList()); } // increment current round once we hit the first player again currentRound = (currentDart == 1 && currentPlayer == scores.First().player) ? currentRound + 1 : currentRound; }
[TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 214)] // (((3 * 0 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25) public void TestSmallTicksAccuracy(ScoringMode scoringMode, HitResult hitResult, int expectedScore) { IEnumerable <HitObject> hitObjects = Enumerable .Repeat(new TestHitObject(HitResult.SmallTickHit), 4) .Append(new TestHitObject(HitResult.Ok)); IBeatmap fiveObjectBeatmap = new TestBeatmap(new RulesetInfo()) { HitObjects = hitObjects.ToList() }; scoreProcessor.Mode.Value = scoringMode; scoreProcessor.ApplyBeatmap(fiveObjectBeatmap); for (int i = 0; i < 4; i++) { var judgementResult = new JudgementResult(fiveObjectBeatmap.HitObjects[i], new Judgement()) { Type = i == 2 ? HitResult.SmallTickMiss : hitResult }; scoreProcessor.ApplyResult(judgementResult); } var lastJudgementResult = new JudgementResult(fiveObjectBeatmap.HitObjects.Last(), new Judgement()) { Type = HitResult.Ok }; scoreProcessor.ApplyResult(lastJudgementResult); Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d)); }
public void ThrowDart_2Players_SectionComplete_ScoresNotUpdated(ScoringMode mode, int rounds, Section section) { List <String> players = new List <String>(); players.Add("Player1"); players.Add("Player2"); var match = new Match(players, mode, rounds); match.Throw(section, Bed.Single); match.Throw(section, Bed.Single); match.Throw(section, Bed.Single); match.Throw(section, Bed.Single); match.Throw(section, Bed.Single); match.Throw(section, Bed.Single); // check that the section is complete (6 for a 2 player game) var countTicks = match.currentGame .scores .Aggregate(0, (total, score) => total + score.states .Where(state => state.section == section) .Aggregate(0, (t, state) => t + state.count)); Assert.Equal(6, countTicks); // and that no unexpected points were captured Assert.Equal(0, match.currentGame.scores[0].points); Assert.Equal(0, match.currentGame.scores[1].points); // Finally, check that a further hit doesn't result in a score update, regardless of game mode match.Throw(section, Bed.Single); Assert.Equal(0, match.currentGame.scores[0].points); Assert.Equal(0, match.currentGame.scores[1].points); }
public void TestEmptyBeatmap( [Values(ScoringMode.Standardised, ScoringMode.Classic)] ScoringMode scoringMode) { scoreProcessor.Mode.Value = scoringMode; scoreProcessor.ApplyBeatmap(new TestBeatmap(new RulesetInfo())); Assert.IsTrue(Precision.AlmostEquals(0, scoreProcessor.TotalScore.Value)); }
public void TestEmptyBeatmap( [Values(ScoringMode.Standardised, ScoringMode.Classic)] ScoringMode scoringMode) { scoreProcessor.Mode.Value = scoringMode; scoreProcessor.ApplyBeatmap(new TestBeatmap(new RulesetInfo())); Assert.That(scoreProcessor.TotalScore.Value, Is.Zero); }
/// <summary> /// Retrieves the total score of a <see cref="ScoreInfo"/> in the given <see cref="ScoringMode"/>. /// </summary> /// <param name="score">The <see cref="ScoreInfo"/> to calculate the total score of.</param> /// <param name="mode">The <see cref="ScoringMode"/> to return the total score as.</param> /// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the process.</param> /// <returns>The total score.</returns> public async Task <long> GetTotalScoreAsync([NotNull] ScoreInfo score, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { // TODO: This is required for playlist aggregate scores. They should likely not be getting here in the first place. if (string.IsNullOrEmpty(score.BeatmapInfo.MD5Hash)) { return(score.TotalScore); } int beatmapMaxCombo; if (score.IsLegacyScore) { // This score is guaranteed to be an osu!stable score. // The combo must be determined through either the beatmap's max combo value or the difficulty calculator, as lazer's scoring has changed and the score statistics cannot be used. if (score.BeatmapInfo.MaxCombo != null) { beatmapMaxCombo = score.BeatmapInfo.MaxCombo.Value; } else { if (difficulties == null) { return(score.TotalScore); } // We can compute the max combo locally after the async beatmap difficulty computation. var difficulty = await difficulties().GetDifficultyAsync(score.BeatmapInfo, score.Ruleset, score.Mods, cancellationToken).ConfigureAwait(false); // Something failed during difficulty calculation. Fall back to provided score. if (difficulty == null) { return(score.TotalScore); } beatmapMaxCombo = difficulty.Value.MaxCombo; } } else { // This is guaranteed to be a non-legacy score. // The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values. beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType <HitResult>().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetValueOrDefault(r)).Sum(); } if (beatmapMaxCombo == 0) { return(0); } var ruleset = score.Ruleset.CreateInstance(); var scoreProcessor = ruleset.CreateScoreProcessor(); scoreProcessor.Mods.Value = score.Mods; return((long)Math.Round(scoreProcessor.ComputeFinalLegacyScore(mode, score, beatmapMaxCombo))); }
/// <summary> /// Creates scoring object with the given full score and mode /// </summary> /// <param name="CurrentMode">Current scoring mode</param> /// <param name="FullScore">Full score points</param> /// <param name="CaseSensitive">Indicates if this criteria is case sensitive. Makes sense in specific contexts /// False by default.</param> public Scoring(ScoringMode CurrentMode, int FullScore) { if (FullScore < 1) { throw new Exception("Liczba punktów nie może być mniejsza od 1!"); } Mode = CurrentMode; FullPointScore = FullScore; }
public void UpdateScore(ScoreProcessor processor, ScoringMode mode) { if (LastHeader == null) { return; } (score.Value, accuracy.Value) = processor.GetScoreAndAccuracy(mode, LastHeader.MaxCombo, LastHeader.Statistics); currentCombo.Value = LastHeader.Combo; }
public void UpdateScore(ScoreProcessor processor, ScoringMode mode) { if (LastHeader == null) { return; } score.Value = processor.GetImmediateScore(mode, LastHeader.MaxCombo, LastHeader.Statistics); accuracy.Value = LastHeader.Accuracy; currentCombo.Value = LastHeader.Combo; }
public static List <string> SelectRandomTagSet(ScoringMode scoringMode) { List <string> tagSet = DBGWAPLoader.GenerateRandomTagset(); if (scoringMode == ScoringMode.Both) { string lastTag = tagSet[tagSet.Count - 1]; tagSet[tagSet.Count - 1] = lastTag + "-compete"; tagSet.Add(lastTag + "-collab"); } return(tagSet); }
// The game is complete once a player has hit all targets and has the most points (least for cutthroat mode) // or if all rounds have completed private void UpdateGameCompletionStatus(ScoringMode scoringMode, int maxRounds) { if (maxRounds < currentRound && maxRounds > 0) { complete = true; return; } complete = sumTargetsPerPlayerPerGame == (scores.OrderBy(score => (scoringMode == ScoringMode.CutThroat) ? score.points : -score.points) .First() .states .Sum(s => s.count * (int)s.section)); }
private double getScore(ScoringMode mode) { switch (mode) { default: case ScoringMode.Standardised: return (max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo.Value / maxHighestCombo) + bonusScore) * scoreMultiplier; case ScoringMode.Classic: // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) return bonusScore + baseScore * ((1 + Math.Max(0, HighestCombo.Value - 1) * scoreMultiplier) / 25); } }
public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore) { scoreProcessor.Mode.Value = scoringMode; scoreProcessor.ApplyBeatmap(beatmap); var judgementResult = new JudgementResult(beatmap.HitObjects.Single(), new OsuJudgement()) { Type = hitResult }; scoreProcessor.ApplyResult(judgementResult); Assert.IsTrue(Precision.AlmostEquals(expectedScore, scoreProcessor.TotalScore.Value)); }
public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore) { scoreProcessor.Mode.Value = scoringMode; scoreProcessor.ApplyBeatmap(beatmap); var judgementResult = new JudgementResult(beatmap.HitObjects.Single(), new OsuJudgement()) { Type = hitResult }; scoreProcessor.ApplyResult(judgementResult); Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d)); }
/// <summary> /// Start a new match from scratch with an empty scoreboard /// </summary> public Match(List <String> players, ScoringMode scoringMode, int maxRounds) { this.scoringMode = scoringMode; this.maxRounds = maxRounds; scores = ( from player in players select new MatchScore(player, 0, 0) ).ToList(); currentGame = new Game(players); // defer initial game creation logic to the Game class currentGameHistory = new Stack <Game>(); previousGames = new List <Game>(); }
[TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 450)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 50 (bonus points) public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore) { var minResult = new TestJudgement(hitResult).MinResult; IBeatmap fourObjectBeatmap = new TestBeatmap(new RulesetInfo()) { HitObjects = new List <HitObject>(Enumerable.Repeat(new TestHitObject(maxResult), 4)) }; scoreProcessor.Mode.Value = scoringMode; scoreProcessor.ApplyBeatmap(fourObjectBeatmap); for (int i = 0; i < 4; i++) { var judgementResult = new JudgementResult(fourObjectBeatmap.HitObjects[i], new Judgement()) { Type = i == 2 ? minResult : hitResult }; scoreProcessor.ApplyResult(judgementResult); } Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d)); }
public void ThrowDart_2Players_SectionCompleteWithinThrow_ScoresNotUpdated(ScoringMode mode, int rounds, Section section) { List <String> players = new List <String>(); players.Add("Player1"); players.Add("Player2"); var match = new Match(players, mode, rounds); match.Throw(section, Bed.Single); match.Throw(section, Bed.Single); match.Throw(section, null); // first player not completed section match.Throw(section, Bed.Single); match.Throw(section, Bed.Single); match.Throw(section, Bed.Single); // second player completed section but no scores yet Assert.Equal(0, match.currentGame.scores[0].points); Assert.Equal(0, match.currentGame.scores[1].points); // first player scores multiple, this should not result in a score update, regardless of game mode match.Throw(section, Bed.Double); Assert.Equal(0, match.currentGame.scores[0].points); Assert.Equal(0, match.currentGame.scores[1].points); }
public bool SetMode(int m) { if (hasReceivedMode) { return(false); } switch (m) { // 1-indexing to be consistent with the DB. case 1: GameState.Singleton.ScoringMode = ScoringMode.Collaborative; this.mode = ScoringMode.Collaborative; break; case 2: GameState.Singleton.ScoringMode = ScoringMode.Competitive; this.mode = ScoringMode.Competitive; break; case 3: GameState.Singleton.ScoringMode = ScoringMode.Both; this.mode = ScoringMode.Both; break; default: return(false); // WE SHOULD NEVER GET HERE } hasReceivedMode = true; // We got the tags before we got the mode, so we need to go back and resolve it. if (hasReceivedTags) { ResolveTags(); } return(true); }
// Use this for initialization void Start() { // XXX (kasiu): Remove eventually. LoadTemporaryTextAsset(); ScoringMode mode = SelectRandomScoringMode(); List <string> objectSet = SelectRandomSubset(numObjects, allObjects); List <string> tagSet = SelectRandomSubset(2, allTags); // HACK (kasiu): if (mode == ScoringMode.Both) { tagSet.Add(tagSet[tagSet.Count - 1]); } // Set things. GameState.Singleton.ScoringMode = mode; // Modify the spawner GameObject spawner = GameObject.Find("Spawner"); if (spawner != null && (spawner.GetComponent <LoadObject>() != null)) { if (mode == ScoringMode.Both) { spawner.GetComponent <LoadObject>().prefab = prefabs[1]; } else { spawner.GetComponent <LoadObject>().prefab = prefabs[0]; } spawner.GetComponent <LoadObject>().objectNames = objectSet; spawner.GetComponent <LoadObject>().tagNames = tagSet; } }
private void radModeStandard_CheckedChanged(object sender, EventArgs e) { if (radModeStandard.Checked) { scoreMode = ScoringMode.STANDARD; } }
private void radModeScrabble_CheckedChanged(object sender, EventArgs e) { if (radModeScrabble.Checked) { scoreMode = ScoringMode.SCRABBLE; } }
private void radModePointPer_CheckedChanged(object sender, EventArgs e) { if (radModePointPer.Checked) { scoreMode = ScoringMode.POINTPERLETTER; } }
public void GetTotalScore([NotNull] ScoreInfo score, [NotNull] Action <long> callback, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { GetTotalScoreAsync(score, mode, cancellationToken) .ContinueWith(task => scheduler.Add(() => { if (!cancellationToken.IsCancellationRequested) { callback(task.GetResultSafely()); } }), TaskContinuationOptions.OnlyOnRanToCompletion); }
public static List <Triple <double, string, string> > SelectRandomPartnerTrace(List <string> objects, List <string> tags, float minTime, float maxTime, ScoringMode mode) { List <Triple <double, string, string> > list = new List <Triple <double, string, string> >(); int numTimes = Random.Range(1, objects.Count + 1); List <string> newTags; if (mode == ScoringMode.Both) { // MODIFY TAGS string myTag = TagUtils.TrimBothModeTag(tags[1]); string opponentTag = tags[0]; newTags = new List <string>(); newTags.Add(myTag); newTags.Add(opponentTag + "-compete"); newTags.Add(opponentTag + "-collab"); } else { newTags = tags; } // HACK (kasiu): Doesn't guarantee a good time distribution. List <double> times = new List <double>(); for (int i = 0; i < numTimes; i++) { times.Add(Random.Range(minTime, maxTime)); } times.Sort(); List <string> objectSubset = SelectRandomSubset(numTimes, objects); for (int i = 0; i < numTimes; i++) { Triple <double, string, string> triple = new Triple <double, string, string>(); triple.First = times[i]; triple.Second = objectSubset[i]; int randomTag = Random.Range(0, newTags.Count); triple.Third = newTags[randomTag]; list.Add(triple); } return(list); }
/// <summary> /// Retrieves the total score of a <see cref="ScoreInfo"/> in the given <see cref="ScoringMode"/>. /// The score is returned in a callback that is run on the update thread. /// </summary> /// <param name="score">The <see cref="ScoreInfo"/> to calculate the total score of.</param> /// <param name="callback">The callback to be invoked with the total score.</param> /// <param name="mode">The <see cref="ScoringMode"/> to return the total score as.</param> /// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the process.</param> public void GetTotalScore([NotNull] ScoreInfo score, [NotNull] Action <long> callback, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { GetTotalScoreAsync(score, mode, cancellationToken) .ContinueWith(s => scheduler.Add(() => callback(s.Result)), TaskContinuationOptions.OnlyOnRanToCompletion); }
public BoggleGUI(Options c, Color valid_move, Color selected_move, ScoringMode score_mode = ScoringMode.SCRABBLE, BoardSize board_size = BoardSize.BIGBOGGLE, BoundaryMode bound_mode = BoundaryMode.STANDARD, int t_limit = 180000, int min_length = 3) { InitializeComponent(); caller = (Options)c; colorValid = valid_move; colorSelected = selected_move; scoreMode = score_mode; boardSize = board_size; boundMode = bound_mode; timeLimit = t_limit; wordMin = min_length; string dice = caller.customDice; string word = caller.customWords; //Create psuedo control array btnArray = new Button[(int)boardSize]; btnArray[0] = button1; btnArray[1] = button2; btnArray[2] = button3; btnArray[3] = button4; btnArray[4] = button5; btnArray[5] = button6; btnArray[6] = button7; btnArray[7] = button8; btnArray[8] = button9; btnArray[9] = button10; btnArray[10] = button11; btnArray[11] = button12; btnArray[12] = button13; btnArray[13] = button14; btnArray[14] = button15; btnArray[15] = button16; if (boardSize == BoardSize.BIGBOGGLE) // If Big Boggle need all the buttons { btnArray[16] = button17; btnArray[17] = button18; btnArray[18] = button19; btnArray[19] = button20; btnArray[20] = button21; btnArray[21] = button22; btnArray[22] = button23; btnArray[23] = button24; btnArray[24] = button25; } else // Else if Standard hide the extra buttons { button17.Visible = false; button18.Visible = false; button19.Visible = false; button20.Visible = false; button21.Visible = false; button22.Visible = false; button23.Visible = false; button24.Visible = false; button25.Visible = false; int btnNumber = 0; for (int row = 0; row < 4; row++) //Rearrange the buttons into a 4x4 grid { for (int col = 0; col < 4; col++) { Point myPoint = new Point(41 * (col % 4), (41 * row)); btnArray[btnNumber].Location = myPoint; btnNumber++; } } } }
/// <summary> /// Retrieves the total score of a <see cref="ScoreInfo"/> in the given <see cref="ScoringMode"/>. /// </summary> /// <param name="score">The <see cref="ScoreInfo"/> to calculate the total score of.</param> /// <param name="mode">The <see cref="ScoringMode"/> to return the total score as.</param> /// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the process.</param> /// <returns>The total score.</returns> public async Task <long> GetTotalScoreAsync([NotNull] ScoreInfo score, ScoringMode mode = ScoringMode.Standardised, CancellationToken cancellationToken = default) { // TODO: This is required for playlist aggregate scores. They should likely not be getting here in the first place. if (string.IsNullOrEmpty(score.BeatmapInfo.Hash)) { return(score.TotalScore); } int beatmapMaxCombo; double accuracy = score.Accuracy; if (score.IsLegacyScore) { if (score.RulesetID == 3) { // In osu!stable, a full-GREAT score has 100% accuracy in mania. Along with a full combo, the score becomes indistinguishable from a full-PERFECT score. // To get around this, recalculate accuracy based on the hit statistics. // Note: This cannot be applied universally to all legacy scores, as some rulesets (e.g. catch) group multiple judgements together. double maxBaseScore = score.Statistics.Select(kvp => kvp.Value).Sum() * Judgement.ToNumericResult(HitResult.Perfect); double baseScore = score.Statistics.Select(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value).Sum(); if (maxBaseScore > 0) { accuracy = baseScore / maxBaseScore; } } // This score is guaranteed to be an osu!stable score. // The combo must be determined through either the beatmap's max combo value or the difficulty calculator, as lazer's scoring has changed and the score statistics cannot be used. if (score.BeatmapInfo.MaxCombo != null) { beatmapMaxCombo = score.BeatmapInfo.MaxCombo.Value; } else { if (difficulties == null) { return(score.TotalScore); } // We can compute the max combo locally after the async beatmap difficulty computation. var difficulty = await difficulties().GetDifficultyAsync(score.BeatmapInfo, score.Ruleset, score.Mods, cancellationToken).ConfigureAwait(false); // Something failed during difficulty calculation. Fall back to provided score. if (difficulty == null) { return(score.TotalScore); } beatmapMaxCombo = difficulty.Value.MaxCombo; } } else { // This is guaranteed to be a non-legacy score. // The combo must be determined through the score's statistics, as both the beatmap's max combo and the difficulty calculator will provide osu!stable combo values. beatmapMaxCombo = Enum.GetValues(typeof(HitResult)).OfType <HitResult>().Where(r => r.AffectsCombo()).Select(r => score.Statistics.GetValueOrDefault(r)).Sum(); } if (beatmapMaxCombo == 0) { return(0); } var ruleset = score.Ruleset.CreateInstance(); var scoreProcessor = ruleset.CreateScoreProcessor(); scoreProcessor.Mods.Value = score.Mods; return((long)Math.Round(scoreProcessor.GetScore(mode, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics))); }