# NNUE Training Pipeline

This directory contains the complete NNUE (Efficiently Updatable Neural Network) training pipeline for the Now-Chess bot.

## Overview

The pipeline generates 500,000 random chess positions, evaluates them with Stockfish, trains a neural network, and exports the weights as Scala code for integration into the engine.

## Prerequisites

Install Python dependencies:

```bash
pip install -r requirements.txt
```

Ensure Stockfish is installed. You can:
- Install via package manager: `apt-get install stockfish` (Linux) or `brew install stockfish` (macOS)
- Or download from [stockfish.org](https://stockfishchess.org)

Set the Stockfish path:
```bash
export STOCKFISH_PATH=/path/to/stockfish
```

## Pipeline Steps

### Quick Run

Run the entire pipeline:

```bash
chmod +x run_pipeline.sh
./run_pipeline.sh
```

This automatically runs all 4 steps in sequence and confirms each succeeds before continuing.

### Individual Steps

#### Step 1: Generate Positions

Generate 500,000 random chess positions:

```bash
python3 generate_positions.py positions.txt
```

Output: `positions.txt` (one FEN per line)
- Plays 8-20 random opening moves
- Filters out checks, captures available, and game-over positions
- Shows progress bar with tqdm

#### Step 2: Label with Stockfish

Evaluate each position with Stockfish at depth 12:

```bash
export STOCKFISH_PATH=/path/to/stockfish
python3 label_positions.py positions.txt training_data.jsonl $STOCKFISH_PATH
```

Output: `training_data.jsonl` (one JSON per line)
- Format: `{"fen": "...", "eval": 123}` (centipawns)
- Evals clamped to [-2000, 2000] to avoid mate score outliers
- Supports resuming if interrupted (checks for existing entries)
- Shows progress bar with tqdm

**Note:** This step is slow (~24-36 hours for 500K positions at depth 12). You can reduce games or use lower depth for testing.

#### Step 3: Train NNUE Model

Train the neural network:

```bash
python3 train_nnue.py training_data.jsonl nnue_weights.pt
```

Output: `nnue_weights.pt` (PyTorch model weights)

Architecture:
- Input: 768 binary features (12 piece types × 64 squares)
- Hidden 1: 256 neurons + ReLU
- Hidden 2: 32 neurons + ReLU
- Output: 1 neuron (sigmoid applied to eval/400)

Training:
- 20 epochs, batch size 4096, Adam optimizer (lr=1e-3)
- 90% train / 10% validation split
- Saves best weights by validation loss
- Shows train/val loss per epoch

**Note:** Requires GPU for reasonable speed (~2-4 hours). CPU falls back to ~8-16 hours.

#### Step 4: Export to Scala

Export weights as Scala code:

```bash
python3 export_weights.py nnue_weights.pt ../src/main/scala/de/nowchess/bot/bots/nnue/NNUEWeights.scala
```

Output: `NNUEWeights.scala`
- Object with `val` arrays for each layer's weights and biases
- Format: `Array[Float]` with precision sufficient for inference
- Includes shape comments for reference

## Scala Integration

### Step 5: NNUE Evaluator

Create `NNUE.scala` in `src/main/scala/de/nowchess/bot/bots/nnue/`:

```scala
package de.nowchess.bot.bots.nnue

class NNUE:
  // Load weights from NNUEWeights.scala
  // Convert Position to 768-feature vector
  // Run inference: l1→ReLU→l2→ReLU→l3
  // Return centipawn score
```

### Step 6: Integration

Implement `NNUEBot` that uses the NNUE evaluator for move selection.

## File Reference

| File | Purpose |
|------|---------|
| `requirements.txt` | Python dependencies |
| `generate_positions.py` | Step 1: Position generator |
| `label_positions.py` | Step 2: Stockfish labeler |
| `train_nnue.py` | Step 3: NNUE trainer |
| `export_weights.py` | Step 4: Weight exporter |
| `run_pipeline.sh` | Master script (runs steps 1-4) |
| `positions.txt` | Output: Raw FENs (500K) |
| `training_data.jsonl` | Output: FEN+eval pairs |
| `nnue_weights.pt` | Output: Trained weights |
| `../src/main/scala/.../NNUEWeights.scala` | Output: Scala weights |

## Tips

- **For testing:** Reduce `generate_positions.py` to 10,000 games for quick iteration
- **Resume labeling:** Run step 2 again; it skips already-evaluated positions
- **GPU acceleration:** Install CUDA for PyTorch to speed up training
- **Stockfish tuning:** Lower depth (e.g., 8 instead of 12) for faster labeling
- **Batch size:** Increase to 8192 if OOM; decrease if out of memory

## Troubleshooting

**ImportError: No module named 'chess'**
- Run: `pip install -r requirements.txt`

**Stockfish not found**
- Check: `which stockfish` or set `export STOCKFISH_PATH=/full/path/to/stockfish`

**CUDA out of memory**
- Reduce batch size in `train_nnue.py` (e.g., 2048)
- Or use CPU: Remove CUDA check and device setup

**Training loss not decreasing**
- Check data quality: Sample some entries from `training_data.jsonl`
- Increase learning rate to 1e-2 or 5e-4 for experimentation
- Verify Stockfish depth was sufficient (depth ≥ 10)

## References

- [NNUE Overview](https://www.chessprogramming.org/NNUE)
- [python-chess](https://python-chess.readthedocs.io/)
- [PyTorch](https://pytorch.org/)
- [Stockfish](https://stockfishchess.org/)
