Crates.io | minacalc-rs |
lib.rs | minacalc-rs |
version | 0.2.2 |
created_at | 2025-08-12 20:58:37.782876+00 |
updated_at | 2025-09-07 10:27:29.670708+00 |
description | Rust bindings for MinaCalc C++ library (Etterna rating calculator) |
homepage | |
repository | https://github.com/Glubus/minacalc-rs |
max_upload_size | |
id | 1792890 |
size | 449,300 |
Safe and ergonomic Rust bindings for the MinaCalc rhythm game difficulty calculator. This crate provides high-level Rust APIs for calculating difficulty scores in rhythm games like StepMania, Etterna, and osu!.
cc
and bindgen
(automatically handled by Cargo)Add this to your Cargo.toml
:
[dependencies]
minacalc-rs = "0.2.1"
The crate supports several optional features:
[dependencies]
minacalc-rs = { version = "0.2.1", features = ["hashmap", "thread", "osu", "utils"] }
hashmap
(default): Provides HashMap conversion for MSD resultsthread
: Provides thread-safe calculator poolosu
: Provides osu! beatmap parsing and calculationutils
: Provides utility functions for pattern analysisuse minacalc_rs::{Calc, Note};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a calculator instance
let calc = Calc::new()?;
// Define some notes (4K chart)
let notes = vec![
Note { notes: 1, row_time: 0.0 }, // Left column at 0s
Note { notes: 8, row_time: 1.0 }, // Right column at 1s
Note { notes: 15, row_time: 2.0 }, // All columns at 2s
];
// Calculate MSD scores for all rates
let msd_results = calc.calc_msd(¬es)?;
// Access scores for specific rates
let overall_1x = msd_results.msds[3].overall; // 1.0x rate
let stream_2x = msd_results.msds[13].stream; // 2.0x rate
println!("1.0x Overall: {:.2}", overall_1x);
println!("2.0x Stream: {:.2}", stream_2x);
Ok(())
}
use minacalc_rs::{Calc, Note, HashMapCalcExt};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let calc = Calc::new()?;
let notes = vec![
Note { notes: 1, row_time: 0.0 },
Note { notes: 8, row_time: 1.0 },
];
let msd_results = calc.calc_msd(¬es)?;
// Convert to HashMap for easy access
let hashmap = msd_results.as_hashmap()?;
// Access by rate string
if let Some(scores) = hashmap.get("1.0") {
println!("1.0x: Overall={:.2}, Stream={:.2}",
scores.overall, scores.stream);
}
Ok(())
}
use minacalc_rs::{Calc, OsuCalcExt};
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let calc = Calc::new()?;
// Analyze an osu! beatmap file
let beatmap_path = PathBuf::from("path/to/beatmap.osu");
let msd_results = calc.calculate_msd_from_osu_file(beatmap_path)?;
// Access scores for different rates
let rates = [0.7, 1.0, 1.5, 2.0];
let rate_indices = [0, 3, 8, 13];
for (rate, &index) in rates.iter().zip(rate_indices.iter()) {
if index < msd_results.msds.len() {
let scores = msd_results.msds[index];
println!("{:.1}x: Overall={:.2}, Stream={:.2}, Tech={:.2}",
rate, scores.overall, scores.stream, scores.technical);
}
}
Ok(())
}
use minacalc_rs::{Calc, Note, utils::*};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let calc = Calc::new()?;
let notes = vec![
Note { notes: 1, row_time: 0.0 },
Note { notes: 8, row_time: 1.0 },
];
let msd_results = calc.calc_msd(¬es)?;
// Get top 3 patterns for 1.0x rate
let top_patterns = calculate_highest_patterns(&msd_results.msds[3], 3);
println!("Top 3 patterns: {:?}", top_patterns);
// Get all patterns ranked by difficulty
let all_patterns = calculate_highest_patterns(&msd_results.msds[3], 7);
println!("All patterns ranked: {:?}", all_patterns);
Ok(())
}
use minacalc_rs::thread::{CalcPool, Note};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a pool of calculators
let pool = CalcPool::new(4)?; // 4 calculator instances
let notes = vec![
Note { notes: 1, row_time: 0.0 },
Note { notes: 8, row_time: 1.0 },
];
// Calculate MSD using the pool
let msd_results = pool.calc_msd(¬es)?;
println!("Overall difficulty: {:.2}", msd_results.msds[3].overall);
Ok(())
}
Calc
: Main calculator instanceNote
: Represents a note in a rhythm game chartAllRates
: Contains MSD scores for all music rates (0.7x to 2.0x)SkillsetScores
: Individual skillset scores (stream, jumpstream, etc.)The calculator provides scores for these skillsets:
Scores are calculated for 14 different music rates:
The crate includes several examples demonstrating different use cases:
basic_usage
: Basic MSD calculationosu
: osu! beatmap analysisutils_example
: Pattern analysis utilitiesRun examples with:
# Basic usage
cargo run --example basic_usage
# osu! beatmap analysis (requires osu feature)
cargo run --example osu --features="osu hashmap"
# Utils example (requires utils feature)
cargo run --example utils_example --features="utils"
The crate uses custom error types for different failure modes:
MinaCalcError
: General calculation errorsOsuError
: osu! beatmap parsing errorsAll functions return Result<T, E>
for proper error handling.
The Calc
type is not Send
or Sync
by default. For multi-threaded applications, use the CalcPool
from the thread
feature, which provides a thread-safe pool of calculator instances.
CalcPool
for concurrent calculationsContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.
utils
feature with pattern analysis functionsosu
feature for beatmap parsingthread
feature for thread-safe calculator poolsAllRates
instead of MsdForAllRates
)