# DRL pinball training quickstart

This directory contains the source workflow for calibrating and training the modern 2000×600 CelerisLab PPO environments. The release reproduces the workflow and includes compact best-policy bundles for the retained active runs. Existing `results/` material remains historical support for prior writing and is not a claim that current code has been fully retrained or validated.

## Setup

From the repository root:

```bash
git submodule update --init --recursive
conda create -n pycuda_3_10 python=3.10 -y
conda activate pycuda_3_10
pip install pycuda
pip install -e CelerisLab
pip install -e .
```

Run on Linux with an NVIDIA GPU. Commands below assume the repository's `pycuda_3_10` environment.

## Calibrate the target

Training requires a matched, calibrated artifact set: `calibration.json` and `target.npy`, plus `target_harmonics.json` for Illusion. A JSON alone is not a complete target artifact. Generate the target on the training server before training; do not substitute an uncalibrated target. For example:

```bash
cd src/drl_pinball/train
conda run --no-capture-output -n pycuda_3_10 python -u calibrate.py \
  --case kar_re100 --device-id 0 --si 800 \
  --config ../../../configs/config_lbm_karman_2000x600.json \
  --out-dir calibrations/kar_re100
``` Canonical active case IDs omit method suffixes:

- Karman: `kar_re100`, `kar_re60`, `kar_re200`, `kar_re400`, `kar_d075`, `kar_d15`, `kar_d2`
- Illusion: `ill_075L`, `ill_1L`, `ill_15L`, `ill_2L`

`_tr` identifies historical archived transfer runs only. Older `_sc` names may still appear inside retained artifacts and history; they are not canonical active IDs.

## Train

Launchers run cases serially on one GPU:

```bash
cd src/drl_pinball/train
bash scripts/train_baseline.sh --gpu 0 --seeds 41,42 --episodes 500
bash scripts/crossre_scratch.sh --gpu 0 --only re200 --episodes 500
bash scripts/vardist_scratch.sh --gpu 0 --only d15 --episodes 500
bash scripts/train_illusion.sh --gpu 0 --only 1L --episodes 500
```

For an explicit single run, use the Python entry point and the matching config/calibration:

```bash
conda run --no-capture-output -n pycuda_3_10 python -u train_karman.py \
  --case-name kar_re100 --device-id 0 --seed 42 \
  --config ../../../configs/config_lbm_karman_2000x600.json \
  --calibration calibrations/kar_re100/calibration.json \
  --total-episodes 500 --symmetry-prob 0
```

Illusion uses `train_illusion.py` and an `ill_*` calibration. Symmetry is intentionally default-off; see `DESIGN_DECISIONS.md`.

Resume requires the matching policy and normalizer checkpoint:

```bash
bash scripts/resume.sh --case kar_re200 --seed 43 --resume 460 --episodes 500 --gpu 0
```

A training “episode” in these interfaces is one PPO learning chunk (normally 2048 environment steps) followed by a 360-step deterministic evaluation. It is not a Gym episode.

Outputs are written below `output/{case}_seed{seed}/`. The repository retains only each active run’s compact inference bundle: `models/best_model.zip`, `best_vecnormalize.pkl`, `meta.json`, `calibration.json`, and a run-local `target.npy` when available. Canonical calibration targets provide the documented fallback for retained legacy bundles. Intermediate/final checkpoints, TensorBoard streams, and logs remain local. Resume is policy-plus-normalizer continuation, not exact process replay.

See `TRAIN_PIPELINE.md` for the full data path, `SERVER_DEPLOY.md` for server commands, and `DESIGN_DECISIONS.md` for conventions and limitations.
