機能 #313
完了ノーツレーダー6軸の細分化データモデル設計(弱点分析 #293 の前提整備)
説明
目的¶
#293(弱点分析・地力推定パネル)の前提として、既存のノーツレーダー6軸(NOTES / PEAK / SCRATCH / SOFLAN / CHARGE / CHORD)を、より細かい音ゲー特性(単皿・連皿、LN拘束、最大1秒密度…等)へ細分化した内部データモデルの設計を先行検討する。本チケットの成果物は設計(採用方針の決定)であり、実装・パネル本体は別チケット(#293 ほか)で行う。
#293 ユーザーメモの「6軸を拡張したもっと細かい音ゲーデータを保持したほうがいいかもしれない。先にこちらを検討」を受けたもの。
スコープ¶
- 対象: 細分化サブ軸の定義、データ取得方式、保持(スキーマ)方式、集計・正規化方式、既存6軸との関係、段階導入計画の決定。
- 対象外: #293 の弱点分析パネルそのものの実装(未着手のまま保留)。弱点判定で比較する「全体平均」の定義は本設計確定後に #293 側で別途決定する(ユーザー指示)。
現状のデータパイプライン(調査結果)¶
6軸レーダー値は次の経路で算出・保持されている。
-
beatoraja 本体が算出:
bms.player.beatoraja.song.NotesRadarCalculator#calculate(BMSModel)がNotesRadarFeature(約30フィールドの record)を生成。譜面ロード時SongDataModelMapper#apply()で呼ばれる。 -
SongDataには最終6値のみ保持:featureNotesDensity/featurePeakDensity/featureScratchDensity/featureSoflan/featureCharge/featureChord(各 0〜200 にスケール済み)。NotesRadarFeatureが持つ生メトリクス約24個は SongData に載らず破棄される。 -
plugin が伝送:
bms.player.beatoraja.ir.IzuIRConnection(IRChartData)が上記6値をfeature_*キーでChartDataDto.values(Map<String,String>)に詰めて送信。 -
server が保持:
ScoreService#upsertSong()がchart.values()からfeature_*を読み、songsテーブルのradar_notes/radar_peak/radar_scratch/radar_soflan/radar_charge/radar_chord(DOUBLE PRECISION, マイグレーションV9__add_song_radar.sql)へ格納。エンティティはSongEntity。 -
集計:
PlayerNotesRadarAxis(enum, 軸→列マッピング)とPlayerNotesRadarQueryRepository(aggregate/topEntries)が、mode='7' に限定し、各譜面の達成率min(exscore/(notes*2), 1.0)で重み付けし、軸ごと上位10譜面を平均して 0〜200 値を返す。PlayerNotesRadarService#radar()/detail()。
重要な含意: 細分化に必要な生データの多くは beatoraja 内部で既に計算されているが伝送・保持されていない。よって細分化は本質的に「解析の露出 → 伝送 → スキーマ → 集計」の拡張であり、新規の重い譜面解析はサブ軸の一部に限られる。
細分化サブ軸の提案とデータ源マッピング¶
ユーザー例の各サブ軸を、取得可能性で A: 既存計算の露出のみ / B: 新規解析が必要 に分類する。B は izu-ir 側で BMSModel(TimeLine / Note / NormalNote / LongNote / Mode#isScratchKey)から追加算出する想定。
SCRATCH(皿)¶
| サブ軸 | 定義(案) | 区分 | データ源 / 備考 |
|---|---|---|---|
| 単皿 | 孤立した皿の量 | B | 皿総数 scratchTotal から連皿分を除く。新規判定要 |
| 連皿 | 短間隔で連続する皿の密度 | A寄り |
scratchPeakWindowNotes(3秒窓内皿数)を流用可。窓定義の精緻化は B |
| 皿複合 | 皿と鍵盤が同時/近接する密度 | B | 皿タイムライン近傍の鍵盤数。新規解析 |
| 終盤皿 | 譜面終盤(末尾x%)の皿密度 | B | 「終盤」の定義(末尾何%か)要決定 |
| 皿直後鍵盤 | 皿の直後一定時間内の鍵盤量 | B | 皿後ms窓の鍵盤カウント。新規解析 |
CHARGE(LN/CN)¶
| サブ軸 | 定義(案) | 区分 | データ源 / 備考 |
|---|---|---|---|
| LN数 | LN/CN 総本数 | A | longNoteTotal |
| LN拘束 | LN の累積拘束時間 | A | longNoteSecondsTotal |
| LN中鍵盤 | LN保持中に押す通常鍵盤量 | A/B |
chargeNotesDuringLongNote(鍵盤/皿合算)。鍵盤のみへ分離は B |
| LN中皿 | LN保持中の皿量 | B | 上記から皿のみ分離。新規解析 |
| LN終端密度 | LN終端付近のノーツ密度 | B | LN終端ms窓の密度。新規解析 |
PEAK(密度ピーク)¶
| サブ軸 | 定義(案) | 区分 | データ源 / 備考 |
|---|---|---|---|
| 最大1秒密度 | 1秒窓の最大ノーツ数 | B | 既存は3秒窓 peakWindowNotes。1秒窓は新規 |
| 最大5秒密度 | 5秒窓の最大ノーツ数 | B | 新規窓 |
| 最大10秒密度 | 10秒窓の最大ノーツ数 | B | 新規窓 |
| 終盤ピーク | 譜面終盤のピーク密度 | B | 「終盤」定義に依存 |
| 局所難率 | 局所的な難所の集中度 | B | 指標定義要決定(密度分散/上位窓比など) |
SOFLAN / CHORD も同様に細分化余地あり(
soflan*系メトリクス、chordTimelineCount/maxChordSize/chordPeakWindowNotes等)。本チケットで全軸の最終サブ軸セットを確定する。
データ取得の実現方式(要決定)¶
-
案A: beatoraja の生メトリクスを
SongData経由で露出-
NotesRadarFeatureの生フィールドをSongDataに載せ、plugin でfeature_*に追加する。 - 長所: 既存計算の再利用。短所: beatoraja 本体の改変に依存し、izu-ir が特定 beatoraja ビルドに強く結合する(脆い)。サブ軸の一部(B分類)はそもそも未計算。
-
-
案B: izu-ir plugin 側に独自アナライザを実装(推奨)
- plugin 内に
NotesAnalyzerを新設し、beatoraja のBMSModel系モデルクラスから全サブ軸を izu-ir 独自に算出。 - 長所: 軸定義を izu-ir が完全制御でき、beatoraja 内部実装に非依存。A/B 双方のサブ軸を一元的に扱える。短所: 解析ロジックの新規実装コスト。
-
要確認(重要): スコア送信時に plugin が
BMSModel(または BMS ファイル)へアクセスできるか。アクセス不可なら譜面ロード時にキャッシュして送信時に参照する設計が必要。
- plugin 内に
保持(スキーマ)方式(要決定)¶
-
案1:
songsにradar_*列を追加し続ける — 既存踏襲だが、〜15+列で列爆発・マイグレーション肥大。 -
案2: 正規化テーブル
song_chart_feature(song_sha256, axis_key, value, updated_at)(EAV) — 軸追加がデータ行追加で済み、集計クエリもaxis_keyで汎用化。推奨。 -
案3:
songs.chart_features JSONB— 柔軟だが集計クエリ・型保証が弱い。
伝送層は ChartDataDto.values(任意キーの Map)が既に汎用なので、いずれの案でも plugin→server 互換性は高い。
集計・正規化方針¶
- 既存同様、各サブ軸を 0〜200 アンカーテーブル方式でスケールし、達成率
min(exscore/(notes*2), 1.0)重み付け+軸ごと上位N平均を踏襲。 - 既存6軸は親カテゴリとして残し、サブ軸はそのドリルダウンとして表現(後方互換・UI 段階導入)。
#293 への接続¶
- サブ軸の average を「親軸グループ平均」または「本人全サブ軸平均」と比較して弱点サブ軸を抽出する余地が生まれる。
- 「全体平均」の具体定義(本人内平均 / 全プレイヤー横断平均)は、本設計でサブ軸セットが確定した後に #293 側で決定する。
段階導入計画(案)¶
- Phase 1: A分類(既に算出済み or 軽微な算出)サブ軸の露出・伝送・保持・集計。
- Phase 2: B分類(皿直後鍵盤・終盤系・1/5/10秒窓・局所難率・LN終端密度・LN中皿)の新規解析実装。
- 各 Phase は別 機能 チケットとして起票予定。
未解決の検討事項(Open Questions)¶
- plugin がスコア送信時に
BMSModel/ BMS ファイルへアクセスできるか(案B の前提)。 - 「終盤」の定義(末尾何 % / 何秒か)。
- 「局所難率」の具体指標定義。
- スキーマ案(1/2/3)の最終選択。
- 既存
songs行のバックフィル方針(新サブ軸は再プレイ/再ロードまで NULL になる扱い)。 - 全軸の最終サブ軸セット(SOFLAN/CHORD 含む)の確定。
テスト方針(将来実装時)¶
- plugin:
NotesAnalyzer単体テスト(既知譜面で各サブ軸値の境界・期待値)。 - server: 集計クエリ/正規化の境界テスト、コントローラ 404。
- web: パネル描画・i18n(#293 側)。