207 lines
6.8 KiB
Markdown
207 lines
6.8 KiB
Markdown
|
|
---
|
|||
|
|
type: 설계문서
|
|||
|
|
project: 수상한잡화점
|
|||
|
|
subject: 시뮬레이션 결과 출력 JSON 포맷
|
|||
|
|
version: v1
|
|||
|
|
date: 2026-04-17
|
|||
|
|
status: 초판
|
|||
|
|
author: 개발팀장
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 결과 JSON 포맷 v1
|
|||
|
|
|
|||
|
|
## 1. 목적
|
|||
|
|
|
|||
|
|
`SimulationRunner` 실행 결과의 **출력 포맷**. 기획팀이 후처리·비교·차트화할 때 구조화된 형태로 활용. 단일 실행·스윕·배치 모두 동일 스키마의 확장.
|
|||
|
|
|
|||
|
|
## 2. 단일 실행 결과
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"schema_version": "1.0",
|
|||
|
|
"scenario_id": "anchor_stage1_no_card_4mob_touch",
|
|||
|
|
"run_id": "run_20260417_142301_0001",
|
|||
|
|
"timestamp": "2026-04-17T14:23:01Z",
|
|||
|
|
"seed": 42,
|
|||
|
|
"result": {
|
|||
|
|
"pc_survived": true,
|
|||
|
|
"pc_remaining_hp": 23,
|
|||
|
|
"pc_remaining_hp_ratio": 0.23,
|
|||
|
|
"total_turns": 142,
|
|||
|
|
"duration_sec": 14.2,
|
|||
|
|
"monsters_killed": 4,
|
|||
|
|
"monsters_killed_ids": ["m1", "m2", "m3", "m4"],
|
|||
|
|
"pc_damage_taken_total": 77,
|
|||
|
|
"pc_damage_blocked_total": 33,
|
|||
|
|
"pc_damage_dealt_total": 60,
|
|||
|
|
"defence_activations": 14,
|
|||
|
|
"defence_active_ratio": 0.42,
|
|||
|
|
"attacks_by_pc": 8,
|
|||
|
|
"attacks_by_pc_blocked_ratio": 0.0
|
|||
|
|
},
|
|||
|
|
"breakdown": {
|
|||
|
|
"damage_taken_by_monster": {"m1": 16, "m2": 20, "m3": 22, "m4": 19},
|
|||
|
|
"damage_blocked_by_monster": {"m1": 7, "m2": 9, "m3": 10, "m4": 7},
|
|||
|
|
"defence_duration_sec": 5.96
|
|||
|
|
},
|
|||
|
|
"detail_log_path": null,
|
|||
|
|
"errors": []
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 3. 필드 정의
|
|||
|
|
|
|||
|
|
### 3-1. 메타
|
|||
|
|
| 필드 | 설명 |
|
|||
|
|
|------|------|
|
|||
|
|
| `schema_version` | 결과 스키마 버전 |
|
|||
|
|
| `scenario_id` | 입력 시나리오 ID echo |
|
|||
|
|
| `run_id` | 실행별 고유 ID (스윕·배치에서 중복 방지) |
|
|||
|
|
| `timestamp` | ISO 8601 UTC |
|
|||
|
|
| `seed` | 사용된 난수 시드 |
|
|||
|
|
|
|||
|
|
### 3-2. result (결과 요약 — 밸런싱 판단 핵심 축)
|
|||
|
|
| 필드 | 타입 | 설명 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `pc_survived` | bool | PC 생존 여부 |
|
|||
|
|
| `pc_remaining_hp` | float | 종료 시점 PC HP |
|
|||
|
|
| `pc_remaining_hp_ratio` | float | remaining_hp / max_hp |
|
|||
|
|
| `total_turns` | int | 실행된 tick 수 |
|
|||
|
|
| `duration_sec` | float | 시뮬 내부 경과 시간 (tick × interval) |
|
|||
|
|
| `monsters_killed` | int | 처치된 몬스터 수 |
|
|||
|
|
| `monsters_killed_ids` | string[] | 처치된 몬스터 ID 목록 |
|
|||
|
|
| `pc_damage_taken_total` | float | PC가 받은 총 피해 (감소 후) |
|
|||
|
|
| `pc_damage_blocked_total` | float | 방어로 감소된 총 피해량 (= 원본피해 - 실피해) |
|
|||
|
|
| `pc_damage_dealt_total` | float | PC가 입힌 총 피해 |
|
|||
|
|
| `defence_activations` | int | 방어 상태 진입 횟수 |
|
|||
|
|
| `defence_active_ratio` | float | 방어 상태 유지 시간 / 전체 시뮬 시간 |
|
|||
|
|
| `attacks_by_pc` | int | PC 공격 발동 횟수 |
|
|||
|
|
| `attacks_by_pc_blocked_ratio` | float | PC 공격이 방어로 인해 취소된 비율 (기획팀 분석용) |
|
|||
|
|
|
|||
|
|
### 3-3. breakdown (세부 분해)
|
|||
|
|
| 필드 | 타입 | 설명 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `damage_taken_by_monster` | map<id, float> | 몬스터별 가한 피해 (감소 후) |
|
|||
|
|
| `damage_blocked_by_monster` | map<id, float> | 몬스터별 감소시킨 피해량 |
|
|||
|
|
| `defence_duration_sec` | float | 누적 방어 유지 시간 |
|
|||
|
|
|
|||
|
|
### 3-4. detail_log_path (선택)
|
|||
|
|
- 입력의 `simulation.record_detail == true` 이면 tick별 상세 로그 파일 경로
|
|||
|
|
- false면 `null`
|
|||
|
|
|
|||
|
|
### 3-5. errors (진단)
|
|||
|
|
- 시뮬 중 발생한 경고·비치명 에러 목록 (string[])
|
|||
|
|
- 치명 에러는 예외 throw로 종료되므로 본 필드에 오지 않음
|
|||
|
|
|
|||
|
|
## 4. 스윕 결과
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"schema_version": "1.0",
|
|||
|
|
"sweep_id": "Defence_Mul_Grid",
|
|||
|
|
"sweep_axes": [
|
|||
|
|
{"path": "global_value.PCDefence_Mul", "values": [0.2, 0.3, 0.4, 0.5]},
|
|||
|
|
{"path": "monsters[0].attack_dmg", "values": [3, 5, 7]}
|
|||
|
|
],
|
|||
|
|
"runs_per_cell": 10,
|
|||
|
|
"cells": [
|
|||
|
|
{
|
|||
|
|
"coords": {"PCDefence_Mul": 0.2, "monster_attack_dmg": 3},
|
|||
|
|
"runs": [ /* 결과 10개 */ ],
|
|||
|
|
"stats": {
|
|||
|
|
"pc_survived_ratio": 1.0,
|
|||
|
|
"pc_remaining_hp_mean": 62.3,
|
|||
|
|
"pc_remaining_hp_std": 8.1,
|
|||
|
|
"total_turns_mean": 98,
|
|||
|
|
"total_turns_std": 12
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/* N × M cells */
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
스윕 cell별 자동 집계 통계:
|
|||
|
|
- `pc_survived_ratio`: 생존률
|
|||
|
|
- `pc_remaining_hp_mean` / `std`: 종료 HP 평균·표준편차
|
|||
|
|
- `total_turns_mean` / `std`: tick 수 평균·표준편차
|
|||
|
|
|
|||
|
|
## 5. 배치 결과
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"schema_version": "1.0",
|
|||
|
|
"batch_id": "Anchor_Strategies_Compare",
|
|||
|
|
"results": [
|
|||
|
|
{"scenario_id": "strategy_never", /* 단일 결과 §2 */},
|
|||
|
|
{"scenario_id": "strategy_always", /* ... */},
|
|||
|
|
{"scenario_id": "strategy_touch_hold", /* ... */}
|
|||
|
|
],
|
|||
|
|
"comparison": {
|
|||
|
|
"best_by": "pc_remaining_hp_ratio",
|
|||
|
|
"ranking": ["strategy_touch_hold", "strategy_always", "strategy_never"],
|
|||
|
|
"deltas": {
|
|||
|
|
"strategy_touch_hold_vs_never": {"pc_remaining_hp_ratio": 0.42, "total_turns": 34}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 6. 출력 위치 선택지
|
|||
|
|
|
|||
|
|
기획팀 결정 영역 (1차 응답서 §3-2 참조). 현 설계는 3가지 지원:
|
|||
|
|
|
|||
|
|
| 모드 | 출력 방식 | 용도 |
|
|||
|
|
|------|----------|------|
|
|||
|
|
| `stdout` | 표준 출력 스트림 | MCP `execute_code` 응답으로 즉시 수신 |
|
|||
|
|
| `file` | `Assets/Sim/Output/{scenario_id}_{timestamp}.json` | Editor 내 기록 보존 |
|
|||
|
|
| `path` | 사용자 지정 경로 (프로젝트 외부 가능) | `기획팀/.cache/` 등으로 직접 저장 |
|
|||
|
|
|
|||
|
|
`ResultEmitter.Emit(result, mode, pathOptional)` 시그니처.
|
|||
|
|
|
|||
|
|
## 7. 세부 로그 포맷 (record_detail=true 시)
|
|||
|
|
|
|||
|
|
별도 파일 `detail_log_path`가 가리키는 JSON (tick별 상태):
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"scenario_id": "...",
|
|||
|
|
"ticks": [
|
|||
|
|
{
|
|||
|
|
"t": 0.0,
|
|||
|
|
"pc": {"hp": 100, "is_defencing": false, "attack_acc": 0.0},
|
|||
|
|
"monsters": [{"id": "m1", "hp": 15, "attack_acc": 0.0}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"t": 0.1,
|
|||
|
|
"pc": {"hp": 100, "is_defencing": true},
|
|||
|
|
"events": ["monster_m1_projectile_spawned", "pc_defence_activated"]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**주의**: 상세 로그 파일은 tick당 수 KB이므로 `max_turns: 500` 시나리오에서 수 MB까지 가능. 평소 off 권장, 밸런스 디버깅 시점만 on.
|
|||
|
|
|
|||
|
|
## 8. 후처리 가이드 (기획팀)
|
|||
|
|
|
|||
|
|
### 8-1. 단일 실행 분석
|
|||
|
|
- `pc_remaining_hp_ratio` 를 "체감 난이도" 지표로 활용 (0.5 이상 = 여유, 0.2 이하 = 타이트)
|
|||
|
|
- `defence_active_ratio` 가 과도히 높으면 (>0.6) 방어가 의도보다 강력함 신호
|
|||
|
|
|
|||
|
|
### 8-2. 스윕 분석
|
|||
|
|
- `pc_survived_ratio == 1.0` 이지만 `pc_remaining_hp_ratio` 편차가 큰 cell = 불안정 밸런스
|
|||
|
|
- `stats.total_turns_std / mean > 0.3` = 난수 편차 과다 (시나리오 취약)
|
|||
|
|
|
|||
|
|
### 8-3. 배치 분석
|
|||
|
|
- `comparison.ranking` 로 전략 우위 확인
|
|||
|
|
- `deltas` 로 구체 수치 차이 측정
|
|||
|
|
|
|||
|
|
## 9. 변경 이력
|
|||
|
|
- **v1 (2026-04-17)**: 초판.
|
|||
|
|
|
|||
|
|
## 10. 기각안
|
|||
|
|
- **기각안 A**: Markdown 결과 출력 → 후처리 자동화 곤란. JSON 구조화가 밸런싱 툴 호환성 최고
|
|||
|
|
- **기각안 B**: Excel/CSV 결과 → 중첩 구조(breakdown/sweep cells) 표현 제한. JSON + 필요 시 CSV 변환이 유연
|
|||
|
|
- **기각안 C**: tick 로그 항상 포함 → 토큰·디스크 낭비. 플래그 제어가 효율
|