diff --git a/output/.DS_Store b/output/.DS_Store index 1500622..f7ab4a6 100644 Binary files a/output/.DS_Store and b/output/.DS_Store differ diff --git a/output/xgboost_copy/features.json b/output/xgboost_copy/features.json new file mode 100644 index 0000000..81cbf0f --- /dev/null +++ b/output/xgboost_copy/features.json @@ -0,0 +1,74 @@ +[ + { + "songno": "XODUS", + "difficulty": "oni", + "note_count": 1007, + "density_avg": 8.14959935897436, + "density_peak": 17, + "bpm_avg": 202, + "bpm_change": 0, + "scroll_change": 14, + "rhythm_complexity": 868, + "color_complexity": 0.012565274045963932 + }, + { + "songno": "Destructive Little Sister", + "difficulty": "oni", + "note_count": 1119, + "density_avg": 7.917452830188679, + "density_peak": 21, + "bpm_avg": 247.5, + "bpm_change": 0, + "scroll_change": 17, + "rhythm_complexity": 1079, + "color_complexity": 0.016291673920775917 + }, + { + "songno": "7 Wonders", + "difficulty": "oni", + "note_count": 794, + "density_avg": 5.571929824561403, + "density_peak": 12, + "bpm_avg": 168, + "bpm_change": 0, + "scroll_change": 0, + "rhythm_complexity": 726, + "color_complexity": 0.006656416024691356 + }, + { + "songno": "7 Wonders", + "difficulty": "ura", + "note_count": 1207, + "density_avg": 8.47017543859649, + "density_peak": 15, + "bpm_avg": 168, + "bpm_change": 0, + "scroll_change": 0, + "rhythm_complexity": 965, + "color_complexity": 0.01463965596985236 + }, + { + "songno": "Destruction 3 2 1", + "difficulty": "oni", + "note_count": 1160, + "density_avg": 7.46659375, + "density_peak": 15, + "bpm_avg": 321.3209999999973, + "bpm_change": 0, + "scroll_change": 5, + "rhythm_complexity": 1101, + "color_complexity": 0.012682773678672012 + }, + { + "songno": "Destruction 3 2 1", + "difficulty": "ura", + "note_count": 1491, + "density_avg": 9.59714765625, + "density_peak": 20, + "bpm_avg": 321.32099999999707, + "bpm_change": 0, + "scroll_change": 8, + "rhythm_complexity": 1417, + "color_complexity": 0.028926671611070473 + } +] \ No newline at end of file diff --git a/predict/predict_xgboost.py b/predict/predict_xgboost.py index 3617794..0fd882e 100644 --- a/predict/predict_xgboost.py +++ b/predict/predict_xgboost.py @@ -159,5 +159,6 @@ if __name__ == "__main__": predict( args.workingDir, - args.songno + args.songno, + args.feature ) \ No newline at end of file diff --git a/preprocess/parse.ts b/preprocess/parse.ts index 0ecb696..e2bd900 100644 --- a/preprocess/parse.ts +++ b/preprocess/parse.ts @@ -1,51 +1,45 @@ import tjaParser, { Bar, Branch, Course } from 'tja-parser' -export function parseTja(tja: string): Partial> | null { - try { - const song = tjaParser.Song.parse(tja); +export function parseTja(tja: string): Partial> { + const song = tjaParser.Song.parse(tja); - let oni: Course | undefined = undefined; - let edit: Course | undefined = undefined; + let oni: Course | undefined = undefined; + let edit: Course | undefined = undefined; - if (song.course?.oni) { - const noteGroups = song.course.oni.noteGroups; - oni = song.course.oni; - oni.noteGroups = [] + if (song.course?.oni) { + const noteGroups = song.course.oni.noteGroups; + oni = song.course.oni; + oni.noteGroups = [] - for (const noteGroup of noteGroups) { - if (noteGroup instanceof Bar) { - oni.pushNoteGroups(noteGroup) - } - else if (noteGroup instanceof Branch) { - const bar = noteGroup.master || noteGroup.advanced || noteGroup.normal; - if (bar) { - oni.pushNoteGroups(...bar) - } + for (const noteGroup of noteGroups) { + if (noteGroup instanceof Bar) { + oni.pushNoteGroups(noteGroup) + } + else if (noteGroup instanceof Branch) { + const bar = noteGroup.master || noteGroup.advanced || noteGroup.normal; + if (bar) { + oni.pushNoteGroups(...bar) } } } - if (song.course?.edit) { - const noteGroups = song.course.edit.noteGroups; - edit = song.course.edit; - edit.noteGroups = [] + } + if (song.course?.edit) { + const noteGroups = song.course.edit.noteGroups; + edit = song.course.edit; + edit.noteGroups = [] - for (const noteGroup of noteGroups) { - if (noteGroup instanceof Bar) { - edit.pushNoteGroups(noteGroup) - } - else if (noteGroup instanceof Branch) { - const bar = noteGroup.master || noteGroup.advanced || noteGroup.normal; - if (bar) { - edit.pushNoteGroups(...bar) - } + for (const noteGroup of noteGroups) { + if (noteGroup instanceof Bar) { + edit.pushNoteGroups(noteGroup) + } + else if (noteGroup instanceof Branch) { + const bar = noteGroup.master || noteGroup.advanced || noteGroup.normal; + if (bar) { + edit.pushNoteGroups(...bar) } } } + } - return { oni, edit } - } - catch (err){ - console.error(err) - return null; - } + return { oni, edit } } \ No newline at end of file diff --git a/script/predict_tja.ts b/script/predict_tja.ts new file mode 100644 index 0000000..310f35f --- /dev/null +++ b/script/predict_tja.ts @@ -0,0 +1,94 @@ +import Bun from 'bun'; +import path from 'node:path'; +import { parseArgs } from 'node:util'; +import fs from 'node:fs'; +import { featurize } from '../preprocess/featurize'; +import { parseTja } from '../preprocess/parse'; + +const { values } = parseArgs({ + args: Bun.argv, + options: { + tja: { + type: "string" + }, + workingDir: { + type: "string" + }, + script: { + type: "string" + } + }, + allowPositionals: true, +}); + +if (!values.tja || !values.workingDir || !values.script) { + console.error("Usage: bun script/predict_tja.ts --tja [--workingDir ] [--script ]"); + process.exit(1); +} + +const tjaPath = values.tja; +if (!fs.existsSync(tjaPath)) { + console.error(`File not found: ${tjaPath}`); + process.exit(1); +} + +const workingDir = values.workingDir!; +const pythonScript = values.script!; + +if (!fs.existsSync(pythonScript)) { + console.error(`Python script not found: ${pythonScript}`); + process.exit(1); +} + +// 1. TJA 파싱 및 피처 추출 +const tjaContent = fs.readFileSync(tjaPath, 'utf-8'); +const songno = path.basename(tjaPath, '.tja'); +const parsed = parseTja(tjaContent); + +const features: any[] = []; +if (parsed.oni) { + features.push({ + songno, + difficulty: 'oni', + ...featurize(parsed.oni) + }); +} +if (parsed.edit) { + features.push({ + songno, + difficulty: 'ura', + ...featurize(parsed.edit) + }); +} + +if (features.length === 0) { + console.error("No Oni or Ura difficulty found in the TJA file."); + process.exit(1); +} + +// 2. 임시 피처 파일 저장 +const tempFeaturePath = path.join(workingDir, `temp_predict_${Date.now()}.json`); +fs.writeFileSync(tempFeaturePath, JSON.stringify(features, null, 2), 'utf-8'); + +try { + // 3. Python 예측 스크립트 실행 + const proc = Bun.spawnSync([ + "python3", + pythonScript, + "--workingDir", workingDir, + "--feature", tempFeaturePath, + "--songno", songno + ]); + + if (proc.success) { + console.log(proc.stdout.toString()); + } else { + console.error("Prediction failed:"); + console.error(proc.stderr.toString()); + } +} finally { + // 4. 임시 파일 삭제 + if (fs.existsSync(tempFeaturePath)) { + fs.unlinkSync(tempFeaturePath); + } +}