Metadata-Version: 2.4
Name: lootqdm
Version: 0.1.0
Summary: Turn your training loop into an idle RPG.
Project-URL: Homepage, https://github.com/lootqdm/lootqdm
Project-URL: Repository, https://github.com/lootqdm/lootqdm
Author: lootqdm contributors
License-Expression: MIT
License-File: LICENSE
Keywords: deep-learning,game,idle,ml,progress-bar,rich,tqdm,training
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: System :: Logging
Requires-Python: >=3.10
Requires-Dist: rich>=13.0
Provides-Extra: dev
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Provides-Extra: keras
Requires-Dist: tensorflow; extra == 'keras'
Provides-Extra: torch
Requires-Dist: torch; extra == 'torch'
Description-Content-Type: text/markdown

# lootqdm

> Turn your training loop into an idle RPG.

[![PyPI](https://img.shields.io/pypi/v/lootqdm)](https://pypi.org/project/lootqdm/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://pypi.org/project/lootqdm/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)

**lootqdm** replaces bland training progress bars with an idle RPG experience.
XP bars, loot drops, quests, achievements, level-ups, critical batches — all in your terminal.

```
╭─ 🗡️ LOOTQDM ─────────────────────────────────────────────────────────╮
│  mnist_run · dark_dungeon                    ⚙        00:12:34       │
├───────────────────────────────────┬──────────────────────────────────┤
│                                   │                                  │
│  PROGRESS                         │  STATS                           │
│                                   │                                  │
│  Epoch  3/10  █████████░░░  80%   │  ⚔  DMG  (loss)       0.0234   │
│  Batch 128/500 ███████░░░░  56%   │  🎯 CRIT (acc)        94.2%    │
│  ETA 00:08:22  ·  1284 it/s       │  💧 MANA (lr)         1e-4     │
│                                   │  📉 loss  ▇▆▅▄▃▃▂▂▁▁           │
│  🩸 ██████████░░  2340/3000 LV 7  │  🔥 GPU   78%  · 4.2 GB       │
│                                   │                                  │
├───────────────────────────────────┼──────────────────────────────────┤
│                                   │                                  │
│  LOOT & EVENTS                    │  QUESTS                          │
│                                   │                                  │
│  ⚜️  LEGENDARY Cursed Crown! +500 │  📜 acc ≥ 0.95  ▰▰▰▰▱  94%    │
│  💠 Rare Void Shard  +50 XP       │  📜 loss < 0.01 ▰▰▱▱▱  42%    │
│  💥 CRITICAL BATCH!  +20 XP       │  ✅ First epoch  ✓              │
│  🪙 Rusty Coin  +5 XP             │  ✅ New best acc  ✓             │
│                                   │                                  │
╰───────────────────────────────────┴──────────────────────────────────╯
```

## Install

```bash
pip install lootqdm
```

## Quick Start

### Minimal

```python
from lootqdm import GameUI

with GameUI(total_steps=1000) as ui:
    for i in range(1000):
        loss = 0.9 / (i + 1)
        ui.step({"loss": loss, "lr": 1e-3})
```

### Wrap an iterable

```python
from lootqdm import GameUI

with GameUI(total_steps=len(dataloader)) as ui:
    for batch in ui.wrap(dataloader):
        loss = train_step(batch)
        ui.step({"loss": loss.item()})
```

### PyTorch

```python
from lootqdm import GameUI
from lootqdm.integrations.torch import LootqdmCallback

epochs = 10
with GameUI(
    total_steps=len(loader) * epochs,
    total_epochs=epochs,
    theme="neon_cyber",
    run_name="mnist",
) as ui:
    ui.quest("Low Loss", "loss", 0.1, "<=")
    cb = LootqdmCallback(ui)

    for epoch in range(epochs):
        for batch_x, batch_y in loader:
            loss, acc = train_step(batch_x, batch_y)
            cb.on_batch_end(loss=loss, acc=acc)
        cb.on_epoch_end(val_acc=validate())
```

### Keras

```python
from lootqdm import GameUI
from lootqdm.integrations.keras import LootqdmKerasCallback

with GameUI(
    total_steps=steps_per_epoch * epochs,
    total_epochs=epochs,
    theme="cozy_pastel",
) as ui:
    model.fit(x, y, callbacks=[LootqdmKerasCallback(ui)], verbose=0)
```

## Themes

Pick a vibe. Four built-in themes:

| Theme | Mood | Icons |
|-------|------|-------|
| `dark_dungeon` | Roguelike, gritty | 🩸 🗡️ 🪙 💠 ⚜️ |
| `cozy_pastel` | Cottagecore, soft | 🌱 🧺 🍓 🫐 🧁 |
| `neon_cyber` | Synthwave, flashy | ⚡ 🧬 📀 🧪 💎 |
| `retro_crt` | Old terminal, ASCII | `+XP` `LV` `[C]` `[R]` `[L]` |

```python
GameUI(total_steps=1000, theme="cozy_pastel")
```

## Quests

Set metric-based goals. Progress bars track them live:

```python
with GameUI(total_steps=1000) as ui:
    ui.quest("Low Loss", "loss", 0.05, "<=")
    ui.quest("High Accuracy", "acc", 0.95, ">=")
    # quests check on epoch_end()
```

## Loot System

Every 50 steps, a loot drop is rolled:

| Rarity | Chance | XP | Example (dark_dungeon) |
|--------|--------|----|------------------------|
| Common | 70% | +5 | 🪙 Rusty Coin |
| Uncommon | 20% | +15 | 🔧 Iron Buckle |
| Rare | 8% | +50 | 💠 Cursed Relic |
| Epic | 1.9% | +150 | 🔮 Bloodstone Amulet |
| Legendary | 0.1% | +500 | ⚜️ Cursed Crown |

## Random Events

- **💥 Critical Batch** (3% per step) — bonus XP burst
- **🦇 Lucky Batch** (1% per step) — mystery bonus

## Achievements

Unlocked automatically:
- First epoch complete
- New best metric (per metric key)
- NaN survived (with warning)
- Level milestones (5, 10, 25, 50, 100)

## Stat Mapping

Metrics are auto-translated to game stats:

| ML Metric | Game Stat | Icon |
|-----------|-----------|------|
| `loss` | DMG | ⚔ |
| `acc` / `accuracy` | CRIT | 🎯 |
| `lr` | MANA | 💧 |
| `val_loss` | vDMG | 🛡 |
| `val_acc` | vCRIT | 🎯 |

Custom mappings:

```python
from lootqdm import StatMapper

mapper = StatMapper(mappings={
    "f1_score": ("F1 PWR", "⚔"),
    "bleu": ("BLEU", "📝"),
})
```

## API Reference

### `GameUI`

```python
GameUI(
    total_steps=1000,       # Total training steps
    total_epochs=10,        # Number of epochs
    theme="dark_dungeon",   # Theme: dark_dungeon, cozy_pastel, neon_cyber, retro_crt
    refresh_rate=10,        # Max FPS for terminal refresh
    emoji=True,             # Enable emoji icons
    persist=True,           # Save profile to ~/.lootqdm/
    run_name="my_run",      # Display name for the run
)
```

| Method | Description |
|--------|-------------|
| `step(metrics, n=1)` | Advance by `n` steps with optional metrics dict |
| `epoch_end(metrics)` | Signal end of epoch; triggers quest checks |
| `log(message, rarity)` | Add message to the loot feed |
| `quest(name, metric, target, compare)` | Register a quest goal |
| `wrap(iterable, metrics_fn)` | Wrap iterable with auto step tracking |
| `pytorch_step(**metrics)` | Convenience for `step(metrics)` via kwargs |

### Persistence

Profile persists across runs at `~/.lootqdm/profile.json`:
- Lifetime XP, total runs, level, achievements, loot log
- Set `persist=False` to disable

### TTY Fallback

When not in a terminal (CI, pipes, `LOOTQDM_PLAIN=1`), lootqdm automatically falls back to plain `logging` output. No crashes, no garbled text.

## Examples

Run the included examples:

```bash
# Simple loop with all 4 themes
python examples/basic_loop.py

# PyTorch (falls back to simulation if torch not installed)
python examples/pytorch_mnist.py

# Keras (falls back to simulation if tensorflow not installed)
python examples/keras_callback.py
```

## Development

```bash
git clone https://github.com/lootqdm/lootqdm.git
cd lootqdm
pip install -e ".[dev]"
pytest
```

## License

MIT
