# Iteración autónoma del Agent

> Deja que un Coding Agent juegue partidas, lea los resultados, cambie el código y vuelva a jugar por su cuenta. Necesita un comando que funcione sin supervisión, un informe de partida estructurado y un objetivo claro.

Fuente: https://war3ai.com/es/docs/agent-loop/

En [Escribe un Bot con un LLM](https://war3ai.com/es/docs/ai-bot/), el paso «jugar una partida → ver qué pasa → contárselo al modelo» lo haces tú. Un Coding Agent capaz de ejecutar comandos (Claude Code, Codex, el modo Agent de Cursor, etc.) puede encargarse también de ese paso y cerrar el ciclo:

```text
  cambiar código ──► jugar una partida (sin supervisión) ──► leer el informe ──► hallar lo que más pesa ──┐
    ▲                                                                                                     │
    └─────────────────────────────────────────────────────────────────────────────────────────────────────┘
```

Para que ese ciclo converja de verdad, el Agent necesita tres cosas.

## 1. Un comando que funcione sin supervisión

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

- `--minutes` garantiza que la partida termine (minutos de reloj real), así el Agent no se queda atascado en una partida;
- `--speed 200` usa velocidad 2× para ahorrar tiempo, pero dentro del Bot **espera según el reloj del juego** (`g.clock()`), no con `sleep` según el reloj real;
- `--fair` hace que escriba desde el primer día con las reglas de la Arena: solo ve lo que está en su campo de visión;
- Al terminar, la terminal imprime el motivo del final, por ejemplo `我方没有单位了` (no nos quedan unidades) o `到时间了` (se acabó el tiempo); lo que el Bot imprima con `print` también aparece en la terminal.

> **Atención**
>
> Con la ventana minimizada, la simulación del juego se detiene. Haz que el Agent arranque el juego en el modo de ventana por defecto y que no use el mismo número de instancia que la que estás usando tú (`--inst`).

## 2. Un informe de partida estructurado

La salida de la terminal es para personas. Lo que debe leer el Agent es un JSON: qué pasó, qué no se consiguió y por qué. El SDK ya te da toda la materia prima: los recibos traen códigos de motivo y el flujo de eventos trae producciones terminadas y bajas. Basta con recopilarlos:

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

class Recorder(Bot):
    """Añade un informe de partida a un Bot. Hereda de esta clase y llama a super() desde tus propios on_start / on_event."""

    def on_start(self, g):
        self.rejects = collections.Counter()   # "train hfoo: rejected（人口不够）" -> número de veces
        self.timeline = []                     # [segundo de juego, categoría, código de cuatro caracteres]: entrenamiento / investigación / construcción / mejora terminados
        self.lost = collections.Counter()      # qué hemos perdido
        self.killed = collections.Counter()    # qué hemos matado

    def check(self, r, what):
        """Envuelve un comando para registrar el motivo del rechazo: 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:                                   # puede que el juego ya se haya cerrado; si no se puede leer, da igual
            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)
```

Preguntas que puede responder este informe:

| Señal | De dónde sale | Qué revela |
|---|---|---|
| Motivos de rechazo más frecuentes | `reason` / `verdict` del recibo | La comida siempre atascada (3), órdenes una y otra vez sin dinero suficiente (8 / 9), ataques a objetivos en la niebla (1001), entrenar un héroe que ya murió (221) |
| Línea de tiempo de producción | Eventos `production.done` (con los segundos de juego que tardó) | En qué segundo sale el primer héroe, en qué segundo sube de tier, si el cuartel produce sin parar; se puede comparar con la apertura de un jugador profesional |
| Bajas de ambos bandos | Eventos `unit.died` | Si está regalando unidades todo el rato, cuántas veces murió el héroe, si el creeping salió a cuenta |
| Motivo del final | `on_end(g, reason)` | `我方没有单位了` = derrota; `到时间了` = todavía sin ganador |
| Ejército y recursos finales | Una lectura de la instantánea en `on_end` | Dinero acumulado sin gastar = la producción no da abasto; pocos trabajadores = la economía no despegó |

> **Nota**
>
> Determinar la victoria por programa es uno de los experimentos de base de la [Arena](https://war3ai.com/es/arena/) y todavía está en la hoja de ruta. Por ahora puedes considerar derrota `我方没有单位了` (no nos quedan unidades) y aproximar la victoria con «todos los edificios enemigos visibles destruidos».

## 3. Un objetivo claro y algunas restricciones

Pásale esto al Agent, adaptado a tu objetivo:

```text
Objetivo: que brains/my_bot.py gane de forma estable en Echo Isles al ordenador en dificultad "Fácil" (Humanos contra raza aleatoria).

En cada ronda:
1. Ejecuta python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair
2. Lee la salida de la terminal y el run_*.json más reciente: motivo del final, línea de tiempo de producción, motivos de rechazo más frecuentes, bajas de ambos bandos
3. Encuentra "un solo" problema, el que más afecte al resultado, y cambia solo eso; escribe en un comentario del código el motivo del cambio y los datos en que te basas
4. Vuelve al paso 1. Si no hay mejora en 3 partidas seguidas, detente y cuéntame el informe y tu valoración

Restricciones:
- Usa solo los métodos de docs/api.json; no inventes interfaces
- No repitas la misma orden a la misma unidad en cada tick; da órdenes solo a las unidades ociosas
- Mantén --fair (usa solo los enemigos que se ven en el campo de visión)
- Antes de cambiar el código, ejecuta python tools/run_tests.py para confirmar que no has roto los ejemplos
```

## Hábitos para que el ciclo converja más rápido

- **Cambia una sola cosa cada vez.** Si cambias tres a la vez y ganas, no sabes cuál funcionó; si pierdes, tampoco sabes cuál lo rompió.
- **Juega suficientes partidas para comparar.** La misma situación tiene mucha aleatoriedad; con dos partidas solo se detectan diferencias muy grandes. Para decidir si «hay mejora», mira la tendencia de varias partidas.
- **Arregla primero los «rechazos» y luego ajusta la estrategia.** El motivo de rechazo más frecuente en los recibos suele ser el mayor bug del Bot.
- **Escribe tu razonamiento en los comentarios.** El Agent de la siguiente ronda (o la siguiente conversación) sabrá por los comentarios por qué el código es así y no deshará lo que ya estaba arreglado.
- **Red de seguridad con tests offline.** Escribe tests unitarios que no necesiten abrir el juego para la lógica clave (los tests de los Bots de ejemplo están en `brains/examples/tests/`), y haz que el Agent los ejecute después de cada cambio.
