# Iteração autônoma do agente

> Deixe um agente de programação jogar partidas, ler os resultados, mudar o código e jogar de novo sozinho. Ele precisa de um comando que rode sem supervisão, de um relatório de partida estruturado e de um objetivo claro.

Fonte: https://war3ai.com/pt/docs/agent-loop/

Em [Escreva um Bot com um LLM](https://war3ai.com/pt/docs/ai-bot/), o passo "jogar uma partida → observar → contar ao modelo" fica por sua conta. Um agente de programação que executa comandos (Claude Code, Codex, o modo agente do Cursor etc.) pode assumir esse passo também e fechar o ciclo:

```text
  muda o código ──► joga uma partida (sem supervisão) ──► lê o relatório ──► acha o ponto que mais pesa ──┐
    ▲                                                                                                     │
    └─────────────────────────────────────────────────────────────────────────────────────────────────────┘
```

Para esse ciclo convergir de verdade, o agente precisa de três coisas.

## 1. Um comando que rode sem supervisão

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

- `--minutes` garante que a partida termina (em minutos de relógio real), então o agente nunca fica preso numa partida;
- `--speed 200` usa velocidade 2× para ganhar tempo — mas dentro do Bot, **espere pelo relógio do jogo** (`g.clock()`), não com um `sleep` de relógio real;
- `--fair` faz o Bot seguir as regras da Arena desde o primeiro dia: ele só vê o que está no campo de visão;
- Quando a execução termina, o terminal imprime o motivo do fim, por exemplo `我方没有单位了` ("não temos mais unidades") ou `到时间了` ("acabou o tempo"); o que o próprio Bot imprime com `print` também aparece no terminal.

> **Atenção**
>
> A simulação do jogo para enquanto a janela está minimizada. Faça o agente iniciar o jogo no modo janela padrão e garanta que ele não use o mesmo número de instância que você está usando (`--inst`).

## 2. Um relatório de partida estruturado

A saída do terminal é feita para humanos. O que o agente deve ler é um JSON: o que aconteceu, o que não deu certo e por quê. O SDK já entrega toda a matéria-prima — os recibos trazem códigos de motivo, e o fluxo de eventos traz produções concluídas e baixas. Basta juntar tudo:

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

class Recorder(Bot):
    """Adiciona um relatório de partida a um Bot. Herde dele e chame super() nos seus próprios on_start / on_event."""

    def on_start(self, g):
        self.rejects = collections.Counter()   # "train hfoo: rejected（人口不够）" -> contagem  (= falta comida)
        self.timeline = []                     # [segundos de jogo, categoria, código de quatro caracteres]: treino / pesquisa / construção / melhoria concluídos
        self.lost = collections.Counter()      # o que nós perdemos
        self.killed = collections.Counter()    # o que nós matamos

    def check(self, r, what):
        """Envolve um comando para registrar motivos de rejeição: 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:                                   # o jogo pode já ter fechado; se não der para ler, deixa para lá
            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)
```

Perguntas que esse relatório responde:

| Sinal | De onde vem | O que revela |
|---|---|---|
| Motivos de rejeição mais frequentes | `reason` / `verdict` do recibo | Travado por comida o tempo todo (3), dando ordens sem ter dinheiro (8 / 9), atacando alvos na névoa de guerra (1001), treinando um herói que já morreu (221) |
| Linha do tempo de produção | Eventos `production.done` (com os segundos de jogo gastos) | Em que segundo saiu o primeiro herói, em que segundo você subiu de tier, se o Quartel ficou produzindo sem parar; compare com as aberturas de jogadores profissionais |
| Baixas dos dois lados | Eventos `unit.died` | Se você está entregando unidades o tempo todo, quantas vezes o herói morreu, se o creeping compensou |
| Motivo do fim | `on_end(g, reason)` | `我方没有单位了` ("não temos mais unidades") = derrota; `到时间了` ("acabou o tempo") = ainda sem vencedor |
| Exército e recursos finais | Um snapshot lido no `on_end` | Ouro acumulado sem gastar = a produção não acompanha; poucos trabalhadores = a economia não decolou |

> **Nota**
>
> Detectar vitória e derrota por programa é um dos experimentos de fundação da [Arena](https://war3ai.com/pt/arena/) e ainda está no roadmap. Por enquanto, você pode tratar `我方没有单位了` ("não temos mais unidades") como derrota e aproximar a vitória como "todas as construções inimigas visíveis foram destruídas".

## 3. Um objetivo claro e algumas restrições

Entregue o texto abaixo ao agente, ajustado ao seu objetivo:

```text
Objetivo: fazer brains/my_bot.py vencer com consistência o computador no nível "Fácil" em Echo Isles (Humanos contra raça aleatória).

A cada rodada:
1. Rode python tools/play.py --bot brains/my_bot.py --speed 200 --minutes 10 --fair
2. Leia a saída do terminal e o run_*.json mais recente: motivo do fim, linha do tempo de produção, motivos de rejeição mais frequentes, baixas dos dois lados
3. Encontre O problema que mais afeta o resultado e mude só esse ponto; escreva num comentário do código o motivo da mudança e os dados em que ela se baseia
4. Volte ao passo 1. Se não houver melhora por 3 partidas seguidas, pare e me mostre o relatório e a sua avaliação

Restrições:
- Use apenas métodos de docs/api.json; não invente APIs
- Não repita o mesmo comando para a mesma unidade a cada tick; só dê ordens a unidades ociosas
- Mantenha --fair (use só inimigos visíveis no campo de visão)
- Antes de mudar o código, rode python tools/run_tests.py para confirmar que os exemplos não quebraram
```

## Hábitos que fazem o ciclo convergir mais rápido

- **Mude uma coisa por vez.** Se você muda três coisas ao mesmo tempo e ganha, não sabe qual ajudou; se perde, não sabe qual estragou.
- **Jogue partidas suficientes para comparar.** O mesmo confronto tem muita aleatoriedade; duas partidas só revelam diferenças muito grandes. Avalie "se melhorou" pela tendência de, no mínimo, várias partidas.
- **Corrija as rejeições antes de ajustar a estratégia.** O motivo de rejeição mais frequente nos recibos costuma ser o maior bug do Bot.
- **Escreva seu raciocínio nos comentários.** O agente da próxima rodada (ou a próxima conversa) consegue ler nos comentários por que o código está assim e não desfaz as correções.
- **Testes offline como rede de segurança.** Escreva testes unitários para a lógica principal que não precisem do jogo aberto (os testes dos Bots de exemplo ficam em `brains/examples/tests/`) e faça o agente rodá-los depois de cada mudança.
