Skip to content

Commit 2b4df32

Browse files
rlafurudclaude
andcommitted
feat(playback): stat-driven simulation from real BAR unit data
- Add frontend/src/data/unitStats.json: HP/DPS/range/speed/struct for 188 Armada units (extracted from the v4 unit DB). - SimPlayback now uses per-unit real stats instead of flat hardcoded values; unit size scales with HP, damaged units show health bars, firing units draw attack lines. Commander d-gun DPS is capped so it doesn't one-shot the map. - Refactor: extract pure sim logic into src/lib/simModel.js (statFor, worldSize, findSpawner, victoryTime, makeUnit, buildUnits) so it is testable and decoupled from the React/canvas component. - Add vitest + src/lib/simModel.test.js (11 tests) and a 'test' npm script. - Add root CLAUDE.md documenting architecture and commands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2370232 commit 2b4df32

7 files changed

Lines changed: 2618 additions & 84 deletions

File tree

CLAUDE.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## What this is
6+
7+
Text prompt → Beyond All Reason (BAR) minigame config (JSON) generator. A natural-language
8+
query is matched against a database of existing game scenarios, then a final scenario config is
9+
synthesized and visualized in the browser. The README is in Korean and is the canonical design doc.
10+
11+
## Commands
12+
13+
Backend (FastAPI, Python 3.11):
14+
```bash
15+
cd backend
16+
pip install -r requirements.txt
17+
cp .env.example .env # set OPENAI_API_KEY (BASE_URL optional)
18+
uvicorn app:app --reload # http://localhost:8000
19+
python pipeline.py "<prompt>" # run the full pipeline from the CLI, no server
20+
python db_call.py # smoke-test DB folder/file LLM routing
21+
```
22+
23+
Frontend (React + Vite, requires Node 20):
24+
```bash
25+
cd frontend
26+
npm install
27+
npm run dev # http://localhost:5173, proxies /api -> :8000
28+
npm run build # production build (this is what CI runs)
29+
```
30+
31+
There is no test suite. CI (`.github/workflows/ci.yml`, on push/PR to `main`/`dev`) only does
32+
`npm run build` for the frontend and, for the backend, `python -m compileall` + `import app`
33+
with a dummy `OPENAI_API_KEY`. **Module import must stay side-effect-free** (no LLM client built
34+
at import time) or CI breaks — clients are constructed lazily inside functions/constructors.
35+
36+
## Architecture
37+
38+
The core design decision: **do not generate a GDD or new rules from scratch.** Find the most
39+
similar existing scenario in `backend/db/`, reuse its validated rules, and only synthesize the
40+
final script. An earlier multi-agent build with an analyst/verify loop was deliberately stripped
41+
to remove the `game_simulation` (BAR engine) and `psutil` runtime dependencies — do not
42+
reintroduce them.
43+
44+
### Backend pipeline (`backend/pipeline.py`, `generate()`) — 3 steps
45+
46+
1. **`find_scenario(query)`** — match the prompt to a scenario in `db/scenario/meta.json`.
47+
Resilient by design: LLM match (`DBCall`) → keyword overlap fallback (`_keyword_fallback`) →
48+
default to the first scenario. Returns `None` only if the scenario DB is unreadable.
49+
2. **`load_existing_mode(name)`** — build a `gdd` dict from the scenario's `specification` and its
50+
referenced rules. Every rule is loaded as `action: existing, validated: True` (never generated).
51+
Rule `.lua` files are copied into the per-session `log/<timestamp>/.../rules_temp/`.
52+
3. **`ScriptDeveloperAgent.run()`** (`script_builder.py`) — a linear LangGraph:
53+
`select_map → place_units → generate_rule_config → get_condition → assemble_draft`. Each node
54+
is one LLM call (prompts in `developer_prompt.py`); `select_map`/`place_units` loop, issuing
55+
`call_db` actions to fetch more context, until the LLM returns `finish`.
56+
57+
`generate()` returns `{scenario, config, raw}` where `config` is the `"normal"` difficulty entry of
58+
`final_json`. The agent always produces exactly the `["normal"]` difficulty.
59+
60+
### DBCall (`backend/db_call.py`) — two-stage LLM retrieval
61+
62+
`db/meta.json` lists folders (`scenario`, `map`, `unit`, `rule`, `decision`, `verify`, `rubric`);
63+
each folder's `meta.json` has a `details` map of `{name: {description, file}}`. `DBCall.call(query)`
64+
asks the LLM to pick a folder, then which files within it — returning file paths relative to `db/`.
65+
Pass `folder=` to skip folder selection. `call_with_names()` also returns the matched DB key names
66+
(used by `find_scenario`).
67+
68+
### API (`backend/app.py`)
69+
70+
- `POST /generate {query, seed?}` → pipeline result. `seed` is threaded down to every `ChatOpenAI`
71+
for reproducibility.
72+
- `GET /catalog` → scenarios (from `db/scenario/meta.json`) + maps (from `info/map.json`).
73+
- `GET /health`. CORS is wide open (`*`).
74+
- The top-level `try/except` returns `{error, config: None}` rather than raising.
75+
76+
### Frontend (`frontend/src/`)
77+
78+
`App.jsx` loads `/catalog` on mount and calls `/generate` via `api.js`. Components: `PromptInput`
79+
(prompt + example chips, ⌘/Ctrl+Enter to submit), `ConfigSummary`, `MiniMap` (2D canvas of
80+
`unit_placement`), `SimPlayback`, `JsonView`. No state library, no router.
81+
82+
## Key conventions
83+
84+
- **LLM model** is hardcoded to `gpt-5.2` in `common.get_client()`. Reads `OPENAI_API_KEY` and
85+
optional `BASE_URL` from env. All token usage flows through the shared `TokenTracker` callback.
86+
- **Coordinate system:** maps are sized in tiles where **1 tile = 512px**. A 16×16 map → 8192×8192
87+
pixels. Units are placed in pixel coordinates. Prompts and `MiniMap` both rely on this.
88+
- **Config shape** (`assemble_draft` output): `{information, end_condition, unit_placement, customize}`.
89+
`unit_placement` is keyed by team (`"1"`, `"2"`) → list of `[unit_code, [x, y]]`. `customize` holds
90+
rule/gadget config (e.g. `enemy_wave_spawner`).
91+
- **`backend/db/`** is the source of truth (scenarios, validated rules + their `.lua` codes, maps,
92+
units, decisions). **`backend/info/`** holds flat unit/map/gadget summaries fed into prompts
93+
(`units_info.json`, `map.json`, etc.) — distinct from `db/`.
94+
- **Logs:** every pipeline run writes to `backend/log/<timestamp>/` (gitignored). Each agent node
95+
dumps its raw result via `common.log_node_result`.
96+
- **Name matching is fuzzy on purpose:** `pipeline._resolve_db_name` / `_normalize_name` reconcile
97+
LLM-returned names against DB keys (case, spaces, parentheses, hyphens). Keep this when LLM output
98+
may not match DB keys exactly.
99+
100+
## Git workflow
101+
102+
`main → dev → feature/*`. Branch `feature/*` off `dev`; PR `feature/* → dev`; release via
103+
`dev → main`. PRs close issues with `Closes #<n>`. Project docs live in the GitHub Wiki (`wiki/`).

0 commit comments

Comments
 (0)