# Agent-Selbstiteration

> Lass einen Coding Agent selbst Partien spielen, Ergebnisse lesen, Code ändern und erneut spielen. Dafür braucht er einen Befehl, der unbeaufsichtigt läuft, einen strukturierten Spielbericht und ein klares Ziel.

Quelle: https://war3ai.com/de/docs/agent-loop/

In [Einen Bot mit einem LLM schreiben](https://war3ai.com/de/docs/ai-bot/) übernimmst du den Schritt „Partie spielen → beobachten → dem Modell berichten“ selbst. Ein Coding Agent, der Befehle ausführen kann (Claude Code, Codex, der Agent-Modus von Cursor usw.), kann auch diesen Schritt übernehmen und so den Kreis schließen:

```text
  Code ändern ──► Partie spielen (unbeaufsichtigt) ──► Spielbericht lesen ──► wichtigstes Problem finden ──┐
    ▲                                                                                                      │
    └──────────────────────────────────────────────────────────────────────────────────────────────────────┘
```

Damit diese Schleife wirklich konvergiert, braucht der Agent drei Dinge.

## 1. Ein Befehl, der unbeaufsichtigt läuft

```bash
python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair
```

- `--minutes` sorgt dafür, dass die Partie garantiert endet (Minuten Echtzeit), damit der Agent nicht in einer Partie hängen bleibt;
- `--speed 200` spart mit doppelter Geschwindigkeit Zeit – aber im Bot **nach der Spieluhr warten** (`g.clock()`), nicht per `sleep` nach Echtzeit;
- `--fair` sorgt dafür, dass er vom ersten Tag an nach Arena-Regeln schreibt und nur sieht, was in Sichtweite ist;
- Am Ende gibt das Terminal den Grund für das Ende aus, z. B. `我方没有单位了` (keine eigenen Einheiten mehr) oder `到时间了` (Zeit abgelaufen); was der Bot selbst per `print` ausgibt, steht ebenfalls im Terminal.

> **Achtung**
>
> Ist das Fenster minimiert, steht die Spielsimulation still. Lass den Agent das Spiel im Standard-Fenstermodus starten und achte darauf, dass seine Instanznummer (`--inst`) nicht mit der Instanz kollidiert, die du gerade benutzt.

## 2. Ein strukturierter Spielbericht

Die Terminalausgabe ist für Menschen gedacht. Für den Agent sollte es ein JSON sein: was passiert ist, was nicht geklappt hat und warum. Das SDK liefert dir alle Rohdaten – Quittungen tragen Grundcodes, der Event-Stream liefert fertige Produktionen und Verluste. Du musst sie nur einsammeln:

```python title="recorder.py"
import collections, json, time
from openwar3 import Bot

class Recorder(Bot):
    """Ergänzt einen Bot um einen Spielbericht. Davon erben und in eigenem on_start / on_event super() aufrufen."""

    def on_start(self, g):
        self.rejects = collections.Counter()   # "train hfoo: rejected（人口不够）" -> Anzahl
        self.timeline = []                     # [Spielsekunde, Kategorie, Vier-Zeichen-Code]: Training / Forschung / Bau / Aufwertung fertig
        self.lost = collections.Counter()      # was bei uns gestorben ist
        self.killed = collections.Counter()    # was wir getötet haben

    def check(self, r, what):
        """Umhüllt einen Befehl und zählt Ablehnungsgründe: self.check(g.train(b, "hfoo"), "train hfoo")"""
        if r is not None and not r:
            self.rejects[f"{what}: {r.reason}"] += 1
        return r

    def on_event(self, g, ev):
        me = g.me()
        if ev.kind == "production.done" and ev.owner == me:
            self.timeline.append([round(ev.clock), ev.done_kind, ev.done_code])
        elif ev.kind == "unit.died":
            (self.lost if ev.owner == me else self.killed)[ev.type] += 1

    def on_end(self, g, reason):
        report = {"reason": reason, "timeline": self.timeline, "lost": self.lost,
                  "killed": self.killed, "rejects": self.rejects.most_common(10)}
        try:                                   # das Spiel ist evtl. schon beendet; nicht lesbar -> egal
            report |= {"clock": g.clock(), "resources": g.resources(),
                       "army": len(g.my_army()), "workers": len(g.my_workers())}
        except Exception:
            pass
        with open(f"run_{int(time.time())}.json", "w", encoding="utf-8") as f:
            json.dump(report, f, ensure_ascii=False, indent=1)
```

Fragen, die dieser Bericht beantwortet:

| Signal | Quelle | Was man daran erkennt |
|---|---|---|
| Häufigste Ablehnungsgründe | Quittung `reason` / `verdict` | Nahrung blockiert ständig (3), Befehle trotz Geldmangel (8 / 9), Angriffe auf Ziele im Kriegsnebel (1001), Helden werden trotz Tod neu trainiert (221) |
| Produktions-Timeline | `production.done`-Events (mit benötigten Spielsekunden) | In welcher Sekunde der erste Held kam, wann der Tier-up fertig war, ob die Kaserne durchgehend produziert hat; lässt sich mit den Eröffnungen von Profis vergleichen |
| Verluste beider Seiten | `unit.died`-Events | Ob ständig Einheiten verschenkt werden, wie oft der Held gestorben ist, ob sich das Creepen gelohnt hat |
| Grund für das Ende | `on_end(g, reason)` | `我方没有单位了` (keine eigenen Einheiten mehr) = verloren; `到时间了` (Zeit abgelaufen) = noch kein Sieger |
| Armee und Ressourcen am Ende | Einmal den Snapshot in `on_end` lesen | Gold gespart statt ausgegeben = Produktion kommt nicht hinterher; zu wenige Arbeiter = Wirtschaft kommt nicht in Gang |

> **Info**
>
> Den Sieger programmatisch zu bestimmen ist eines der Grundlagenexperimente für die [Arena](https://war3ai.com/de/arena/) und steht noch auf der Roadmap. Bis dahin kannst du „keine eigenen Einheiten mehr“ als Niederlage werten und „alle sichtbaren gegnerischen Gebäude zerstört“ näherungsweise als Sieg.

## 3. Ein klares Ziel und ein paar Regeln

Gib dem Agent den folgenden Text und passe ihn an dein Ziel an:

```text
Ziel: brains/my_bot.py soll auf Echo Isles zuverlässig gegen den Computer auf Schwierigkeit "Leicht" gewinnen (Menschen gegen zufälliges Volk).

In jeder Runde:
1. python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair ausführen
2. Terminalausgabe und die neueste run_*.json lesen: Grund für das Ende, Produktions-Timeline, häufigste Ablehnungsgründe, Verluste beider Seiten
3. Das EINE Problem finden, das das Ergebnis am stärksten beeinflusst, und nur diese Stelle ändern; im Code-Kommentar Grund der Änderung und die zugrunde liegenden Daten festhalten
4. Zurück zu Schritt 1. Gibt es in 3 Partien hintereinander keinen Fortschritt, anhalten und mir Bericht und deine Einschätzung geben

Regeln:
- Nur Methoden aus docs/api.json verwenden, keine Schnittstellen erfinden
- Derselben Einheit nicht jeden Tick denselben Befehl erneut geben; nur untätigen Einheiten Befehle erteilen
- --fair beibehalten (nur Gegner verwenden, die in Sichtweite sind)
- Vor jeder Codeänderung python tools/run_tests.py ausführen und sicherstellen, dass die Beispiele nicht kaputt sind
```

## Gewohnheiten, mit denen die Schleife schneller konvergiert

- **Immer nur eine Stelle ändern.** Änderst du drei Stellen gleichzeitig, weißt du bei einem Sieg nicht, welche geholfen hat, und bei einer Niederlage nicht, welche geschadet hat.
- **Genug Partien vergleichen.** Dieselbe Ausgangslage streut stark; zwei Partien zeigen nur große Unterschiede. Um zu beurteilen, ob es Fortschritt gibt, schau dir den Trend über mehrere Partien an.
- **Erst Ablehnungen beheben, dann die Strategie tunen.** Der häufigste Ablehnungsgrund in den Quittungen ist oft der größte Bug des Bots.
- **Einschätzungen in Kommentare schreiben.** Der Agent in der nächsten Runde (oder im nächsten Gespräch) sieht dann in den Kommentaren, warum etwas so geschrieben ist, und macht Korrekturen nicht wieder rückgängig.
- **Offline-Tests als Absicherung.** Schreib für die zentrale Logik Unit-Tests, die ohne laufendes Spiel auskommen (die Tests der Beispiel-Bots liegen in `brains/examples/tests/`), und lass den Agent sie nach jeder Änderung zuerst ausführen.
