Skip to content

PP and star calculation for all osu! gamemodes

License

Notifications You must be signed in to change notification settings

Niotid/akatsuki-pp-rs

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

crates.io docs

rosu-pp

A standalone crate to calculate star ratings and performance points for all osu! gamemodes.

Async is supported through features, see below.

Usage

use rosu_pp::{Beatmap, BeatmapExt};

// Parse the map yourself
let map = match Beatmap::from_path("/path/to/file.osu") {
    Ok(map) => map,
    Err(why) => panic!("Error while parsing map: {}", why),
};

// If `BeatmapExt` is included, you can make use of
// some methods on `Beatmap` to make your life simpler.
let result = map.pp()
    .mods(24) // HDHR
    .combo(1234)
    .accuracy(99.2)
    .misses(2)
    .calculate();

println!("PP: {}", result.pp());

// If you want to reuse the current map-mod combination, make use of the previous result!
// If attributes are given, then stars & co don't have to be recalculated.
let next_result = map.pp()
    .mods(24) // HDHR
    .attributes(result) // recycle
    .combo(543)
    .misses(5)
    .n50(3)
    .accuracy(96.5)
    .calculate();

println!("Next PP: {}", next_result.pp());

let stars = map.stars()
    .mods(16)  // HR
    .calculate()
    .stars();

let max_pp = map.max_pp(16).pp();

println!("Stars: {} | Max PP: {}", stars, max_pp);

With async

If either the async_tokio or async_std feature is enabled, beatmap parsing will be async.

use rosu_pp::{Beatmap, BeatmapExt};

// Parse the map asynchronously
let map = match Beatmap::from_path("/path/to/file.osu").await {
    Ok(map) => map,
    Err(why) => panic!("Error while parsing map: {}", why),
};

// The rest stays the same
let result = map.pp()
    .mods(24) // HDHR
    .combo(1234)
    .n_misses(2)
    .accuracy(99.2)
    .calculate();

println!("PP: {}", result.pp());

Gradual calculation

Sometimes you might want to calculate the difficulty of a map or performance of a score after each hit object. This could be done by using passed_objects as the amount of objects that were passed so far. However, this requires to recalculate the beginning again and again, we can be more efficient than that.

Instead, you should use GradualDifficultyAttributes and GradualPerformanceAttributes:

use rosu_pp::{
    Beatmap, BeatmapExt, GradualPerformanceAttributes, ScoreState, taiko::TaikoScoreState,
};

let map = match Beatmap::from_path("/path/to/file.osu") {
    Ok(map) => map,
    Err(why) => panic!("Error while parsing map: {}", why),
};

let mods = 8 + 64; // HDDT

// If you're only interested in the star rating or other difficulty value,
// use `GradualDifficultyAttributes`, either through its function `new`
// or through the method `BeatmapExt::gradual_difficulty`.
let gradual_difficulty = map.gradual_difficulty(mods);

// Since `GradualDifficultyAttributes` implements `Iterator`, you can use
// any iterate function on it, use it in loops, collect them into a `Vec`, ...
for (i, difficulty) in gradual_difficulty.enumerate() {
    println!("Stars after object {}: {}", i, difficulty.stars());
}

// Gradually calculating performance values does the same as calculating
// difficulty attributes but it goes the extra step and also evaluates
// the state of a score for these difficulty attributes.
let mut gradual_performance = map.gradual_performance(mods);

// The default score state is kinda chunky because it considers all modes.
let state = ScoreState {
    max_combo: 1,
    n_geki: 0, // only relevant for mania
    n_katu: 0, // only relevant for mania and ctb
    n300: 1,
    n100: 0,
    n50: 0,
    n_misses: 0,
};

// Process the score state after the first object
let curr_performance = match gradual_performance.process_next_object(state) {
    Some(perf) => perf,
    None => panic!("the map has no hit objects"),
};

println!("PP after the first object: {}", curr_performance.pp());

// If you're only interested in maps of a specific mode, consider
// using the mode's gradual calculator instead of the general one.
// Let's assume it's a taiko map.
// Instead of starting off with `BeatmapExt::gradual_performance` one could have
// created the struct via `TaikoGradualPerformanceAttributes::new`.
let mut gradual_performance = match gradual_performance {
    GradualPerformanceAttributes::Taiko(gradual) => gradual,
    _ => panic!("the map was not taiko but {:?}", map.mode),
};

// A little simpler than the general score state.
let state = TaikoScoreState {
    max_combo: 11,
    n300: 9,
    n100: 1,
    n_misses: 1,
};

// Process the next 10 objects in one go
let curr_performance = match gradual_performance.process_next_n_objects(state, 10) {
    Some(perf) => perf,
    None => panic!("the last `process_next_object` already processed the last object"),
};

println!("PP after the first 11 objects: {}", curr_performance.pp());

Features

Flag Description
default Beatmap parsing will be non-async
async_tokio Beatmap parsing will be async through tokio
async_std Beatmap parsing will be async through async-std

Version

A large portion of this repository is a port of osu!lazer's difficulty and performance calculation.

  • osu!:

    • osu!lazer: Commit 85adfc2df7d931164181e145377a6ced8db2bfb3 (Wed Sep 28 18:26:36 2022 +0300)
    • osu!tools: Commit 146d5916937161ef65906aa97f85d367035f3712 (Sat Oct 8 14:28:49 2022 +0900)
    • Article
  • taiko:

    • osu!lazer: Commit 234c6ac7998fbc6742503e1a589536255554e56a (Wed Oct 5 20:21:15 2022 +0900)
    • osu!tools: Commit 146d5916937161ef65906aa97f85d367035f3712 (Sat Oct 8 14:28:49 2022 +0900)
    • Article
  • catch: (will be updated on the next rework)

    • osu!lazer: -
    • osu!tools: -
  • mania:

    • osu!lazer: Commit 7342fb7f51b34533a42bffda89c3d6c569cc69ce (Tue Oct 11 14:34:50 2022 +0900)
    • osu!tools: Commit 146d5916937161ef65906aa97f85d367035f3712 (Sat Oct 8 14:28:49 2022 +0900)
    • Article

Accuracy

The difficulty and performance attributes generated by osu-tools itself were compared with rosu-pp's results when running on 130,000 different maps. Additionally, multiple mod combinations were tested depending on the mode:

  • osu!: NM, EZ, HD, HR, DT
  • taiko: NM, HD, HR, DT (+ all osu! converts)
  • catch: -
  • mania: NM, DT (+ all osu! converts)

For every (!) comparison of the star and pp values, the error margin was below 0.000000001, ensuing a great accuracy.

Benchmark

To be done

Bindings

Using rosu-pp from other languages than Rust:

About

PP and star calculation for all osu! gamemodes

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 100.0%