English | 简体中文
Euterpe ML predicts a chart's decimal difficulty rating as a regression problem, using only the note-structure data of the chart — the timing, lane, type, and hold information of each note. It deliberately ignores audio, cover art, song and charter names, the author-declared integer level, and the visual size of notes. Difficulty is treated as a property of what the hands have to do, not of presentation.
A chart is a sequence of timed note events. The pipeline turns that sequence into a fixed-width numeric feature vector, then maps the vector to a decimal difficulty with a gradient-boosted regressor. The modeling ideas that matter:
- Density as a distribution, not an average. A single "notes per second" number is too blunt. Density is measured in sliding windows (1s / 2s / 5s / 10s) and kept as a percentile profile (p50…p99, max, std) — a chart's hardest 5% of time tells you more than its mean.
- Patterns relative to the chart's own baseline. Absolute thresholds don't transfer across tempos. Runs, alternations, sandwiches, hold/mash interactions, tempo irregularity and the like are detected relative to each chart's own rhythmic baseline, after millisecond-level event clustering.
- Fusion over raw signals. A bare pattern count is weak. Patterns are fed to the model fused with the time window, local density, and local entropy in which they occur.
- Compressed difficulty dimensions. A final feature generation hands the model no raw signal at all — only a few fused scalar dimensions of difficulty per window, forcing the regressor to reason from compressed intent rather than memorize surface counts.
Evaluation uses song-grouped cross-validation: every chart of a given song is held out together, so reported error reflects songs the model has never seen.
Three independent, complementary feature generations are computed for every chart and concatenated:
| Module | Generation | Idea |
|---|---|---|
features.py |
v1 — compound NPS superposition (~96 dims) | Windowed density percentile statistics across multiple horizons, basic structural stats, pattern ratios, and engineered interaction terms (e.g. masher pressure, peak ratios). |
features_pattern.py |
v2 — baseline-relative pattern structure (8 layers) | Note classification → trimming, 3 ms event clustering and rhythm baseline → priority-based, mutually-exclusive pattern marking → sandwich / hold-mash / irregularity detection → windowed pattern × density × entropy fusion. |
features_god.py |
v3 — compressed difficulty dimensions | Per window, collapses everything into a handful of fused scalars — speed pressure, complexity, reading load, physical demand, and a unified total intensity — then takes their percentiles. No raw density / entropy / pattern values are exposed to the model. |
Non-playable notes (visual effects, animations) are filtered out; collectibles are excluded from difficulty and only counted separately; special note types (gear, ghost, masher, hold) get dedicated handling.
| Path | Purpose |
|---|---|
odin_parser.py, mdcp_parser.py, bms_parser.py |
Decoders that translate chart container formats into a common note-event representation. |
features.py, features_pattern.py, features_god.py |
The three feature generations described above. |
build_unified_dataset.py |
Assemble decoded charts into a single labelled feature matrix. |
train.py |
Train the gradient-boosted regressor with grouped cross-validation and export the model. |
predict.py |
Load a trained model, extract features for a chart, and return a difficulty prediction with a structural breakdown. |
This repository contains the full modeling code but no chart data. The raw training material is not provided and cannot be supplied here. If you wish to reproduce the model yourself, you will need to obtain suitable training material through legitimate channels and place it where the pipeline expects it — the code shows exactly what shape that data must take.
This project is licensed under the GNU Affero General Public License v3.0.
Third-Party Notices
| Library | License |
|---|---|
| NumPy | BSD-3-Clause |
| pandas | BSD-3-Clause |
| scikit-learn | BSD-3-Clause |
| XGBoost | Apache-2.0 |