Docs La IA escribe tu Bot

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.

En Escribe un Bot con un LLM, 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:

  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

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.

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:

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ñalDe dónde saleQué revela
Motivos de rechazo más frecuentesreason / verdict del reciboLa 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ónEventos 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 bandosEventos unit.diedSi está regalando unidades todo el rato, cuántas veces murió el héroe, si el creeping salió a cuenta
Motivo del finalon_end(g, reason)我方没有单位了 = derrota; 到时间了 = todavía sin ganador
Ejército y recursos finalesUna lectura de la instantánea en on_endDinero acumulado sin gastar = la producción no da abasto; pocos trabajadores = la economía no despegó

Determinar la victoria por programa es uno de los experimentos de base de la 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:

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.