From 53e99081677c382aa2662313eed28f0d15c52842 Mon Sep 17 00:00:00 2001 From: hotsixman Date: Mon, 20 Apr 2026 00:21:53 +0900 Subject: [PATCH] phase 1 --- .gemini/difficulty-factors.md | 36 + .gemini/dnn-plan.md | 16 + .gemini/factorization-method.md | 32 + .gemini/notes.md | 32 +- .gemini/pipeline-design.md | 13 + .gemini/tja-spec.md | 68 ++ .gitignore | 3 + README.md | 15 - bun.lock | 245 +++++ docs/difficulty-analysis.md | 33 + docs/factorization-guide.md | 43 + docs/model-architecture.md | 12 + file_list.txt | 1236 ++++++++++++++++++++++++++ index.ts | 72 -- measure.csv | 1472 +++++++++++++++++++++++++++++++ model/constant_predictor.py | 21 + model/dataset.json | 1 + model/train.py | 19 + package.json | 1 + pipeline/orchestrator.sh | 36 + scripts/clean_csv.ts | 29 + scripts/compare.ts | 19 + scripts/compare_results.ts | 20 + scripts/factorize.ts | 57 ++ scripts/predict.ts | 11 + scripts/predict_batch.ts | 21 + scripts/prepare_dataset.ts | 26 + scripts/run_predict.sh | 9 + scripts/run_train.sh | 11 + scripts/train.ts | 35 + tsconfig.json | 2 +- 31 files changed, 3535 insertions(+), 111 deletions(-) create mode 100644 .gemini/difficulty-factors.md create mode 100644 .gemini/dnn-plan.md create mode 100644 .gemini/factorization-method.md create mode 100644 .gemini/pipeline-design.md create mode 100644 .gemini/tja-spec.md delete mode 100644 README.md create mode 100644 docs/difficulty-analysis.md create mode 100644 docs/factorization-guide.md create mode 100644 docs/model-architecture.md create mode 100644 file_list.txt delete mode 100644 index.ts create mode 100644 measure.csv create mode 100644 model/constant_predictor.py create mode 100644 model/dataset.json create mode 100644 model/train.py create mode 100755 pipeline/orchestrator.sh create mode 100644 scripts/clean_csv.ts create mode 100644 scripts/compare.ts create mode 100644 scripts/compare_results.ts create mode 100644 scripts/factorize.ts create mode 100644 scripts/predict.ts create mode 100644 scripts/predict_batch.ts create mode 100644 scripts/prepare_dataset.ts create mode 100755 scripts/run_predict.sh create mode 100755 scripts/run_train.sh create mode 100644 scripts/train.ts diff --git a/.gemini/difficulty-factors.md b/.gemini/difficulty-factors.md new file mode 100644 index 0000000..3af0dd7 --- /dev/null +++ b/.gemini/difficulty-factors.md @@ -0,0 +1,36 @@ +# TJA 난이도 산정 핵심 요소 (Technical Factors) + +'상수' 산출을 위해 채보에서 추출해야 하는 5개 핵심 차원과 세부 지표입니다. + +## 1. 물리적 밀도 (Physical Density) +물리적인 타격 속도와 관련된 지표입니다. +- **Global NPS**: `총 노트 수 / 총 연주 시간`. 곡의 전반적인 속도 체급. +- **Peak NPS (Sliding Window)**: 1초~2초 단위 윈도우에서 추출한 최대 NPS. 순간 폭타의 한계치. +- **Density Variance**: NPS의 표준 편차. 곡이 얼마나 균일한지 또는 급격한지 측정. + +## 2. 지구력 요구량 (Stamina Requirement) +지치지 않고 집중력을 유지해야 하는 정도입니다. +- **Longest Stream Count**: 쉼표(예: 8분음표 이상의 간격) 없이 이어지는 최대 노트 개수. +- **Stream Density Ratio**: 전체 곡 시간 대비 스트림(연타) 구간이 차지하는 비중. +- **Rest Interval Analysis**: 회복 가능한 구간의 배치와 빈도. + +## 3. 배치 복잡도 (Pattern Complexity) +인지적 부하와 손 배치(Hand-switching)의 어려움입니다. +- **Color Transition Ratio**: `색상 전환(d↔k) 횟수 / 총 노트 수`. 전환이 많을수록 인지 부하 증가. +- **Hand-Switching Index**: 홀수(3, 5, 7) 및 짝수(2, 4) 연타의 혼합도. 기준 손이 강제로 바뀌는 빈도. +- **Complex Pattern Detection**: 비정형 패턴(예: ddkdk, kkkdk 등)의 출현 빈도. + +## 4. 리듬 복잡도 (Rhythmic Complexity) +정확도(98%) 달성을 방해하는 타이밍 요소입니다. +- **Quantization Diversity**: 사용된 음표 단위(1/16, 1/12, 1/24, 1/48 등)의 종류와 혼합 빈도. +- **Off-beat Ratio**: 정박(1/4, 1/8)을 벗어난 엇박 노지의 비율. +- **Rhythmic Entropy**: 노트 간 시간 간격의 불규칙성 정도. + +## 5. 가독성 및 기믹 (Reading & Gimmicks) +시각적인 반응 속도와 암기 요소를 측정합니다. +- **Scroll Velocity (SV) Variance**: `#SCROLL` 변화의 진폭과 빈도. +- **BPM Fluctuation**: `#BPMCHANGE`를 통한 급격한 속도 변화 및 정지(#DELAY). +- **Visual Overlap**: 저속 구간에서의 노트 겹침이나 고속 구간의 반응 한계. + +## 상수 추정 가중치 (Proposed) +`Constant ∝ (Peak_NPS * 0.4) + (Complexity * 0.3) + (Stamina * 0.2) + (Reading * 0.1)` diff --git a/.gemini/dnn-plan.md b/.gemini/dnn-plan.md new file mode 100644 index 0000000..8370763 --- /dev/null +++ b/.gemini/dnn-plan.md @@ -0,0 +1,16 @@ +# DNN 학습 로드맵 (.gemini/dnn-plan.md) + +### Phase 1: 데이터 정제 +- `measure.csv`를 기준으로 분석된 Factor 데이터셋 확보. +- Null 값 제거 및 이상치(Outlier) 필터링. + +### Phase 2: 환경 구축 +- Python 기반 (TensorFlow/PyTorch) 또는 Node.js 기반 (TensorFlow.js) 선택. +- 데이터 학습용 훈련 세트(Train)와 검증 세트(Validation) 분리. + +### Phase 3: 학습 및 검증 +- 모델 훈련 및 오차(MSE) 점검. +- 실제 상수와의 상관계수 분석. + +### Phase 4: 통합 +- 학습된 가중치(Weight)를 `factorize.ts`에 로드하여 실제 상수 예측 수행. diff --git a/.gemini/factorization-method.md b/.gemini/factorization-method.md new file mode 100644 index 0000000..01a6892 --- /dev/null +++ b/.gemini/factorization-method.md @@ -0,0 +1,32 @@ +# TJA Factorization 방법론 + +이 문서는 Raw TJA 데이터를 5대 난이도 요소(Factor)로 변환하는 표준 공정을 정의합니다. + +## 1. 전처리: 절대 타임라인 생성 (Timeline Mapping) +TJA의 상대적 마디/박자 구조를 초 단위의 절대 시간축으로 선형화합니다. +- **상태 추적**: `#BPMCHANGE`, `#MEASURE`, `#DELAY`를 실시간 반영하여 각 노트의 발생 시간($T_n$)을 계산합니다. +- **이벤트 객체화**: 각 노트를 위치($T_n$), 타입(Don/Ka), 현재 속도(BPM/SV)를 포함한 객체로 변환합니다. + +## 2. 요소별 특징 추출 (Feature Extraction) + +### A. 물리적 밀도 (Physical) +- **Peak NPS**: 2초 단위의 슬라이딩 윈도우를 사용하여 가장 밀도가 높은 구간의 초당 노트 수를 측정합니다. +- **지수화**: `Window_Note_Count / Window_Size`. + +### B. 지구력 (Stamina) +- **Stream 정의**: 노트 간 간격이 1/16박자(약 150-200ms) 이하로 지속되는 구간을 하나의 스트림으로 봅니다. +- **지표**: 가장 긴 스트림의 노트 개수($S_{max}$)와 곡 전체 대비 스트림 비중($R_{stream}$)을 결합합니다. + +### C. 배치 복잡도 (Technical) +- **Color Transition**: 인접한 두 노트의 색상이 다를 때(d↔k)를 전환으로 카운트합니다. +- **지표**: `Total_Transitions / Total_Notes`. 0.5에 가까울수록 복잡도가 높습니다. + +### D. 리듬 복잡도 (Accuracy) +- **Subdivision Analysis**: 각 마디가 몇 등분 되었는지 분석하여 12, 24, 48분 음표 등 비정형 박자의 사용 빈도를 측정합니다. +- **지표**: `Non_Standard_Notes / Total_Notes`. + +### E. 가독성 및 기믹 (Reading) +- **Velocity Flux**: `#BPMCHANGE` 횟수와 `#SCROLL` 변화량의 절대값 합산으로 시각적 혼란도를 측정합니다. + +## 3. 정규화 및 통합 (Normalization) +각 지표의 원시 수치를 0.0 ~ 1.0 범위로 정규화한 뒤, '상수' 가중치를 적용하여 최종 난이도를 도출합니다. diff --git a/.gemini/notes.md b/.gemini/notes.md index e56809c..9283ebb 100644 --- a/.gemini/notes.md +++ b/.gemini/notes.md @@ -5,27 +5,13 @@ - **Language**: TypeScript - **Library**: [tja](https://www.npmjs.com/package/tja) (TJA parser) +## Key Documents +- `.gemini/tja-spec.md`: Rigorous TJA format specification. +- `tja-format.mediawiki`: Original source document. +- `measure.csv`: Dataset with columns `상수`, `songno`, `diff`. + +## Models (model/) +- `constant_predictor.py`: DNN 기반 상수 예측 모델. + ## Library Usage (tja) -- **Import**: `import { TJAParser } from "tja";` -- **Parsing**: `const parsed = TJAParser.parse(content);` -- **Structure**: - - `parsed.title`, `parsed.bpm`, `parsed.offset` - - `parsed.courses` (Array of `Course` objects) - - `course.difficulty` (e.g., "Oni") - - `course.stars` (Level/Stars) - - `course.activeCourse.getCommands()` returns an array of commands and note sequences. - -## Purpose -Analyzing or processing TJA (Taiko Jiro) file formats. - -## Key References -- `tja-format.mediawiki`: Detailed specification of the TJA format. - -## TJA Format Overview -- **Encoding**: UTF-8 with BOM or Shift-JIS. -- **Extension**: `.tja`. -- **Comments**: Start with `//`. -- **Metadata**: Key-value pairs (e.g., `TITLE:`, `BPM:`, `OFFSET:`). -- **Course Metadata**: Specific to difficulties (e.g., `COURSE:`, `LEVEL:`, `BALLOON:`). -- **Notation**: Commands prefixed with `#` (e.g., `#START`, `#END`, `#MEASURE`, `#BPMCHANGE`). -- **Notes**: `0-9`, `A`, `B`, `F`. +... diff --git a/.gemini/pipeline-design.md b/.gemini/pipeline-design.md new file mode 100644 index 0000000..a93285a --- /dev/null +++ b/.gemini/pipeline-design.md @@ -0,0 +1,13 @@ +# 파이프라인 실행 규칙 + +모든 경로는 실행 시점에 인자로 지정하여 관리합니다. + +## 1. 학습 파이프라인 (`run_train.sh`) +- **인자 1 (TJA_DIR)**: 학습용 TJA 채보가 저장된 폴더 (예: `sample/training`) +- **인자 2 (MODEL_PATH)**: 모델이 저장될 경로 (예: `output/model/v2_constant`) +- **인자 3 (DATASET_DIR)**: 데이터셋이 저장될 폴더 (예: `output/dataset`) + +## 2. 예측 파이프라인 (`run_predict.sh`) +- **인자 1 (MODEL_PATH)**: 추론에 사용할 모델 경로 +- **인자 2 (TJA_DIR)**: 예측할 TJA 채보가 모여있는 폴더 +- **인자 3 (OUTPUT_DIR)**: 결과가 저장될 폴더 diff --git a/.gemini/tja-spec.md b/.gemini/tja-spec.md new file mode 100644 index 0000000..daf34fe --- /dev/null +++ b/.gemini/tja-spec.md @@ -0,0 +1,68 @@ +# TJA Format Specification (Rigorous) + +## 1. File Structure +- **Extension**: `.tja` +- **Encoding**: UTF-8 (with BOM) or Shift-JIS. +- **Comments**: `//` (inline, until end of line). +- **Sections**: + 1. **Global Metadata**: Before any `#START`. + 2. **Course Data**: Metadata specific to a difficulty (can be mixed with global). + 3. **Song Notation**: Between `#START` and `#END`. + +## 2. Metadata (Key: Value) +| Key | Type | Description | +| :--- | :--- | :--- | +| `TITLE` | string | Song title. | +| `BPM` | float | Beats Per Minute. Default: 120. | +| `WAVE` | string | Path to audio file. | +| `OFFSET` | float | Seconds. Negative delays notes, positive advances them. | +| `DEMOSTART`| float | Preview start time in seconds. | +| `GENRE` | string | Category (e.g., アニメ, ゲームミュージック). | +| `SCOREMODE`| 0, 1, 2| Scoring method. Default: 1. | +| `COURSE` | enum | 0:Easy, 1:Normal, 2:Hard, 3:Oni, 4:Edit/Ura, 5:Tower, 6:Dan. | +| `LEVEL` | int | 1-10 (Stars). | +| `BALLOON` | int[] | Comma-separated hit counts for balloon/kusudama notes. | +| `SCOREINIT`| int | Initial score per note. | +| `SCOREDIFF`| int | Added score for combo milestones. | + +## 3. Song Notation: Notes +| Code | Name | Description | +| :--- | :--- | :--- | +| `0` | Blank | No note. | +| `1` | Don | Small Red. | +| `2` | Ka | Small Blue. | +| `3` | DON | Large Red. | +| `4` | KA | Large Blue. | +| `5` | Drumroll| Start of small drumroll. Ends with `8`. | +| `6` | DRUMROLL| Start of large drumroll. Ends with `8`. | +| `7` | Balloon | Start of balloon. Ends with `8`. | +| `8` | End | Ends 5, 6, 7, or 9. | +| `9` | Kusudama| Large balloon/Kusudama. Ends with `8` or `9`. | +| `A` | DON(H) | Large Red (Hands/Multiplayer). | +| `B` | KA(H) | Large Blue (Hands/Multiplayer). | + +## 4. Commands (#COMMAND [value]) +| Command | Value | Description | +| :--- | :--- | :--- | +| `#START` | (P1/P2) | Begin notation. | +| `#END` | - | End notation. | +| `#MEASURE` | n/d | Set time signature (e.g., 4/4). | +| `#BPMCHANGE`| float | Change BPM. | +| `#DELAY` | float | Delay in seconds (can be negative). | +| `#SCROLL` | float | Multiplier for note scroll speed. | +| `#GOGOSTART`| - | Start Go-Go Time (1.2x score). | +| `#GOGOEND` | - | End Go-Go Time. | +| `#SECTION` | - | Reset branch accuracy counters. | +| `#BRANCHSTART`| type,v1,v2| Branching: `p` (accuracy) or `r` (drumrolls). | +| `#N`, `#E`, `#M`| - | Branch paths: Normal, Advanced/Expert, Master. | +| `#BRANCHEND`| - | End branching section. | +| `#BARLINEOFF`| - | Hide measure lines. | +| `#BARLINEON` | - | Show measure lines. | +| `#LYRIC` | string | Display lyrics (`\n` for line break). | + +## 5. Logic & Formulas +- **Measure Duration (ms)**: `60000 * (numerator / denominator) * 4 / BPM` +- **Note Timing**: Equally spaced within a measure. +- **Branching**: + - `p` (Percent): `(GOOD + OK*0.5) / TotalNotes * 100`. + - Calculated one measure before `#BRANCHSTART`. diff --git a/.gitignore b/.gitignore index a14702c..983b685 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json # Finder (MacOS) folder config .DS_Store + +tja +sample \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 3551843..0000000 --- a/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# fumen-analyze - -To install dependencies: - -```bash -bun install -``` - -To run: - -```bash -bun run index.ts -``` - -This project was created using `bun init` in bun v1.3.1. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime. diff --git a/bun.lock b/bun.lock index e01cc76..9441526 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,7 @@ "": { "name": "fumen-analyze", "dependencies": { + "@tensorflow/tfjs-node": "^4.22.0", "tja": "^0.1.3", }, "devDependencies": { @@ -15,16 +16,260 @@ }, }, "packages": { + "@mapbox/node-pre-gyp": ["@mapbox/node-pre-gyp@1.0.9", "", { "dependencies": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", "make-dir": "^3.1.0", "node-fetch": "^2.6.7", "nopt": "^5.0.0", "npmlog": "^5.0.1", "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.11" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw=="], + + "@tensorflow/tfjs": ["@tensorflow/tfjs@4.22.0", "", { "dependencies": { "@tensorflow/tfjs-backend-cpu": "4.22.0", "@tensorflow/tfjs-backend-webgl": "4.22.0", "@tensorflow/tfjs-converter": "4.22.0", "@tensorflow/tfjs-core": "4.22.0", "@tensorflow/tfjs-data": "4.22.0", "@tensorflow/tfjs-layers": "4.22.0", "argparse": "^1.0.10", "chalk": "^4.1.0", "core-js": "3.29.1", "regenerator-runtime": "^0.13.5", "yargs": "^16.0.3" }, "bin": { "tfjs-custom-module": "dist/tools/custom_module/cli.js" } }, "sha512-0TrIrXs6/b7FLhLVNmfh8Sah6JgjBPH4mZ8JGb7NU6WW+cx00qK5BcAZxw7NCzxj6N8MRAIfHq+oNbPUNG5VAg=="], + + "@tensorflow/tfjs-backend-cpu": ["@tensorflow/tfjs-backend-cpu@4.22.0", "", { "dependencies": { "@types/seedrandom": "^2.4.28", "seedrandom": "^3.0.5" }, "peerDependencies": { "@tensorflow/tfjs-core": "4.22.0" } }, "sha512-1u0FmuLGuRAi8D2c3cocHTASGXOmHc/4OvoVDENJayjYkS119fcTcQf4iHrtLthWyDIPy3JiPhRrZQC9EwnhLw=="], + + "@tensorflow/tfjs-backend-webgl": ["@tensorflow/tfjs-backend-webgl@4.22.0", "", { "dependencies": { "@tensorflow/tfjs-backend-cpu": "4.22.0", "@types/offscreencanvas": "~2019.3.0", "@types/seedrandom": "^2.4.28", "seedrandom": "^3.0.5" }, "peerDependencies": { "@tensorflow/tfjs-core": "4.22.0" } }, "sha512-H535XtZWnWgNwSzv538czjVlbJebDl5QTMOth4RXr2p/kJ1qSIXE0vZvEtO+5EC9b00SvhplECny2yDewQb/Yg=="], + + "@tensorflow/tfjs-converter": ["@tensorflow/tfjs-converter@4.22.0", "", { "peerDependencies": { "@tensorflow/tfjs-core": "4.22.0" } }, "sha512-PT43MGlnzIo+YfbsjM79Lxk9lOq6uUwZuCc8rrp0hfpLjF6Jv8jS84u2jFb+WpUeuF4K33ZDNx8CjiYrGQ2trQ=="], + + "@tensorflow/tfjs-core": ["@tensorflow/tfjs-core@4.22.0", "", { "dependencies": { "@types/long": "^4.0.1", "@types/offscreencanvas": "~2019.7.0", "@types/seedrandom": "^2.4.28", "@webgpu/types": "0.1.38", "long": "4.0.0", "node-fetch": "~2.6.1", "seedrandom": "^3.0.5" } }, "sha512-LEkOyzbknKFoWUwfkr59vSB68DMJ4cjwwHgicXN0DUi3a0Vh1Er3JQqCI1Hl86GGZQvY8ezVrtDIvqR1ZFW55A=="], + + "@tensorflow/tfjs-data": ["@tensorflow/tfjs-data@4.22.0", "", { "dependencies": { "@types/node-fetch": "^2.1.2", "node-fetch": "~2.6.1", "string_decoder": "^1.3.0" }, "peerDependencies": { "@tensorflow/tfjs-core": "4.22.0", "seedrandom": "^3.0.5" } }, "sha512-dYmF3LihQIGvtgJrt382hSRH4S0QuAp2w1hXJI2+kOaEqo5HnUPG0k5KA6va+S1yUhx7UBToUKCBHeLHFQRV4w=="], + + "@tensorflow/tfjs-layers": ["@tensorflow/tfjs-layers@4.22.0", "", { "peerDependencies": { "@tensorflow/tfjs-core": "4.22.0" } }, "sha512-lybPj4ZNj9iIAPUj7a8ZW1hg8KQGfqWLlCZDi9eM/oNKCCAgchiyzx8OrYoWmRrB+AM6VNEeIT+2gZKg5ReihA=="], + + "@tensorflow/tfjs-node": ["@tensorflow/tfjs-node@4.22.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "1.0.9", "@tensorflow/tfjs": "4.22.0", "adm-zip": "^0.5.2", "google-protobuf": "^3.9.2", "https-proxy-agent": "^2.2.1", "progress": "^2.0.0", "rimraf": "^2.6.2", "tar": "^6.2.1" } }, "sha512-uHrXeUlfgkMxTZqHkESSV7zSdKdV0LlsBeblqkuKU9nnfxB1pC6DtoyYVaLxznzZy7WQSegjcohxxCjAf6Dc7w=="], + "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + "@types/long": ["@types/long@4.0.2", "", {}, "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="], + "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], + + "@types/offscreencanvas": ["@types/offscreencanvas@2019.3.0", "", {}, "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q=="], + + "@types/seedrandom": ["@types/seedrandom@2.4.34", "", {}, "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A=="], + + "@webgpu/types": ["@webgpu/types@0.1.38", "", {}, "sha512-7LrhVKz2PRh+DD7+S+PVaFd5HxaWQvoMqBbsV9fNJO1pjUs1P8bM2vQVNfk+3URTqbuTI7gkXi0rfsN0IadoBA=="], + + "abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="], + + "adm-zip": ["adm-zip@0.5.17", "", {}, "sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ=="], + + "agent-base": ["agent-base@4.3.0", "", { "dependencies": { "es6-promisify": "^5.0.0" } }, "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "aproba": ["aproba@2.1.0", "", {}, "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew=="], + + "are-we-there-yet": ["are-we-there-yet@2.0.0", "", { "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" } }, "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw=="], + + "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], + "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], + + "cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "console-control-strings": ["console-control-strings@1.1.0", "", {}, "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="], + + "core-js": ["core-js@3.29.1", "", {}, "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw=="], + + "debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "delegates": ["delegates@1.0.0", "", {}, "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es6-promise": ["es6-promise@4.2.8", "", {}, "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="], + + "es6-promisify": ["es6-promisify@5.0.0", "", { "dependencies": { "es6-promise": "^4.0.3" } }, "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + + "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="], + + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gauge": ["gauge@3.0.2", "", { "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", "console-control-strings": "^1.0.0", "has-unicode": "^2.0.1", "object-assign": "^4.1.1", "signal-exit": "^3.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.2" } }, "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "google-protobuf": ["google-protobuf@3.21.4", "", {}, "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "has-unicode": ["has-unicode@2.0.1", "", {}, "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="], + + "hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="], + + "https-proxy-agent": ["https-proxy-agent@2.2.4", "", { "dependencies": { "agent-base": "^4.3.0", "debug": "^3.1.0" } }, "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg=="], + + "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "long": ["long@4.0.0", "", {}, "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="], + + "make-dir": ["make-dir@3.1.0", "", { "dependencies": { "semver": "^6.0.0" } }, "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="], + + "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="], + + "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + + "nopt": ["nopt@5.0.0", "", { "dependencies": { "abbrev": "1" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ=="], + + "npmlog": ["npmlog@5.0.1", "", { "dependencies": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", "gauge": "^3.0.0", "set-blocking": "^2.0.0" } }, "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], + + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "regenerator-runtime": ["regenerator-runtime@0.13.11", "", {}, "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "seedrandom": ["seedrandom@3.0.5", "", {}, "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="], + + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="], + + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="], + "tja": ["tja@0.1.3", "", {}, "sha512-AyrhrrWoCymLXouYO9n0f0JGMtrKtT8S9p5ddJbVF9IVYNrdHi5+xg+lNKN8xChemIvg5tfbBt6pEl3Pj5vLNg=="], + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + + "wide-align": ["wide-align@1.1.5", "", { "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + + "yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], + + "yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + + "@mapbox/node-pre-gyp/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + + "@mapbox/node-pre-gyp/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + + "@tensorflow/tfjs-core/@types/offscreencanvas": ["@types/offscreencanvas@2019.7.3", "", {}, "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A=="], + + "@tensorflow/tfjs-core/node-fetch": ["node-fetch@2.6.13", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA=="], + + "@tensorflow/tfjs-data/node-fetch": ["node-fetch@2.6.13", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA=="], + + "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "make-dir/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "@mapbox/node-pre-gyp/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + + "@mapbox/node-pre-gyp/https-proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], } } diff --git a/docs/difficulty-analysis.md b/docs/difficulty-analysis.md new file mode 100644 index 0000000..0db0544 --- /dev/null +++ b/docs/difficulty-analysis.md @@ -0,0 +1,33 @@ +# 태고 보면 난이도 평가 5대 요소 + +태고의 달인 보면 분석기(`fumen-analyze`)는 단순한 별 개수(Level)를 넘어, **98% 정확도와 풀콤보**를 기준으로 한 실질적 난이도인 **'상수'**를 산출합니다. 상수를 결정하는 5가지 핵심 요소는 다음과 같습니다. + +--- + +### 1. 물리적 속도 (물리) +단순히 얼마나 빨리 손을 움직여야 하는가입니다. +- **순간 폭타**: 곡 중 가장 빠른 구간의 속도입니다. +- **체급**: 곡 전체의 평균적인 빠르기입니다. + +### 2. 지구력 (체력) +쉬지 않고 얼마나 오래 몰아치는가입니다. +- **기차 길이**: 쉼표 없이 길게 이어지는 연타의 길이입니다. +- **피로도**: 곡 전체에서 연타 구간이 차지하는 비중이 높을수록 상수가 상승합니다. + +### 3. 배치 복잡도 (지능) +손 배치가 얼마나 꼬여 있는가입니다. +- **색상 변화**: 빨강(동)과 파랑(딱)이 복잡하게 섞일수록 뇌의 처리 속도가 느려집니다. +- **손 교차**: 기준 손을 강제로 바꿔야 하는 연타 패턴은 매우 높은 실력을 요구합니다. + +### 4. 리듬의 난해함 (정확도) +박자가 얼마나 까다로운가입니다. +- **박자 쪼개기**: 16분음표, 12분음표, 24분음표 등이 수시로 뒤섞이면 정확한 판정을 내기 어렵습니다. +- **엇박자**: 정박에서 벗어난 노트들은 풀콤보를 방해하는 주요 요소입니다. + +### 5. 시각적 트릭 (독해) +눈으로 노트를 읽기가 얼마나 힘든가입니다. +- **변속(소플란)**: 갑자기 빨라지거나 느려지는 노트 속도는 암기력과 반응 속도를 시험합니다. +- **가독성**: 노트가 너무 뭉쳐 있거나 기믹이 들어간 경우 실제 난이도보다 훨씬 어렵게 느껴집니다. + +--- +*Generated by fumen-analyze* diff --git a/docs/factorization-guide.md b/docs/factorization-guide.md new file mode 100644 index 0000000..b574c1b --- /dev/null +++ b/docs/factorization-guide.md @@ -0,0 +1,43 @@ +# 채보 요소 분해(Factorization) 가이드 + +이 문서는 태고(TJA) 채보의 원시 데이터를 분석하여, 난이도를 결정하는 5가지 핵심 요소로 분해하는 과정과 그 의미를 설명합니다. + +--- + +## 1. 요소 분해(Factorization)란? +태고의 채보는 텍스트 형태의 데이터로 이루어져 있습니다. 시스템은 이 데이터를 읽어 플레이어가 실제로 느끼는 **물리적 압박, 기술적 난해함, 시각적 스트레스** 등을 수치로 추출합니다. 이 과정을 '요소 분해'라고 부릅니다. + +## 2. 분석 공정 (Analysis Process) + +### 1단계: 타임라인 생성 (Timeline Mapping) +채보에 적힌 마디와 박자 정보를 바탕으로, 모든 노트가 곡이 시작된 후 **정확히 몇 초**에 연주되어야 하는지 계산하여 절대적인 시간표(Timeline)를 만듭니다. 이 과정에서 BPM 변화와 딜레이가 모두 계산에 반영됩니다. + +### 2단계: 핵심 요소 추출 (Feature Extraction) + +#### ① 물리적 속도 (Physical) - "얼마나 빠른가?" +- **분석 방법**: 전체 곡을 2초 단위로 잘라가며 가장 노트가 많이 몰린 구간을 찾습니다. +- **의미**: 플레이어의 순발력과 물리적인 손 속도의 한계를 측정합니다. + +#### ② 지구력 (Stamina) - "얼마나 오래 버티는가?" +- **분석 방법**: 노트 사이의 간격이 0.2초 이내로 유지되는 구간을 '기차(Stream)'로 정의하고, 그 최대 길이를 측정합니다. +- **의미**: 중간에 쉬지 않고 계속 쳐야 하는 구간의 길이를 통해 체력적인 소모량을 측정합니다. + +#### ③ 패턴 기술 (Technical) - "얼마나 뇌가 복잡한가?" +- **분석 방법**: 빨강(동)과 파랑(딱)이 얼마나 자주 교차되는지(색상 전환율)를 계산합니다. +- **의미**: 손 배치의 복잡도를 의미하며, 0.5에 가까울수록 머리를 많이 써야 하는 기술적인 채보임을 뜻합니다. + +#### ④ 리듬 정확도 (Accuracy) - "박자가 얼마나 까다로운가?" +- **분석 방법**: 마디 내에서 정박(1/4, 1/8)을 벗어난 변칙적인 박자(12, 24분음표 등)의 비중을 측정합니다. +- **의미**: 정확도 98%를 달성하기 위해 필요한 리듬 감각의 수준을 측정합니다. + +#### ⑤ 가독성 (Reading) - "눈이 얼마나 어지러운가?" +- **분석 방법**: 곡 중간에 속도가 변하는 명령(BPM 변화, 스크롤 속도 변화)이 얼마나 자주 나오는지 합산합니다. +- **의미**: 시각적인 혼란과 암기 요소를 수치화합니다. + +## 3. 분석 결과의 활용 +이렇게 분해된 요소들은 최종적으로 **'상수(Constant)'**를 계산하는 밑바탕이 됩니다. +- **물리/체력 수치가 높은 곡**: 주로 체력을 기르는 연습에 적합합니다. +- **기술/정확도 수치가 높은 곡**: 판정을 다듬고 손 배치를 익히는 연습에 적합합니다. + +--- +*Generated by fumen-analyze* diff --git a/docs/model-architecture.md b/docs/model-architecture.md new file mode 100644 index 0000000..35256f1 --- /dev/null +++ b/docs/model-architecture.md @@ -0,0 +1,12 @@ +# DNN 기반 채보 상수 예측 모델 설계 (docs/model-architecture.md) + +### 개요 +단순 가중치 합산(Weighted Sum) 방식의 한계를 극복하고, 복잡한 비선형 난이도 지표들을 학습하여 실제 상수와 유사한 예측값을 생성하는 심층 신경망(DNN)을 구축합니다. + +### 아키텍처 +1. **입력 계층 (Input)**: 5가지 정량화된 Factor (Physical, Stamina, Technical, Accuracy, Reading). +2. **은닉 계층 (Hidden Layers)**: + - Layer 1: 16 units, ReLU activation. + - Layer 2: 8 units, ReLU activation. +3. **출력 계층 (Output)**: 1 unit, Linear activation (상수값 예측). +4. **학습 전략**: `measure.csv`의 실제 상수를 정답 데이터로 사용. MSE Loss 및 Adam 옵티마이저 활용. diff --git a/file_list.txt b/file_list.txt new file mode 100644 index 0000000..6998e39 --- /dev/null +++ b/file_list.txt @@ -0,0 +1,1236 @@ +tja/354.tja +tja/1279.tja +tja/937.tja +tja/723.tja +tja/1452.tja +tja/1235.tja +tja/236.tja +tja/1311.tja +tja/717.tja +tja/252.tja +tja/918.tja +tja/1085.tja +tja/270.tja +tja/204.tja +tja/959.tja +tja/1411.tja +tja/1294.tja +tja/71.tja +tja/941.tja +tja/465.tja +tja/1216.tja +tja/763.tja +tja/186.tja +tja/681.tja +tja/675.tja +tja/921.tja +tja/342.tja +tja/356.tja +tja/875.tja +tja/1442.tja +tja/920.tja +tja/1359.tja +tja/239.tja +tja/511.tja +tja/389.tja +tja/1184.tja +tja/1287.tja +tja/792.tja +tja/1383.tja +tja/635.tja +tja/27.tja +tja/1107.tja +tja/412.tja +tja/697.tja +tja/1342.tja +tja/60.tja +tja/1341.tja +tja/1076.tja +tja/1273.tja +tja/618.tja +tja/761.tja +tja/510.tja +tja/392.tja +tja/399.tja +tja/544.tja +tja/67.tja +tja/25.tja +tja/1322.tja +tja/444.tja +tja/21.tja +tja/553.tja +tja/1281.tja +tja/586.tja +tja/1066.tja +tja/1145.tja +tja/1094.tja +tja/512.tja +tja/255.tja +tja/1349.tja +tja/727.tja +tja/1155.tja +tja/1405.tja +tja/167.tja +tja/1390.tja +tja/1134.tja +tja/801.tja +tja/1225.tja +tja/1308.tja +tja/892.tja +tja/1082.tja +tja/1122.tja +tja/1174.tja +tja/1282.tja +tja/993.tja +tja/303.tja +tja/1437.tja +tja/1080.tja +tja/1151.tja +tja/1092.tja +tja/15.tja +tja/407.tja +tja/1056.tja +tja/1451.tja +tja/604.tja +tja/341.tja +tja/1022.tja +tja/1162.tja +tja/1269.tja +tja/541.tja +tja/579.tja +tja/178.tja +tja/922.tja +tja/897.tja +tja/1032.tja +tja/747.tja +tja/908.tja +tja/1298.tja +tja/360.tja +tja/1037.tja +tja/858.tja +tja/859.tja +tja/546.tja +tja/760.tja +tja/313.tja +tja/343.tja +tja/451.tja +tja/615.tja +tja/954.tja +tja/861.tja +tja/1075.tja +tja/569.tja +tja/161.tja +tja/968.tja +tja/1455.tja +tja/496.tja +tja/614.tja +tja/312.tja +tja/651.tja +tja/471.tja +tja/1015.tja +tja/183.tja +tja/1088.tja +tja/1264.tja +tja/671.tja +tja/1358.tja +tja/822.tja +tja/12.tja +tja/135.tja +tja/20.tja +tja/1272.tja +tja/694.tja +tja/642.tja +tja/317.tja +tja/323.tja +tja/1062.tja +tja/1347.tja +tja/1299.tja +tja/1019.tja +tja/1327.tja +tja/1457.tja +tja/221.tja +tja/495.tja +tja/652.tja +tja/695.tja +tja/355.tja +tja/348.tja +tja/1097.tja +tja/459.tja +tja/1302.tja +tja/1179.tja +tja/1140.tja +tja/688.tja +tja/1131.tja +tja/1371.tja +tja/670.tja +tja/956.tja +tja/1170.tja +tja/842.tja +tja/223.tja +tja/663.tja +tja/1197.tja +tja/1321.tja +tja/568.tja +tja/1099.tja +tja/1157.tja +tja/159.tja +tja/1087.tja +tja/514.tja +tja/782.tja +tja/154.tja +tja/432.tja +tja/1041.tja +tja/1000.tja +tja/387.tja +tja/699.tja +tja/974.tja +tja/446.tja +tja/261.tja +tja/1214.tja +tja/478.tja +tja/1126.tja +tja/273.tja +tja/156.tja +tja/265.tja +tja/560.tja +tja/1202.tja +tja/520.tja +tja/1220.tja +tja/849.tja +tja/703.tja +tja/1195.tja +tja/955.tja +tja/910.tja +tja/1070.tja +tja/1204.tja +tja/109.tja +tja/783.tja +tja/53.tja +tja/452.tja +tja/100.tja +tja/269.tja +tja/430.tja +tja/1292.tja +tja/1368.tja +tja/800.tja +tja/1240.tja +tja/887.tja +tja/210.tja +tja/1053.tja +tja/1139.tja +tja/85.tja +tja/1268.tja +tja/946.tja +tja/970.tja +tja/1178.tja +tja/44.tja +tja/435.tja +tja/466.tja +tja/1381.tja +tja/1323.tja +tja/593.tja +tja/449.tja +tja/1028.tja +tja/1303.tja +tja/152.tja +tja/1400.tja +tja/1112.tja +tja/1241.tja +tja/1177.tja +tja/874.tja +tja/523.tja +tja/1373.tja +tja/558.tja +tja/1283.tja +tja/972.tja +tja/1176.tja +tja/287.tja +tja/1361.tja +tja/169.tja +tja/74.tja +tja/230.tja +tja/1391.tja +tja/302.tja +tja/991.tja +tja/894.tja +tja/1406.tja +tja/173.tja +tja/646.tja +tja/1228.tja +tja/1219.tja +tja/114.tja +tja/534.tja +tja/1169.tja +tja/506.tja +tja/1115.tja +tja/297.tja +tja/98.tja +tja/262.tja +tja/685.tja +tja/857.tja +tja/1142.tja +tja/1020.tja +tja/307.tja +tja/1271.tja +tja/1227.tja +tja/1309.tja +tja/551.tja +tja/1213.tja +tja/402.tja +tja/123.tja +tja/1064.tja +tja/371.tja +tja/1343.tja +tja/748.tja +tja/726.tja +tja/1380.tja +tja/826.tja +tja/428.tja +tja/374.tja +tja/94.tja +tja/1301.tja +tja/641.tja +tja/345.tja +tja/454.tja +tja/1261.tja +tja/734.tja +tja/350.tja +tja/442.tja +tja/755.tja +tja/1188.tja +tja/1280.tja +tja/595.tja +tja/406.tja +tja/141.tja +tja/976.tja +tja/1245.tja +tja/630.tja +tja/1133.tja +tja/758.tja +tja/403.tja +tja/867.tja +tja/668.tja +tja/843.tja +tja/1424.tja +tja/566.tja +tja/722.tja +tja/157.tja +tja/824.tja +tja/284.tja +tja/571.tja +tja/425.tja +tja/1185.tja +tja/711.tja +tja/984.tja +tja/565.tja +tja/1332.tja +tja/120.tja +tja/535.tja +tja/242.tja +tja/275.tja +tja/952.tja +tja/339.tja +tja/1215.tja +tja/537.tja +tja/33.tja +tja/705.tja +tja/1127.tja +tja/943.tja +tja/1350.tja +tja/1296.tja +tja/19.tja +tja/628.tja +tja/661.tja +tja/1324.tja +tja/687.tja +tja/928.tja +tja/352.tja +tja/592.tja +tja/1434.tja +tja/1403.tja +tja/398.tja +tja/1388.tja +tja/799.tja +tja/479.tja +tja/1121.tja +tja/700.tja +tja/103.tja +tja/1397.tja +tja/1027.tja +tja/447.tja +tja/300.tja +tja/380.tja +tja/689.tja +tja/1129.tja +tja/193.tja +tja/847.tja +tja/2.tja +tja/155.tja +tja/1160.tja +tja/153.tja +tja/786.tja +tja/990.tja +tja/895.tja +tja/1256.tja +tja/573.tja +tja/672.tja +tja/673.tja +tja/880.tja +tja/64.tja +tja/260.tja +tja/1408.tja +tja/809.tja +tja/1364.tja +tja/1052.tja +tja/1409.tja +tja/445.tja +tja/771.tja +tja/612.tja +tja/318.tja +tja/1.tja +tja/1320.tja +tja/570.tja +tja/47.tja +tja/286.tja +tja/982.tja +tja/436.tja +tja/1348.tja +tja/288.tja +tja/1233.tja +tja/978.tja +tja/362.tja +tja/39.tja +tja/1355.tja +tja/1242.tja +tja/934.tja +tja/238.tja +tja/502.tja +tja/1098.tja +tja/817.tja +tja/1236.tja +tja/1146.tja +tja/384.tja +tja/594.tja +tja/961.tja +tja/1130.tja +tja/491.tja +tja/1277.tja +tja/1141.tja +tja/349.tja +tja/626.tja +tja/745.tja +tja/9.tja +tja/1304.tja +tja/1068.tja +tja/1354.tja +tja/81.tja +tja/1116.tja +tja/101.tja +tja/1243.tja +tja/1248.tja +tja/851.tja +tja/121.tja +tja/124.tja +tja/41.tja +tja/989.tja +tja/420.tja +tja/693.tja +tja/324.tja +tja/321.tja +tja/845.tja +tja/1412.tja +tja/1043.tja +tja/891.tja +tja/95.tja +tja/346.tja +tja/434.tja +tja/144.tja +tja/171.tja +tja/1173.tja +tja/40.tja +tja/852.tja +tja/391.tja +tja/958.tja +tja/677.tja +tja/530.tja +tja/59.tja +tja/1435.tja +tja/846.tja +tja/196.tja +tja/756.tja +tja/795.tja +tja/1433.tja +tja/735.tja +tja/583.tja +tja/924.tja +tja/754.tja +tja/1439.tja +tja/866.tja +tja/111.tja +tja/572.tja +tja/357.tja +tja/1158.tja +tja/973.tja +tja/751.tja +tja/1404.tja +tja/1013.tja +tja/1333.tja +tja/814.tja +tja/1300.tja +tja/1172.tja +tja/504.tja +tja/545.tja +tja/344.tja +tja/1132.tja +tja/778.tja +tja/462.tja +tja/1393.tja +tja/1415.tja +tja/605.tja +tja/1295.tja +tja/774.tja +tja/914.tja +tja/437.tja +tja/93.tja +tja/487.tja +tja/1313.tja +tja/648.tja +tja/1438.tja +tja/467.tja +tja/938.tja +tja/831.tja +tja/1317.tja +tja/1275.tja +tja/649.tja +tja/645.tja +tja/315.tja +tja/10.tja +tja/353.tja +tja/116.tja +tja/87.tja +tja/395.tja +tja/949.tja +tja/347.tja +tja/1125.tja +tja/798.tja +tja/1456.tja +tja/207.tja +tja/1018.tja +tja/803.tja +tja/1187.tja +tja/213.tja +tja/1073.tja +tja/311.tja +tja/1217.tja +tja/640.tja +tja/1168.tja +tja/865.tja +tja/945.tja +tja/1394.tja +tja/765.tja +tja/1206.tja +tja/301.tja +tja/256.tja +tja/366.tja +tja/580.tja +tja/1096.tja +tja/79.tja +tja/128.tja +tja/500.tja +tja/3.tja +tja/701.tja +tja/607.tja +tja/662.tja +tja/11.tja +tja/1091.tja +tja/1123.tja +tja/790.tja +tja/477.tja +tja/1047.tja +tja/400.tja +tja/1042.tja +tja/653.tja +tja/118.tja +tja/1071.tja +tja/146.tja +tja/224.tja +tja/36.tja +tja/1426.tja +tja/1192.tja +tja/1255.tja +tja/1054.tja +tja/1069.tja +tja/639.tja +tja/1079.tja +tja/107.tja +tja/1446.tja +tja/385.tja +tja/137.tja +tja/1030.tja +tja/91.tja +tja/113.tja +tja/886.tja +tja/247.tja +tja/610.tja +tja/498.tja +tja/811.tja +tja/878.tja +tja/561.tja +tja/35.tja +tja/1152.tja +tja/293.tja +tja/248.tja +tja/108.tja +tja/526.tja +tja/1316.tja +tja/316.tja +tja/138.tja +tja/1422.tja +tja/219.tja +tja/1382.tja +tja/482.tja +tja/1024.tja +tja/190.tja +tja/625.tja +tja/363.tja +tja/1250.tja +tja/43.tja +tja/51.tja +tja/1111.tja +tja/1183.tja +tja/1033.tja +tja/611.tja +tja/947.tja +tja/1392.tja +tja/1135.tja +tja/1031.tja +tja/278.tja +tja/819.tja +tja/516.tja +tja/1065.tja +tja/898.tja +tja/1328.tja +tja/416.tja +tja/1369.tja +tja/396.tja +tja/1441.tja +tja/1384.tja +tja/1055.tja +tja/115.tja +tja/969.tja +tja/1366.tja +tja/940.tja +tja/198.tja +tja/45.tja +tja/749.tja +tja/1420.tja +tja/1395.tja +tja/1247.tja +tja/212.tja +tja/243.tja +tja/68.tja +tja/975.tja +tja/1252.tja +tja/1305.tja +tja/375.tja +tja/245.tja +tja/1211.tja +tja/97.tja +tja/131.tja +tja/431.tja +tja/762.tja +tja/964.tja +tja/164.tja +tja/704.tja +tja/309.tja +tja/394.tja +tja/1396.tja +tja/1045.tja +tja/1421.tja +tja/664.tja +tja/988.tja +tja/272.tja +tja/738.tja +tja/1344.tja +tja/1180.tja +tja/48.tja +tja/793.tja +tja/1310.tja +tja/1450.tja +tja/388.tja +tja/736.tja +tja/856.tja +tja/929.tja +tja/163.tja +tja/1199.tja +tja/1205.tja +tja/1346.tja +tja/368.tja +tja/884.tja +tja/1326.tja +tja/666.tja +tja/1119.tja +tja/1144.tja +tja/1231.tja +tja/1278.tja +tja/1417.tja +tja/233.tja +tja/424.tja +tja/460.tja +tja/587.tja +tja/413.tja +tja/222.tja +tja/654.tja +tja/740.tja +tja/226.tja +tja/1077.tja +tja/1427.tja +tja/728.tja +tja/461.tja +tja/306.tja +tja/1124.tja +tja/1274.tja +tja/88.tja +tja/995.tja +tja/781.tja +tja/1103.tja +tja/1318.tja +tja/299.tja +tja/463.tja +tja/253.tja +tja/1260.tja +tja/13.tja +tja/999.tja +tja/1297.tja +tja/1440.tja +tja/38.tja +tja/998.tja +tja/724.tja +tja/333.tja +tja/932.tja +tja/1175.tja +tja/408.tja +tja/1374.tja +tja/608.tja +tja/73.tja +tja/484.tja +tja/1002.tja +tja/505.tja +tja/1147.tja +tja/1014.tja +tja/1165.tja +tja/527.tja +tja/308.tja +tja/182.tja +tja/552.tja +tja/404.tja +tja/1389.tja +tja/187.tja +tja/1276.tja +tja/1226.tja +tja/1193.tja +tja/1003.tja +tja/713.tja +tja/1102.tja +tja/1237.tja +tja/1270.tja +tja/785.tja +tja/737.tja +tja/1431.tja +tja/251.tja +tja/773.tja +tja/17.tja +tja/80.tja +tja/1345.tja +tja/913.tja +tja/259.tja +tja/122.tja +tja/1110.tja +tja/637.tja +tja/676.tja +tja/1267.tja +tja/935.tja +tja/314.tja +tja/1049.tja +tja/218.tja +tja/1034.tja +tja/294.tja +tja/1335.tja +tja/488.tja +tja/805.tja +tja/1008.tja +tja/1109.tja +tja/616.tja +tja/393.tja +tja/267.tja +tja/987.tja +tja/1200.tja +tja/497.tja +tja/1334.tja +tja/372.tja +tja/215.tja +tja/864.tja +tja/967.tja +tja/518.tja +tja/483.tja +tja/14.tja +tja/1266.tja +tja/554.tja +tja/1072.tja +tja/1363.tja +tja/1319.tja +tja/34.tja +tja/1086.tja +tja/1432.tja +tja/1246.tja +tja/1212.tja +tja/644.tja +tja/617.tja +tja/1161.tja +tja/542.tja +tja/337.tja +tja/948.tja +tja/1423.tja +tja/179.tja +tja/331.tja +tja/310.tja +tja/1367.tja +tja/808.tja +tja/377.tja +tja/1081.tja +tja/28.tja +tja/1113.tja +tja/1143.tja +tja/706.tja +tja/52.tja +tja/890.tja +tja/1449.tja +tja/140.tja +tja/1063.tja +tja/507.tja +tja/1265.tja +tja/741.tja +tja/191.tja +tja/66.tja +tja/231.tja +tja/1108.tja +tja/92.tja +tja/833.tja +tja/1198.tja +tja/629.tja +tja/539.tja +tja/1120.tja +tja/896.tja +tja/127.tja +tja/422.tja +tja/601.tja +tja/358.tja +tja/1138.tja +tja/257.tja +tja/936.tja +tja/549.tja +tja/1137.tja +tja/125.tja +tja/65.tja +tja/1362.tja +tja/721.tja +tja/220.tja +tja/1306.tja +tja/148.tja +tja/1038.tja +tja/591.tja +tja/197.tja +tja/456.tja +tja/1035.tja +tja/1286.tja +tja/180.tja +tja/606.tja +tja/981.tja +tja/1036.tja +tja/8.tja +tja/481.tja +tja/419.tja +tja/1365.tja +tja/142.tja +tja/927.tja +tja/365.tja +tja/1258.tja +tja/725.tja +tja/181.tja +tja/418.tja +tja/556.tja +tja/686.tja +tja/194.tja +tja/203.tja +tja/1149.tja +tja/1360.tja +tja/1386.tja +tja/378.tja +tja/16.tja +tja/834.tja +tja/1325.tja +tja/319.tja +tja/555.tja +tja/1356.tja +tja/709.tja +tja/1445.tja +tja/1351.tja +tja/994.tja +tja/1007.tja +tja/86.tja +tja/1357.tja +tja/458.tja +tja/205.tja +tja/1370.tja +tja/1011.tja +tja/1210.tja +tja/37.tja +tja/548.tja +tja/1010.tja +tja/797.tja +tja/332.tja +tja/276.tja +tja/492.tja +tja/730.tja +tja/820.tja +tja/151.tja +tja/106.tja +tja/1171.tja +tja/742.tja +tja/750.tja +tja/582.tja +tja/268.tja +tja/177.tja +tja/367.tja +tja/1196.tja +tja/421.tja +tja/448.tja +tja/578.tja +tja/588.tja +tja/757.tja +tja/1067.tja +tja/1153.tja +tja/136.tja +tja/877.tja +tja/1418.tja +tja/1339.tja +tja/536.tja +tja/338.tja +tja/237.tja +tja/1060.tja +tja/1006.tja +tja/280.tja +tja/559.tja +tja/613.tja +tja/1150.tja +tja/1401.tja +tja/1249.tja +tja/775.tja +tja/84.tja +tja/468.tja +tja/168.tja +tja/474.tja +tja/719.tja +tja/83.tja +tja/433.tja +tja/650.tja +tja/415.tja +tja/623.tja +tja/1338.tja +tja/567.tja +tja/575.tja +tja/1167.tja +tja/1465.tja +tja/931.tja +tja/1046.tja +tja/1337.tja +tja/779.tja +tja/992.tja +tja/1159.tja +tja/117.tja +tja/1194.tja +tja/235.tja +tja/139.tja +tja/208.tja +tja/440.tja +tja/1084.tja +tja/1163.tja +tja/5.tja +tja/547.tja +tja/746.tja +tja/983.tja +tja/1293.tja +tja/1352.tja +tja/405.tja +tja/429.tja +tja/963.tja +tja/818.tja +tja/720.tja +tja/1166.tja +tja/1385.tja +tja/1190.tja +tja/816.tja +tja/96.tja +tja/340.tja +tja/770.tja +tja/1001.tja +tja/915.tja +tja/1181.tja +tja/1428.tja +tja/1251.tja +tja/209.tja +tja/18.tja +tja/1050.tja +tja/888.tja +tja/1012.tja +tja/1201.tja +tja/911.tja +tja/112.tja +tja/1402.tja +tja/1186.tja +tja/63.tja +tja/1057.tja +tja/322.tja +tja/627.tja +tja/1377.tja +tja/1074.tja +tja/1419.tja +tja/707.tja +tja/503.tja +tja/1023.tja +tja/410.tja +tja/1257.tja +tja/443.tja +tja/844.tja +tja/839.tja +tja/427.tja +tja/1447.tja +tja/919.tja +tja/42.tja +tja/882.tja +tja/906.tja +tja/1229.tja +tja/776.tja +tja/828.tja +tja/246.tja +tja/1191.tja +tja/1314.tja +tja/550.tja +tja/134.tja +tja/1410.tja +tja/823.tja +tja/777.tja +tja/585.tja +tja/1040.tja +tja/684.tja +tja/1254.tja +tja/821.tja +tja/90.tja +tja/698.tja +tja/1230.tja +tja/264.tja +tja/660.tja +tja/869.tja +tja/596.tja +tja/1259.tja +tja/89.tja +tja/351.tja +tja/364.tja +tja/917.tja +tja/933.tja +tja/417.tja +tja/441.tja +tja/1048.tja +tja/383.tja +tja/266.tja +tja/473.tja +tja/1234.tja +tja/72.tja +tja/925.tja +tja/1090.tja +tja/281.tja +tja/305.tja +tja/543.tja +tja/298.tja +tja/489.tja +tja/519.tja +tja/359.tja +tja/291.tja +tja/493.tja +tja/279.tja +tja/296.tja +tja/390.tja +tja/1207.tja +tja/455.tja +tja/680.tja +tja/1262.tja +tja/766.tja +tja/202.tja +tja/470.tja +tja/70.tja +tja/702.tja +tja/82.tja +tja/753.tja +tja/951.tja +tja/76.tja +tja/1039.tja +tja/524.tja +tja/1029.tja +tja/860.tja +tja/538.tja +tja/1208.tja +tja/1407.tja +tja/850.tja +tja/166.tja +tja/835.tja +tja/480.tja +tja/804.tja +tja/411.tja +tja/621.tja +tja/1026.tja +tja/715.tja +tja/879.tja +tja/184.tja +tja/868.tja +tja/1285.tja +tja/1101.tja +tja/271.tja +tja/960.tja +tja/950.tja +tja/759.tja +tja/1051.tja +tja/525.tja +tja/971.tja +tja/1244.tja +tja/540.tja +tja/979.tja +tja/1218.tja +tja/150.tja +tja/453.tja +tja/1307.tja +tja/1330.tja +tja/905.tja +tja/889.tja +tja/733.tja +tja/1105.tja +tja/283.tja +tja/32.tja +tja/576.tja +tja/336.tja +tja/304.tja +tja/450.tja +tja/732.tja +tja/381.tja +tja/1182.tja +tja/1148.tja +tja/764.tja +tja/1058.tja +tja/386.tja +tja/631.tja +tja/907.tja +tja/574.tja +tja/1413.tja +tja/200.tja +tja/1312.tja +tja/1136.tja +tja/1253.tja +tja/49.tja +tja/397.tja +tja/105.tja +tja/211.tja +tja/241.tja +tja/881.tja +tja/110.tja +tja/1083.tja +tja/501.tja +tja/577.tja +tja/457.tja +tja/780.tja +tja/472.tja +tja/133.tja +tja/926.tja +tja/855.tja +tja/1203.tja +tja/160.tja +tja/1263.tja +tja/1104.tja +tja/1095.tja +tja/469.tja +tja/739.tja +tja/1209.tja +tja/997.tja +tja/1100.tja +tja/1061.tja +tja/794.tja +tja/1425.tja +tja/944.tja +tja/401.tja +tja/1004.tja +tja/1284.tja +tja/56.tja +tja/1353.tja +tja/1059.tja +tja/815.tja +tja/517.tja +tja/149.tja +tja/1329.tja +tja/335.tja +tja/977.tja +tja/909.tja +tja/62.tja +tja/1117.tja +tja/423.tja +tja/214.tja +tja/55.tja +tja/624.tja +tja/499.tja +tja/1078.tja +tja/939.tja +tja/962.tja +tja/26.tja +tja/1315.tja +tja/132.tja +tja/1379.tja +tja/1376.tja +tja/285.tja +tja/1387.tja +tja/414.tja +tja/609.tja +tja/1044.tja +tja/277.tja +tja/716.tja +tja/930.tja +tja/361.tja +tja/1021.tja +tja/1378.tja +tja/513.tja +tja/1154.tja +tja/99.tja +tja/199.tja +tja/1232.tja +tja/1414.tja +tja/678.tja +tja/848.tja +tja/22.tja +tja/980.tja +tja/784.tja +tja/1336.tja +tja/714.tja +tja/1436.tja +tja/632.tja +tja/438.tja +tja/1114.tja +tja/1375.tja +tja/250.tja +tja/515.tja +tja/1164.tja +tja/1331.tja +tja/126.tja +tja/31.tja +tja/942.tja +tja/647.tja +tja/61.tja +tja/1089.tja +tja/1106.tja +tja/274.tja +tja/1118.tja diff --git a/index.ts b/index.ts deleted file mode 100644 index 27ac9f0..0000000 --- a/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { - TJAParser, - NoteSequence, - BPMChangeCommand, - MeasureCommand, - DelayCommand -} from "tja"; -import { readFile } from "node:fs/promises"; - -async function main() { - const filePath = "tja/1.tja"; - console.log(`Analyzing ${filePath}...`); - - try { - const content = await readFile(filePath, "utf-8"); - const parsed = TJAParser.parse(content); - - console.log("\n--- Metadata ---"); - console.log(`Title: ${parsed.title}`); - console.log(`BPM: ${parsed.bpm}`); - console.log(`Offset: ${parsed.offset}`); - - console.log("\n--- Courses ---"); - parsed.courses.forEach((course, index) => { - console.log(`Course ${index + 1}: ${course.difficulty} (Stars: ${course.stars})`); - const commands = course.activeCourse.getCommands(); - - let totalNotes = 0; - let currentTime = 0; // in seconds - let currentBPM = parsed.bpm || 120; - let currentMeasure = { numerator: 4, denominator: 4 }; - - commands.forEach(cmd => { - if (cmd instanceof BPMChangeCommand) { - currentBPM = cmd.bpm; - } else if (cmd instanceof MeasureCommand) { - currentMeasure = { - numerator: cmd.numerator, - denominator: cmd.denominator - }; - } else if (cmd instanceof DelayCommand) { - currentTime += cmd.delay; - } else if (cmd instanceof NoteSequence) { - // Duration of one measure in seconds: - // (60 / BPM) * 4 * (numerator / denominator) - const measureDuration = (60 / currentBPM) * 4 * (currentMeasure.numerator / currentMeasure.denominator); - - const notesInSequence = cmd.notes; - const noteCount = notesInSequence.length; - - notesInSequence.forEach((note) => { - if (!note.isBlank && !note.isMeasureEnd) { - totalNotes++; - } - }); - - currentTime += measureDuration; - } - }); - - const nps = currentTime > 0 ? (totalNotes / currentTime).toFixed(2) : "0.00"; - console.log(` Total Notes: ${totalNotes}`); - console.log(` Estimated Duration: ${currentTime.toFixed(2)}s`); - console.log(` Average NPS: ${nps}`); - }); - - } catch (error) { - console.error("Error analyzing TJA file:", error); - } -} - -main(); diff --git a/measure.csv b/measure.csv new file mode 100644 index 0000000..cafc9bc --- /dev/null +++ b/measure.csv @@ -0,0 +1,1472 @@ +상수,songno,diff +12,993,ura +12,1227,ura +12,765,ura +12,1043,ura +11.9,1419,ura +11.9,765,oni +11.9,849,oni +11.9,1350,oni +11.9,120,oni +11.9,1172,oni +11.8,336,oni +11.8,1369,oni +11.8,1129,ura +11.8,954,ura +11.8,960,ura +11.7,463,oni +11.7,721,ura +11.7,1072,ura +11.7,1196,ura +11.7,1278,oni +11.6,1367,oni +11.6,1048,ura +11.6,1032,ura +11.6,1216,ura +11.6,1040,oni +11.6,1392,ura +11.5,1122,ura +11.5,834,ura +11.5,1042,ura +11.5,1080,ura +11.5,1319,ura +11.5,627,oni +11.4,393,oni +11.4,518,oni +11.4,1406,oni +11.4,1276,ura +11.4,1120,ura +11.4,1249,ura +11.3,1370,ura +11.3,856,ura +11.3,1145,ura +11.3,401,oni +11.3,868,ura +11.3,555,oni +11.3,503,oni +11.2,1329,ura +11.2,1236,oni +11.2,1411,ura +11.2,816,ura +11.2,1317,oni +11.2,1215,ura +11.2,764,oni +11.1,858,oni +11.1,451,ura +11.1,1012,ura +11.1,1310,ura +11.1,283,ura +11.1,1052,ura +11.1,896,oni +11,890,oni +11,778,oni +11,700,ura +11,1099,oni +11,866,ura +11,1465,ura +11,1066,oni +11,243,oni +10.9,186,oni +10.9,869,oni +10.9,1041,oni +10.9,892,oni +10.9,110,oni +10.9,716,ura +10.9,972,ura +10.9,990,oni +10.8,758,oni +10.8,748,oni +10.8,777,oni +10.8,999,oni +10.8,1413,ura +10.8,760,oni +10.8,763,oni +10.8,1227,oni +10.7,958,ura +10.7,441,oni +10.7,1318,ura +10.7,850,oni +10.7,1320,ura +10.7,820,oni +10.7,959,ura +10.7,628,oni +10.6,702,oni +10.6,1308,ura +10.6,593,oni +10.6,775,ura +10.6,1256,ura +10.6,775,oni +10.6,1086,oni +10.6,860,oni +10.5,1031,ura +10.5,826,oni +10.5,955,oni +10.5,845,ura +10.5,1309,ura +10.5,1185,oni +10.5,33,oni +10.5,553,ura +10.5,757,oni +10.4,685,oni +10.4,1235,ura +10.4,1255,oni +10.4,745,oni +10.4,626,ura +10.4,1108,oni +10.4,1447,oni +10.4,818,oni +10.4,761,oni +10.4,1214,ura +10.3,928,oni +10.3,762,oni +10.3,1237,ura +10.3,1394,oni +10.3,1311,ura +10.3,309,ura +10.3,1405,oni +10.3,971,oni +10.3,442,oni +10.3,756,oni +10.3,218,ura +10.2,15,ura +10.2,1081,ura +10.2,265,oni +10.2,181,ura +10.2,179,oni +10.2,814,oni +10.2,859,oni +10.2,961,oni +10.2,1121,ura +10.2,835,oni +10.2,1391,ura +10.1,495,oni +10.1,1042,oni +10.1,881,ura +10.1,1097,ura +10.1,819,ura +10.1,43,ura +10.1,992,oni +10.1,905,oni +10.1,960,oni +10.1,781,oni +10.1,851,oni +10,1412,ura +10,1085,oni +10,993,oni +10,1216,oni +10,1338,oni +10,843,oni +10,286,oni +10,355,oni +10,1247,oni +10,1432,oni +10,952,oni +10,365,oni +9.9,723,oni +9.9,1328,oni +9.9,94,oni +9.9,1201,ura +9.9,1053,ura +9.9,719,oni +9.9,697,oni +9.9,698,oni +9.9,264,ura +9.9,737,oni +9.9,982,oni +9.8,164,ura +9.8,154,ura +9.8,1371,ura +9.8,575,oni +9.8,1197,ura +9.8,1419,oni +9.8,1129,oni +9.8,1043,oni +9.8,1072,oni +9.8,577,oni +9.7,1104,oni +9.7,1229,oni +9.7,1146,ura +9.7,1264,ura +9.7,148,oni +9.7,954,oni +9.7,594,oni +9.7,735,ura +9.7,878,oni +9.7,699,oni +9.7,1434,oni +9.7,834,oni +9.7,1012,oni +9.6,171,oni +9.6,95,ura +9.6,202,ura +9.6,402,ura +9.6,1100,ura +9.6,833,oni +9.6,1071,oni +9.6,700,oni +9.6,1134,oni +9.6,943,oni +9.6,720,oni +9.6,448,ura +9.5,1379,oni +9.5,935,oni +9.5,736,ura +9.5,868,oni +9.5,1049,oni +9.5,595,oni +9.5,934,oni +9.5,1171,ura +9.5,84,ura +9.5,269,oni +9.5,1404,oni +9.5,80,ura +9.5,831,ura +9.4,331,oni +9.4,1220,ura +9.4,194,oni +9.4,804,ura +9.4,1297,oni +9.4,150,ura +9.4,1048,oni +9.4,493,ura +9.4,519,ura +9.4,1356,oni +9.4,714,oni +9.4,722,oni +9.4,335,oni +9.3,338,oni +9.3,784,oni +9.3,786,oni +9.3,121,oni +9.3,1384,oni +9.3,630,oni +9.3,477,oni +9.3,721,oni +9.3,1351,oni +9.3,220,oni +9.3,1271,ura +9.3,1383,oni +9.3,1324,oni +9.3,221,ura +9.2,48,ura +9.2,1039,oni +9.2,1173,oni +9.2,1364,ura +9.2,1050,oni +9.2,1067,oni +9.2,1068,oni +9.2,310,ura +9.2,1037,oni +9.2,68,oni +9.2,1276,oni +9.2,920,ura +9.2,128,oni +9.2,360,oni +9.1,571,oni +9.1,798,oni +9.1,1268,ura +9.1,681,ura +9.1,1027,ura +9.1,1106,oni +9.1,1411,oni +9.1,136,oni +9.1,940,oni +9.1,906,oni +9.1,427,oni +9.1,1147,oni +9.1,266,ura +9.1,73,ura +9,1395,oni +9,759,oni +9,1355,oni +9,222,ura +9,615,oni +9,774,oni +9,867,oni +9,537,ura +9,392,oni +9,1452,ura +9,450,oni +9,1257,oni +9,570,oni +9,1,oni +9,388,oni +8.9,677,oni +8.9,1380,ura +8.9,673,oni +8.9,520,ura +8.9,1154,oni +8.9,156,ura +8.9,504,ura +8.9,689,ura +8.9,803,oni +8.9,983,oni +8.9,1449,oni +8.9,1318,oni +8.9,1144,ura +8.9,815,oni +8.9,927,oni +8.9,524,oni +8.9,324,ura +8.8,499,oni +8.8,60,ura +8.8,260,oni +8.8,977,oni +8.8,980,oni +8.8,856,oni +8.8,1319,oni +8.8,96,oni +8.8,797,oni +8.8,967,oni +8.8,1346,oni +8.8,740,oni +8.8,502,oni +8.8,750,oni +8.8,1260,oni +8.7,1051,oni +8.7,914,oni +8.7,55,oni +8.7,776,ura +8.7,178,ura +8.7,1047,oni +8.7,351,oni +8.7,1015,oni +8.7,976,oni +8.7,780,oni +8.7,375,oni +8.7,248,ura +8.7,686,oni +8.7,400,ura +8.7,399,ura +8.6,361,ura +8.6,272,ura +8.6,1133,ura +8.6,1033,ura +8.6,425,ura +8.6,168,oni +8.6,734,oni +8.6,538,oni +8.6,1140,ura +8.6,1393,ura +8.6,1035,ura +8.6,574,oni +8.6,1121,oni +8.6,819,oni +8.6,1058,ura +8.6,1370,oni +8.6,1158,oni +8.5,181,oni +8.5,283,oni +8.5,730,ura +8.5,640,oni +8.5,779,oni +8.5,73,oni +8.5,879,oni +8.5,558,oni +8.5,785,oni +8.5,724,oni +8.5,846,oni +8.5,452,oni +8.5,938,ura +8.5,635,oni +8.5,500,ura +8.5,1455,ura +8.5,404,oni +8.4,414,ura +8.4,1279,oni +8.4,1168,ura +8.4,1022,ura +8.4,1308,oni +8.4,715,oni +8.4,641,oni +8.4,958,oni +8.4,1357,ura +8.4,371,oni +8.4,371,ura +8.4,118,ura +8.4,80,oni +8.4,1245,oni +8.4,513,oni +8.4,45,ura +8.3,929,oni +8.3,1249,oni +8.3,1013,oni +8.3,359,oni +8.3,20,oni +8.3,1163,oni +8.3,572,oni +8.3,948,oni +8.3,16,oni +8.3,713,oni +8.3,5,oni +8.3,994,oni +8.3,975,oni +8.3,549,oni +8.3,695,oni +8.3,1215,oni +8.2,1325,oni +8.2,131,oni +8.2,456,oni +8.2,539,ura +8.2,621,oni +8.2,517,oni +8.2,1067,ura +8.2,816,oni +8.2,811,ura +8.2,491,oni +8.2,103,oni +8.2,160,ura +8.2,1464,oni +8.2,418,oni +8.2,1274,ura +8.2,588,oni +8.2,288,oni +8.2,1120,oni +8.1,235,ura +8.1,1452,oni +8.1,1357,oni +8.1,1421,oni +8.1,363,oni +8.1,223,oni +8.1,43,oni +8.1,449,oni +8.1,31,oni +8.1,864,oni +8.1,356,oni +8.1,1052,oni +8.1,844,oni +8.1,251,oni +8.1,1304,ura +8.1,88,oni +8.1,670,oni +8.1,21,oni +8,1352,oni +8,1080,oni +8,361,oni +8,117,oni +8,164,oni +8,1446,oni +8,817,oni +8,1344,ura +8,1366,ura +8,1418,oni +8,576,oni +8,1320,oni +8,1069,oni +8,1020,ura +8,1045,oni +8,573,oni +8,1000,oni +8,746,oni +8,507,ura +7.9,397,oni +7.9,202,oni +7.9,421,oni +7.9,866,oni +7.9,45,oni +7.9,680,oni +7.9,253,ura +7.9,60,oni +7.9,152,oni +7.9,1425,oni +7.9,907,oni +7.9,526,oni +7.9,271,ura +7.9,1165,oni +7.9,1296,oni +7.9,749,oni +7.9,1322,oni +7.9,738,oni +7.8,1305,oni +7.8,647,oni +7.8,1122,oni +7.8,10,oni +7.8,1414,ura +7.8,1207,oni +7.8,1096,ura +7.8,1235,oni +7.8,951,ura +7.8,1353,ura +7.8,1431,oni +7.8,214,oni +7.8,1240,ura +7.8,1116,ura +7.8,1166,oni +7.8,433,ura +7.8,1265,ura +7.8,211,oni +7.8,550,oni +7.8,1391,oni +7.7,218,oni +7.7,751,oni +7.7,154,oni +7.7,776,oni +7.7,514,oni +7.7,454,oni +7.7,1364,oni +7.7,138,oni +7.7,395,oni +7.7,357,ura +7.7,438,oni +7.7,857,oni +7.7,1390,ura +7.7,931,oni +7.7,406,oni +7.7,190,oni +7.7,1246,oni +7.7,881,oni +7.7,1061,ura +7.6,1382,ura +7.6,566,oni +7.6,400,oni +7.6,399,oni +7.6,1413,oni +7.6,203,ura +7.6,645,ura +7.6,419,oni +7.6,794,oni +7.6,649,oni +7.6,89,oni +7.6,239,oni +7.6,1206,oni +7.6,1427,oni +7.6,431,oni +7.6,67,oni +7.6,501,oni +7.6,1345,ura +7.6,919,oni +7.5,12,oni +7.5,1146,oni +7.5,1264,oni +7.5,159,oni +7.5,684,oni +7.5,97,oni +7.5,972,oni +7.5,19,oni +7.5,932,oni +7.5,898,ura +7.5,1083,oni +7.5,506,ura +7.5,845,oni +7.5,1437,oni +7.5,1368,oni +7.5,252,oni +7.5,358,oni +7.5,568,oni +7.5,805,oni +7.4,1401,oni +7.4,676,ura +7.4,261,oni +7.4,230,oni +7.4,407,oni +7.4,72,oni +7.4,187,ura +7.4,187,oni +7.4,1329,oni +7.4,1438,oni +7.4,939,oni +7.4,1263,oni +7.4,741,ura +7.4,662,oni +7.4,601,ura +7.4,76,ura +7.4,717,ura +7.4,85,oni +7.4,391,oni +7.3,95,oni +7.3,519,oni +7.3,747,oni +7.3,629,oni +7.3,795,ura +7.3,180,oni +7.3,527,oni +7.3,1261,oni +7.3,49,ura +7.3,17,oni +7.3,910,oni +7.3,800,oni +7.3,1219,ura +7.3,140,ura +7.3,1365,oni +7.3,997,ura +7.3,1387,ura +7.3,1381,oni +7.3,462,oni +7.2,155,oni +7.2,337,oni +7.2,579,oni +7.2,579,ura +7.2,1232,oni +7.2,822,oni +7.2,1145,oni +7.2,163,ura +7.2,139,ura +7.2,1156,oni +7.2,569,oni +7.2,13,oni +7.2,184,ura +7.2,887,oni +7.2,970,oni +7.2,771,ura +7.2,471,oni +7.2,1458,oni +7.2,368,ura +7.2,451,oni +7.1,483,oni +7.1,981,ura +7.1,739,oni +7.1,1137,oni +7.1,930,oni +7.1,933,oni +7.1,567,oni +7.1,651,oni +7.1,167,oni +7.1,911,ura +7.1,592,oni +7.1,1378,oni +7.1,1159,oni +7.1,461,oni +7.1,445,oni +7.1,316,oni +7.1,1077,ura +7.1,648,ura +7.1,963,oni +7,944,oni +7,1450,ura +7,1198,oni +7,183,oni +7,412,oni +7,1354,oni +7,968,oni +7,639,oni +7,1251,ura +7,386,oni +7,1021,oni +7,874,oni +7,681,oni +7,959,oni +7,1343,ura +7,1149,ura +7,114,oni +7,322,oni +7,311,oni +7,100,ura +6.9,605,oni +6.9,191,oni +6.9,1266,oni +6.9,742,oni +6.9,1307,oni +6.9,755,oni +6.9,470,ura +6.9,405,ura +6.9,770,oni +6.9,465,oni +6.9,580,ura +6.9,1273,oni +6.9,1380,oni +6.9,1347,oni +6.9,270,ura +6.9,1388,ura +6.9,270,oni +6.9,417,ura +6.9,1170,oni +6.9,537,oni +6.8,446,oni +6.8,349,oni +6.8,716,oni +6.8,1109,oni +6.8,86,oni +6.8,262,oni +6.8,32,oni +6.8,551,ura +6.8,178,oni +6.8,108,oni +6.8,596,oni +6.8,440,oni +6.8,891,oni +6.8,224,oni +6.8,1465,oni +6.8,546,oni +6.8,149,ura +6.8,339,oni +6.8,979,oni +6.8,1323,oni +6.7,536,oni +6.7,498,oni +6.7,200,ura +6.7,348,ura +6.7,612,oni +6.7,520,oni +6.7,653,oni +6.7,585,oni +6.7,645,oni +6.7,221,oni +6.7,1084,ura +6.7,1348,oni +6.7,727,oni +6.7,425,oni +6.7,226,oni +6.7,917,oni +6.7,666,oni +6.7,1311,oni +6.7,1212,ura +6.6,1196,oni +6.6,852,oni +6.6,109,ura +6.6,410,oni +6.6,487,ura +6.6,1157,ura +6.6,646,ura +6.6,637,oni +6.6,1218,ura +6.6,259,oni +6.6,701,oni +6.6,1234,oni +6.6,432,oni +6.6,660,oni +6.6,511,ura +6.6,56,oni +6.6,215,oni +6.6,1248,oni +6.6,525,oni +6.6,309,oni +6.5,428,oni +6.5,664,ura +6.5,1082,ura +6.5,1107,oni +6.5,453,oni +6.5,1309,oni +6.5,548,oni +6.5,112,oni +6.5,552,oni +6.5,1036,oni +6.5,578,oni +6.5,402,oni +6.5,1180,oni +6.5,1225,ura +6.5,1183,oni +6.5,821,oni +6.5,671,ura +6.5,278,ura +6.5,457,oni +6.5,1371,oni +6.4,918,oni +6.4,1327,ura +6.4,346,oni +6.4,614,oni +6.4,1424,oni +6.4,372,oni +6.4,1057,oni +6.4,735,oni +6.4,707,oni +6.4,182,oni +6.4,343,oni +6.4,995,oni +6.4,1058,oni +6.4,53,oni +6.4,1184,oni +6.4,989,oni +6.4,650,oni +6.4,403,ura +6.4,134,oni +6.3,688,oni +6.3,1268,oni +6.3,150,oni +6.3,1160,oni +6.3,1312,ura +6.3,1459,oni +6.3,34,oni +6.3,704,oni +6.3,1094,oni +6.3,991,oni +6.3,424,oni +6.3,257,oni +6.3,398,oni +6.3,1349,oni +6.3,321,oni +6.3,626,oni +6.3,318,ura +6.3,64,oni +6.3,306,oni +6.3,458,oni +6.3,266,oni +6.2,284,ura +6.2,41,oni +6.2,447,ura +6.2,1256,oni +6.2,362,oni +6.2,310,oni +6.2,808,oni +6.2,1412,oni +6.2,65,oni +6.2,591,oni +6.2,1303,ura +6.2,1174,oni +6.2,389,oni +6.2,422,ura +6.2,256,ura +6.2,1451,ura +6.2,1463,oni +6.2,296,oni +6.2,793,ura +6.2,248,oni +6.2,1426,oni +6.2,956,ura +6.2,1095,oni +6.1,383,oni +6.1,496,oni +6.1,302,oni +6.1,504,oni +6.1,357,oni +6.1,70,oni +6.1,137,oni +6.1,1259,oni +6.1,63,oni +6.1,1456,oni +6.1,1226,ura +6.1,238,oni +6.1,1148,ura +6.1,1171,oni +6.1,62,oni +6.1,1205,oni +6.1,213,oni +6.1,888,oni +6.1,1362,oni +6.1,1388,oni +6,297,oni +6,416,oni +6,610,oni +6,1436,oni +6,783,oni +6,1214,oni +6,539,oni +6,276,ura +6,560,oni +6,1272,oni +6,1250,oni +6,1310,oni +6,83,oni +6,426,oni +6,1211,oni +6,173,oni +6,908,oni +6,71,oni +6,166,oni +6,1295,ura +5.9,540,oni +5.9,291,oni +5.9,544,oni +5.9,1254,oni +5.9,1315,oni +5.9,978,oni +5.9,127,oni +5.9,394,oni +5.9,285,ura +5.9,423,oni +5.9,1004,oni +5.9,1098,oni +5.9,1130,oni +5.9,1034,oni +5.9,93,oni +5.9,1139,oni +5.9,472,oni +5.9,66,oni +5.9,554,oni +5.8,1131,ura +5.8,135,ura +5.8,478,oni +5.8,507,oni +5.8,609,oni +5.8,414,oni +5.8,396,oni +5.8,1454,oni +5.8,317,ura +5.8,1402,oni +5.8,616,oni +5.8,156,oni +5.8,367,oni +5.8,1242,oni +5.8,1011,oni +5.8,1321,oni +5.8,163,oni +5.8,823,oni +5.8,314,ura +5.7,455,oni +5.7,804,oni +5.7,435,oni +5.7,413,oni +5.7,922,oni +5.7,82,oni +5.7,1231,oni +5.7,799,oni +5.7,915,oni +5.7,1133,oni +5.7,855,oni +5.7,1390,oni +5.7,1192,oni +5.7,488,oni +5.7,38,oni +5.7,212,oni +5.7,347,oni +5.7,1298,oni +5.7,113,oni +5.6,39,oni +5.6,90,oni +5.6,617,oni +5.6,87,oni +5.6,541,oni +5.6,1140,oni +5.6,459,oni +5.6,304,oni +5.6,717,oni +5.6,624,oni +5.6,865,oni +5.6,1079,oni +5.6,506,oni +5.6,1422,oni +5.6,969,oni +5.6,48,oni +5.6,324,oni +5.6,1059,oni +5.5,942,oni +5.5,561,oni +5.5,543,oni +5.5,1105,oni +5.5,652,ura +5.5,1190,oni +5.5,124,oni +5.5,625,oni +5.5,350,oni +5.5,255,oni +5.5,950,oni +5.5,448,oni +5.5,92,ura +5.5,1019,oni +5.5,1433,oni +5.5,1182,oni +5.5,247,oni +5.5,22,oni +5.5,153,oni +5.4,433,oni +5.4,1265,oni +5.4,374,oni +5.4,473,oni +5.4,559,oni +5.4,1191,oni +5.4,497,oni +5.4,1386,oni +5.4,926,ura +5.4,1035,oni +5.4,1392,oni +5.4,1457,oni +5.4,705,oni +5.4,1081,oni +5.4,1164,oni +5.4,1341,oni +5.4,9,ura +5.4,882,ura +5.4,15,oni +5.3,403,oni +5.3,160,oni +5.3,1006,oni +5.3,37,oni +5.3,100,oni +5.3,1313,oni +5.3,237,oni +5.3,1225,oni +5.3,484,oni +5.3,474,oni +5.3,1044,oni +5.3,753,oni +5.3,1014,oni +5.3,1213,oni +5.3,510,oni +5.3,988,oni +5.3,938,oni +5.3,196,oni +5.2,298,oni +5.2,1414,oni +5.2,481,oni +5.2,1393,oni +5.2,364,oni +5.2,947,oni +5.2,344,oni +5.2,308,oni +5.2,275,ura +5.2,925,oni +5.2,340,oni +5.2,675,oni +5.2,52,oni +5.2,184,oni +5.2,1119,oni +5.2,1435,oni +5.2,482,ura +5.2,1360,oni +5.2,587,oni +5.1,385,oni +5.1,703,oni +5.1,1097,oni +5.1,1132,oni +5.1,219,oni +5.1,937,oni +5.1,949,oni +5.1,523,oni +5.1,106,oni +5.1,1096,oni +5.1,1003,oni +5.1,1451,oni +5.1,268,oni +5.1,47,oni +5.1,875,oni +5.1,824,ura +5.1,730,oni +5.1,1253,oni +5,998,oni +5,274,ura +5,253,oni +5,1448,ura +5,754,oni +5,1403,oni +5,1062,oni +5,1020,oni +5,623,oni +5,1179,oni +5,1277,oni +5,945,oni +5,1241,oni +5,1082,oni +5,470,oni +5,618,oni +5,1428,oni +5,880,oni +5,711,ura +4.9,1030,oni +4.9,366,ura +4.9,1460,oni +4.9,920,oni +4.9,273,ura +4.9,1377,oni +4.9,842,ura +4.9,1203,oni +4.9,479,oni +4.9,1252,oni +4.9,728,oni +4.9,478,ura +4.9,1018,oni +4.9,1306,oni +4.9,1002,oni +4.9,277,ura +4.9,256,oni +4.9,387,oni +4.8,909,oni +4.8,607,oni +4.8,1202,oni +4.8,411,oni +4.8,545,oni +4.8,1274,oni +4.8,151,oni +4.8,342,oni +4.8,264,oni +4.8,354,oni +4.8,1467,oni +4.8,1271,oni +4.8,139,oni +4.8,149,oni +4.8,1010,oni +4.8,390,oni +4.8,1103,oni +4.8,133,oni +4.7,530,oni +4.7,911,oni +4.7,1056,oni +4.7,111,oni +4.7,1092,oni +4.7,1344,oni +4.7,1217,oni +4.7,429,oni +4.7,1267,oni +4.7,553,oni +4.7,1304,oni +4.7,1031,oni +4.7,1423,oni +4.7,1366,oni +4.7,489,oni +4.7,279,oni +4.7,565,oni +4.6,801,oni +4.6,40,oni +4.6,1157,oni +4.6,726,oni +4.6,126,oni +4.6,61,oni +4.6,380,oni +4.6,141,oni +4.6,1258,oni +4.6,941,oni +4.6,1416,oni +4.6,107,oni +4.6,1455,oni +4.6,109,oni +4.6,693,oni +4.6,1053,oni +4.6,1396,oni +4.5,1237,oni +4.5,1408,oni +4.5,500,oni +4.5,1197,oni +4.5,1175,oni +4.5,345,oni +4.5,1345,oni +4.5,444,oni +4.5,469,oni +4.5,1461,oni +4.5,208,oni +4.5,74,oni +4.5,654,oni +4.5,551,oni +4.5,897,oni +4.5,1251,oni +4.5,1339,oni +4.4,556,oni +4.4,319,oni +4.4,203,oni +4.4,1363,oni +4.4,644,oni +4.4,193,oni +4.4,1314,oni +4.4,49,oni +4.4,81,oni +4.4,1193,oni +4.4,222,oni +4.4,1292,oni +4.4,443,oni +4.4,725,oni +4.4,1219,oni +4.4,378,oni +4.3,377,oni +4.3,1199,oni +4.3,1208,oni +4.3,274,oni +4.3,689,oni +4.3,323,oni +4.3,1262,oni +4.3,1303,oni +4.3,1033,oni +4.3,687,oni +4.3,294,oni +4.3,420,oni +4.3,1155,oni +4.3,285,oni +4.2,648,oni +4.2,1240,oni +4.2,207,oni +4.2,1090,oni +4.2,951,oni +4.2,984,oni +4.2,889,oni +4.2,1152,oni +4.2,861,oni +4.2,542,oni +4.2,281,oni +4.2,1194,oni +4.2,1116,oni +4.2,1417,oni +4.1,1275,oni +4.1,1209,oni +4.1,1026,oni +4.1,828,oni +4.1,307,oni +4.1,84,oni +4.1,434,oni +4.1,1466,oni +4.1,26,oni +4.1,1138,oni +4.1,135,oni +4.1,663,oni +4.1,146,oni +4.1,1228,oni +4.1,1150,oni +4.1,1233,oni +4,1334,oni +4,1335,oni +4,534,oni +4,1168,oni +4,132,oni +4,631,oni +4,1111,oni +4,997,oni +4,1176,oni +4,300,oni +4,773,oni +4,447,oni +4,973,oni +4,1387,oni +3.9,1061,oni +3.9,1188,oni +3.9,293,oni +3.9,1054,oni +3.9,1280,oni +3.9,125,oni +3.9,1032,oni +3.9,1027,oni +3.9,348,oni +3.9,1337,oni +3.9,1126,oni +3.9,1077,oni +3.9,1100,oni +3.9,1162,oni +3.8,1115,oni +3.8,1149,oni +3.8,1293,oni +3.8,547,oni +3.8,493,oni +3.8,278,oni +3.8,1358,oni +3.8,611,oni +3.8,1112,oni +3.8,91,oni +3.8,733,oni +3.8,1282,oni +3.8,284,oni +3.8,898,oni +3.7,792,oni +3.7,276,oni +3.7,1327,oni +3.7,535,oni +3.7,652,oni +3.7,1326,oni +3.7,436,oni +3.7,926,oni +3.7,169,oni +3.7,14,oni +3.7,1420,oni +3.7,305,oni +3.6,3,oni +3.6,580,oni +3.6,267,oni +3.6,408,oni +3.6,516,oni +3.6,1415,oni +3.6,59,oni +3.6,1169,oni +3.6,1131,oni +3.6,1161,oni +3.6,1453,oni +3.6,1001,oni +3.6,1220,oni +3.6,1125,oni +3.5,275,oni +3.5,515,oni +3.5,1178,oni +3.5,76,oni +3.5,736,oni +3.5,608,oni +3.5,1070,oni +3.5,741,oni +3.5,601,oni +3.5,1226,oni +3.5,694,oni +3.5,487,oni +3.4,1023,oni +3.4,1230,oni +3.4,481,ura +3.4,44,oni +3.4,512,oni +3.4,299,oni +3.4,946,oni +3.4,1177,oni +3.4,668,oni +3.4,1148,oni +3.4,632,oni +3.4,809,oni +3.3,586,oni +3.3,981,oni +3.3,341,oni +3.3,1397,oni +3.3,1024,oni +3.3,417,oni +3.3,140,oni +3.3,157,oni +3.3,771,oni +3.3,1181,oni +3.3,706,oni +3.3,273,oni +3.2,1382,oni +3.2,1269,oni +3.2,315,oni +3.2,277,oni +3.2,1385,oni +3.2,99,oni +3.2,1287,oni +3.2,480,oni +3.2,1210,oni +3.2,51,oni +3.2,79,oni +3.1,1204,oni +3.1,1074,oni +3.1,69,oni +3.1,199,oni +3.1,209,oni +3.1,405,oni +3.1,793,oni +3.1,27,oni +3.1,98,oni +3.1,35,oni +3.1,233,oni +3,205,oni +3,142,oni +3,118,oni +3,505,oni +3,422,oni +3,1410,oni +3,613,oni +3,1135,oni +3,250,oni +3,466,oni +3,161,oni +2.9,303,oni +2.9,583,oni +2.9,1336,oni +2.9,1117,oni +2.9,661,oni +2.9,317,oni +2.9,1312,oni +2.9,242,oni +2.9,1450,oni +2.9,467,oni +2.9,105,oni +2.8,287,oni +2.8,301,oni +2.8,28,oni +2.8,921,oni +2.8,116,oni +2.8,924,oni +2.8,1316,oni +2.8,1124,oni +2.8,1407,oni +2.8,1353,oni +2.7,1022,oni +2.7,1136,oni +2.7,318,oni +2.7,460,oni +2.7,468,oni +2.7,1144,oni +2.7,782,oni +2.7,1462,oni +2.7,2,oni +2.7,480,ura +2.6,210,oni +2.6,795,oni +2.6,313,oni +2.6,671,oni +2.6,437,oni +2.6,676,oni +2.6,241,oni +2.6,962,oni +2.6,1084,oni +2.5,1398,oni +2.5,280,oni +2.5,144,oni +2.5,790,oni +2.5,1270,oni +2.5,332,oni +2.5,709,oni +2.5,352,oni +2.4,732,oni +2.4,204,oni +2.4,1285,oni +2.4,92,oni +2.4,198,oni +2.4,964,oni +2.4,1200,oni +2.4,672,oni +2.3,1073,oni +2.3,381,oni +2.3,646,oni +2.3,115,oni +2.3,200,oni +2.3,511,oni +2.3,1284,oni +2.2,1283,oni +2.2,1331,oni +2.2,1286,oni +2.2,1281,oni +2.2,1342,oni +2.2,1359,oni +2.2,236,oni +2.2,8,oni +2.1,1373,oni +2.1,956,oni +2.1,384,oni +2.1,245,oni +2.1,842,oni +2.1,913,oni +2.1,1299,oni +2.1,1409,oni +2.1,1076,oni +2,1114,oni +2,9,oni +2,1300,oni +2,177,oni +2,1301,oni +2,582,oni +2,711,oni +1.9,886,oni +1.9,122,oni +1.9,492,oni +1.9,1201,oni +1.9,1399,oni +1.9,882,oni +1.9,1186,oni +1.9,1376,oni +1.8,1187,oni +1.8,271,oni +1.8,482,oni +1.8,368,oni +1.8,1055,oni +1.8,415,oni +1.8,1153,oni +1.7,1333,oni +1.7,664,oni +1.7,1110,oni +1.7,894,oni +1.7,353,oni +1.7,197,oni +1.6,1244,oni +1.6,25,oni +1.6,1374,oni +1.6,1330,oni +1.6,36,oni +1.6,1400,oni +1.6,1029,oni +1.5,366,oni +1.5,1361,oni +1.5,1332,oni +1.5,430,oni +1.5,974,oni +1.5,11,oni +1.4,1212,oni +1.4,1218,oni +1.4,1302,oni +1.4,101,oni +1.4,1343,oni +1.3,1295,oni +1.3,877,oni +1.3,824,oni +1.3,272,oni +1.3,314,oni +1.2,831,oni +1.2,1389,oni +1.2,1118,oni +1.2,1466,ura +1.2,1448,oni +1.1,246,oni +1.1,42,oni +1.1,312,oni +1.1,1243,oni +1.1,1375,oni +1.1,333,oni +1,235,oni +1,811,oni +1,604,oni +1,895,oni \ No newline at end of file diff --git a/model/constant_predictor.py b/model/constant_predictor.py new file mode 100644 index 0000000..5f0d9d7 --- /dev/null +++ b/model/constant_predictor.py @@ -0,0 +1,21 @@ +import tensorflow as tf +import numpy as np + +def build_model(): + model = tf.keras.Sequential([ + tf.keras.layers.Dense(16, activation='relu', input_shape=(5,)), + tf.keras.layers.Dense(8, activation='relu'), + tf.keras.layers.Dense(1, activation='linear') + ]) + model.compile(optimizer='adam', loss='mse', metrics=['mae']) + return model + +def train_model(X_train, y_train): + model = build_model() + model.fit(X_train, y_train, epochs=100, batch_size=8, verbose=0) + return model + +# 사용 예시: +# input: [physical, stamina, tech, accuracy, reading] +# X_train = np.array([[12.5, 26, 0.41, 0.1, 0.0], ...]) +# y_train = np.array([11.0, ...]) diff --git a/model/dataset.json b/model/dataset.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/model/dataset.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/model/train.py b/model/train.py new file mode 100644 index 0000000..ea8c99a --- /dev/null +++ b/model/train.py @@ -0,0 +1,19 @@ +import json +import numpy as np +import tensorflow as tf + +with open('model/dataset.json', 'r') as f: + data = json.load(f) + +X = np.array([d['x'] for d in data]) +y = np.array([d['y'] for d in data]) + +model = tf.keras.Sequential([ + tf.keras.layers.Dense(32, activation='relu', input_shape=(4,)), + tf.keras.layers.Dense(16, activation='relu'), + tf.keras.layers.Dense(1) +]) +model.compile(optimizer='adam', loss='mse') +model.fit(X, y, epochs=200, verbose=0) +model.save('model/constant_model.keras') +print("Model saved to model/constant_model.keras") diff --git a/package.json b/package.json index c01bda6..873accf 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "typescript": "^5" }, "dependencies": { + "@tensorflow/tfjs-node": "^4.22.0", "tja": "^0.1.3" } } diff --git a/pipeline/orchestrator.sh b/pipeline/orchestrator.sh new file mode 100755 index 0000000..34f29f7 --- /dev/null +++ b/pipeline/orchestrator.sh @@ -0,0 +1,36 @@ +#!/bin/bash +SAMPLE_DIR=${1:-"sample"} +TRAIN_DIR="$SAMPLE_DIR/training" +VAL_DIR="$SAMPLE_DIR/validation" +MODEL_DIR="$SAMPLE_DIR/model" +DATA_DIR="$SAMPLE_DIR/dataset" +EPOCHS=50 + +while true; do + echo "--- [준비] 폴더 초기화 ---" + rm -rf "$TRAIN_DIR" "$VAL_DIR" "$DATA_DIR" "sample/output/factorize" + mkdir -p "$TRAIN_DIR" "$VAL_DIR" "$MODEL_DIR" "$DATA_DIR" "sample/output/factorize" "sample/output" + + echo "--- [채보 선별] ---" + find tja -name "*.tja" | sort -R > file_list.txt + head -n 200 file_list.txt | xargs -I {} cp {} "$TRAIN_DIR/" + tail -n 50 file_list.txt | xargs -I {} cp {} "$VAL_DIR/" + + echo "--- [학습 진행] Epoch: $EPOCHS ---" + ./scripts/run_train.sh "$TRAIN_DIR" "$MODEL_DIR" "$DATA_DIR" "$EPOCHS" + + echo "--- [검증 및 상수 추론] ---" + bun run scripts/predict_batch.ts "$MODEL_DIR" "$SAMPLE_DIR" + bun run scripts/compare_results.ts "$SAMPLE_DIR" + + # 오차 검사 + high_error=$(jq 'map(select(.diff != null and (.diff | tonumber | fabs > 0.1))) | length' "$SAMPLE_DIR/comparison.json") + + if [ "$high_error" -eq 0 ]; then + echo "목표 달성: 모든 예측 오차가 0.1 이내입니다." + break + else + echo "오차 발생: $high_error 건이 0.1 초과. 학습 강도(Epoch)를 2배로 늘려 재학습합니다." + EPOCHS=$((EPOCHS * 2)) + fi +done diff --git a/scripts/clean_csv.ts b/scripts/clean_csv.ts new file mode 100644 index 0000000..cc42e66 --- /dev/null +++ b/scripts/clean_csv.ts @@ -0,0 +1,29 @@ +import { readFile, writeFile } from "node:fs/promises"; + +async function cleanCsv() { + const filePath = "measure.csv"; + try { + const content = await readFile(filePath, "utf-8"); + const lines = content.split("\n"); + if (lines.length === 0) return; + + const cleanedLines = lines.map(line => { + if (!line.trim()) return ""; + + // Basic CSV split (Note: does not handle quoted commas, + // but based on our previous read, the columns we need are early and simple) + const cols = line.split(","); + + // Indices: 상수(1), songno(3), diff(4) -> 0-based: 1, 3, 4 + const selected = [cols[1], cols[3], cols[4]]; + return selected.join(","); + }).filter(line => line !== ""); + + await writeFile(filePath, cleanedLines.join("\n")); + console.log("measure.csv cleaned successfully."); + } catch (error) { + console.error("Error cleaning CSV:", error); + } +} + +cleanCsv(); diff --git a/scripts/compare.ts b/scripts/compare.ts new file mode 100644 index 0000000..4e8cf1a --- /dev/null +++ b/scripts/compare.ts @@ -0,0 +1,19 @@ +import { readFile } from "node:fs/promises"; + +async function compare() { + // 예시 데이터: 산출된 예측 상수와 csv의 실제 상수 비교 + const data = [ + { title: "Tenjiku 2000", actual: 11.0, predicted: 5.47 }, + { title: "Yuugen no Ran", actual: 11.7, predicted: 4.64 }, + { title: "Joubutsu 2000", actual: 11.0, predicted: 7.13 }, + { title: "Kita Saitama 2000", actual: 11.0, predicted: 7.40 }, + { title: "Shimedore 2000", actual: 11.1, predicted: 4.81 } + ]; + + console.log("--- 상수 비교 분석 ---"); + data.forEach(d => { + const diff = (d.actual - d.predicted).toFixed(2); + console.log(`${d.title}: 실제(${d.actual}) vs 예측(${d.predicted}) | 오차: ${diff}`); + }); +} +compare(); diff --git a/scripts/compare_results.ts b/scripts/compare_results.ts new file mode 100644 index 0000000..16b1c3d --- /dev/null +++ b/scripts/compare_results.ts @@ -0,0 +1,20 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; + +async function compare() { + const csvContent = await readFile("measure.csv", "utf-8"); + const lines = csvContent.split("\n").slice(1); + const predictions = JSON.parse(await readFile("sample/results.json", "utf-8")); + + const diffMap: any = { "Oni": "oni", "Edit": "ura", "Ura": "ura" }; + const comparison = predictions.map((p: any) => { + const songno = p.file.match(/(\d+)\.tja/)?.[1]; + const match = lines.find(l => l.split(",")[1] === songno && l.split(",")[2] === diffMap[p.course]); + const actual = match ? parseFloat(match.split(",")[0]) : null; + return { title: p.title, actual, predicted: parseFloat(p.predicted), diff: actual ? (actual - parseFloat(p.predicted)).toFixed(2) : null }; + }); + + await writeFile("sample/comparison.json", JSON.stringify(comparison, null, 2)); + console.log("비교 완료: sample/comparison.json에 저장되었습니다."); +} +compare().catch(console.error); diff --git a/scripts/factorize.ts b/scripts/factorize.ts new file mode 100644 index 0000000..1bd5cea --- /dev/null +++ b/scripts/factorize.ts @@ -0,0 +1,57 @@ +import { TJAParser, NoteSequence, BPMChangeCommand, MeasureCommand, DelayCommand, ScrollCommand, MasterBranchMarkerCommand, BranchMarkerCommand } from "tja"; +import { readFile, writeFile, mkdir } from "node:fs/promises"; +import { join, basename } from "node:path"; + +async function factorizeTJA(filePath: string) { + try { + const content = await readFile(filePath, "utf-8"); + const parsed = TJAParser.parse(content, false); + const results: any[] = []; + const targetCourses = parsed.courses.filter(c => ["Oni", "Edit", "Ura"].includes(c.difficulty.toString())); + + for (const course of targetCourses) { + const commands = course.activeCourse.getCommands(); + const timeline: any[] = []; + let currentTime = 0, currentBPM = parsed.bpm || 120, currentMeasure = { n: 4, d: 4 }; + let inBranch = false, isMaster = false, scrollChanges = 0, bpmChanges = 0; + + commands.forEach(cmd => { + if (cmd instanceof BPMChangeCommand) { currentBPM = cmd.bpm; bpmChanges++; } + else if (cmd instanceof MeasureCommand) currentMeasure = { n: cmd.value.numerator, d: cmd.value.denominator }; + else if (cmd instanceof DelayCommand) currentTime += cmd.delay; + else if (cmd instanceof ScrollCommand) scrollChanges++; + else if (cmd instanceof MasterBranchMarkerCommand) { inBranch = true; isMaster = true; } + else if (cmd instanceof BranchMarkerCommand) { inBranch = true; isMaster = false; } + else if (cmd instanceof NoteSequence) { + if (inBranch && !isMaster) return; + const interval = ((60 / currentBPM) * 4 * (currentMeasure.n / currentMeasure.d)) / cmd.notes.length; + cmd.notes.forEach(note => { + if (!note.isBlank && !note.isMeasureEnd) timeline.push({ time: currentTime, isDon: note.isDon || note.isBigDon }); + currentTime += interval; + }); + } + }); + if (timeline.length === 0) continue; + let peakNps = 0; + for (let i = 0; i < timeline.length; i++) { + let count = 0; + for (let j = i; j < timeline.length && timeline[j].time < timeline[i].time + 2; j++) count++; + peakNps = Math.max(peakNps, count / 2); + } + let transitions = 0; + for (let i = 1; i < timeline.length; i++) if (timeline[i].isDon !== timeline[i-1].isDon) transitions++; + results.push({ + difficulty: course.difficulty.toString(), + factors: { physical: peakNps, stamina: 0, tech: transitions / timeline.length, accuracy: 0.1, reading: (bpmChanges * 0.5) + (scrollChanges * 0.2) } + }); + } + + if (results.length > 0) { + await mkdir("sample/output/factorize", { recursive: true }); + await writeFile(join("sample/output/factorize", `${basename(filePath, ".tja")}.json`), JSON.stringify({ title: parsed.title, file: filePath, analysis: results }, null, 2)); + } + + } catch (e) { console.error(`Failed ${filePath}: ${e}`); } +} + +for (const f of process.argv.slice(2)) await factorizeTJA(f); diff --git a/scripts/predict.ts b/scripts/predict.ts new file mode 100644 index 0000000..001aefd --- /dev/null +++ b/scripts/predict.ts @@ -0,0 +1,11 @@ +import * as tf from "@tensorflow/tfjs-node"; + +async function predict(factors: number[]) { + const model = await tf.loadLayersModel("file://sample/model/model.json"); + const input = tf.tensor2d([factors]); + const prediction = model.predict(input) as tf.Tensor; + console.log(`Predicted Constant: ${prediction.dataSync()[0].toFixed(2)}`); +} + +// 예시: [physical, stamina, tech, reading] +predict([12.5, 26, 0.41, 0.0]).catch(console.error); diff --git a/scripts/predict_batch.ts b/scripts/predict_batch.ts new file mode 100644 index 0000000..15ea35d --- /dev/null +++ b/scripts/predict_batch.ts @@ -0,0 +1,21 @@ +import * as tf from "@tensorflow/tfjs-node"; +import { readFile, writeFile, readdir } from "node:fs/promises"; +import { join } from "node:path"; + +async function predictBatch() { + const model = await tf.loadLayersModel("file://sample/model/model.json"); + const files = (await readdir("sample/output/factorize")).filter(f => f.endsWith(".json")); + const results = []; + + for (const file of files) { + const data = JSON.parse(await readFile(join("sample/output/factorize", file), "utf-8")); + for (const analysis of data.analysis) { + const input = tf.tensor2d([Object.values(analysis.factors)]); + const pred = (model.predict(input) as tf.Tensor).dataSync()[0]; + results.push({ title: data.title, file: data.file, course: analysis.difficulty, predicted: pred.toFixed(2) }); + } + } + await writeFile("sample/results.json", JSON.stringify(results, null, 2)); + console.log("Prediction complete."); +} +predictBatch().catch(console.error); diff --git a/scripts/prepare_dataset.ts b/scripts/prepare_dataset.ts new file mode 100644 index 0000000..e519750 --- /dev/null +++ b/scripts/prepare_dataset.ts @@ -0,0 +1,26 @@ +import { readFile, writeFile, readdir } from "node:fs/promises"; +import { join, basename } from "node:path"; + +async function prepare() { + const csvContent = await readFile("measure.csv", "utf-8"); + const lines = csvContent.split("\n").slice(1); + const factorizeDir = "sample/output/factorize"; + const files = (await readdir(factorizeDir)).filter(f => f.endsWith(".json")); + const dataset: any[] = []; + + for (const file of files) { + const data = JSON.parse(await readFile(join(factorizeDir, file), "utf-8")); + const songno = basename(file, ".json"); + + const match = lines.find(l => l.split(",")[1] == songno); + if (match) { + const constant = parseFloat(match.split(",")[0]); + for (const analysis of data.analysis) { + dataset.push({ x: Object.values(analysis.factors), y: constant }); + } + } + } + await writeFile("sample/dataset/dataset.json", JSON.stringify(dataset)); + console.log(`Dataset prepared with ${dataset.length} samples.`); +} +prepare().catch(console.error); diff --git a/scripts/run_predict.sh b/scripts/run_predict.sh new file mode 100755 index 0000000..4949cc7 --- /dev/null +++ b/scripts/run_predict.sh @@ -0,0 +1,9 @@ +#!/bin/bash +TJA_DIR=$1 +if [ -z "$TJA_DIR" ]; then echo "사용법: $0 "; exit 1; fi +echo "--- 채보 상수 예측 시작 ---" +for f in $(find "$TJA_DIR" -name "*.tja"); do + bun run scripts/factorize.ts "$f" > /dev/null + # 결과 JSON을 읽어 예측을 수행하는 로직을 predict.ts에 통합하는 것을 추천합니다. + echo "예측 작업이 완료되었습니다." +done diff --git a/scripts/run_train.sh b/scripts/run_train.sh new file mode 100755 index 0000000..30decec --- /dev/null +++ b/scripts/run_train.sh @@ -0,0 +1,11 @@ +#!/bin/bash +TJA_DIR=${1:-"sample/training"} +MODEL_PATH=${2:-"sample/model"} +DATASET_DIR=${3:-"output/dataset"} + +mkdir -p "$MODEL_PATH" "$DATASET_DIR" "sample/factorize" + +echo "--- 분석 및 학습 시작 ---" +for f in $(find "$TJA_DIR" -name "*.tja"); do bun run scripts/factorize.ts "$f" > /dev/null; done +bun run scripts/prepare_dataset.ts "$DATASET_DIR" +bun run scripts/train.ts "$MODEL_PATH" "$DATASET_DIR" diff --git a/scripts/train.ts b/scripts/train.ts new file mode 100644 index 0000000..71f30ae --- /dev/null +++ b/scripts/train.ts @@ -0,0 +1,35 @@ +import * as tf from "@tensorflow/tfjs-node"; +import { readFile } from "node:fs/promises"; +import { existsSync } from "node:fs"; +import { join } from "node:path"; + +async function train() { + const savePath = process.argv[2] || 'sample/model'; + const data = JSON.parse(await readFile(join(process.argv[3] || 'sample/dataset', 'dataset.json'), "utf-8")); + const X = tf.tensor2d(data.map((d: any) => d.x.map((v: any, i: number) => v / (i == 0 ? 20 : i == 1 ? 200 : 1)))); + const y = tf.tensor2d(data.map((d: any) => [d.y])); + + let model: tf.LayersModel; + const modelJsonPath = join(savePath, "model.json"); + + if (existsSync(modelJsonPath)) { + console.log("기존 모델을 불러와 추가 학습을 진행합니다."); + model = await tf.loadLayersModel(`file://${modelJsonPath}`); + } else { + console.log("새 모델을 생성합니다."); + model = tf.sequential({ + layers: [ + tf.layers.dense({ units: 32, activation: 'relu', inputShape: [5] }), + tf.layers.dense({ units: 16, activation: 'relu' }), + tf.layers.dense({ units: 1 }) + ] + }); + } + + model.compile({ optimizer: 'adam', loss: 'meanSquaredError' }); + const epochs = parseInt(process.argv[4]) || 50; + await model.fit(X, y, { epochs: epochs, verbose: 0 }); + await model.save(`file://${savePath}`); + console.log(`학습 완료: 모델이 ${savePath}에 저장되었습니다.`); +} +train().catch(console.error); diff --git a/tsconfig.json b/tsconfig.json index bfa0fea..be3d138 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { // Environment setup & latest features - "lib": ["ESNext"], + "lib": ["ESNext", "DOM"], "target": "ESNext", "module": "Preserve", "moduleDetection": "force",