Reescrevendo um RTS de 2001 com agentes que testam o próprio trabalho
Empire Earth saiu em 2001. Eu queria o jogo no macOS, com dezenas de milhares de unidades e uma IA que revida. Ninguém ia lançar isso, então comecei uma reescrita em Rust.
A maior parte do código foi escrita por agentes. Meu trabalho foi projetar o sistema em que eles operam: o que podem ver, o que podem tocar, como provam que estão certos e como melhoram nisso. Este post é sobre esse sistema, porque o formato dele não tem nada a ver com jogos.
O loop
Tudo que os agentes fazem é um único loop. A ordem muda dependendo da pergunta, mas cada peça está sempre lá.
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ coletar │──▶│ analisar │──▶│construir │──▶│ testar │
│ original │ │evidência │ │ remake │ │ no lab │
└──────────┘ └──────────┘ └──────────┘ └────┬─────┘
▲ │
│ ▼
┌─────┴────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ melhorar │◀──│registrar │◀──│ corrigir │◀──│ comparar │
│ ferram. │ │ memória │ │ │ │ original │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
Coletar o que o original faz. Analisar até haver evidência. Construir o equivalente no remake. Testar em um lab onde o resultado é um número. Rodar o mesmo cenário no original e comparar. Corrigir o que difere. Registrar o que foi aprendido, incluindo o que se mostrou errado. Depois melhorar a ferramenta ou a regra que teria pegado isso antes, e recomeçar.
O agente não é uma etapa desse loop. O agente roda o loop.
As camadas
┌──────────────────────────────┐
│eu: metas, revisão, prioridade│
└──────────────┬───────────────┘
│
┌──────────────▼───────────────┐
│ agentes de código e pesquisa │
└───────┬──────────────┬───────┘
│ │
┌─────────────▼────┐ ┌─────▼──────────────┐
│ memória │ │ ferramentas │
│ │ │ │
│arquivo/descoberta│ │ 96 pipelines python│
│ busca por tópico │ │ disassembly, RTTI │
│ páginas canônicas│ │ parsers de assets │
│ flags de superado│ │ CLI do jogo (win) │
└─────────────┬────┘ └─────┬──────────────┘
│ │
┌───────▼──────────────▼───────┐
│ avaliação │
│ │
│ lab do jogo original │
│ lab de cenários do remake │
│ lab de paridade de UI │
│ comparador de fidelidade │
└──────────────────────────────┘
Eu fico no topo e decido o que importa. Os agentes são donos do meio. Memória e ferramentas são o que faz a próxima sessão começar mais adiante do que a anterior. A avaliação é o que impede que "parece certo" seja uma resposta aceitável.
Coletar
Antes de escrever qualquer coisa nova, os agentes desmontaram o original. Seis pacotes proprietários, 4.940 arquivos. Todos os 730 modelos 3D parseados, 727 deles idênticos byte a byte no round trip, exportados para glTF para o novo renderer. Todos os 58 scripts de IA achatados em 3.115 transições de estado concretas. Cada tabela do banco de dados, cada cenário, 3.398 strings localizadas.
Nada disso foi feito à mão. Cada formato ganhou um parser, cada parser virou um pipeline com CLI e validador, e cada pipeline ficou no repositório para o próximo agente.
Analisar
Estático primeiro, runtime depois. O disassembly explica o porquê; o jogo rodando diz o quê. Os agentes trabalham no executável Windows de 32 bits a partir do macOS com LIEF e Capstone: 1.132 classes C++ catalogadas via RTTI, vtables nomeadas, endereços de função mapeados para classe e slot, e toda a camada de mensagens do lockstep recuperada: 103 tipos de comando com seus layouts de wire.
O melhor exemplo do método é um erro. Uma passada inicial concluiu que a tabela de tuning da IA era metadado do editor, porque uma busca por referências à tabela encontrou seis. Uma passada posterior notou que o compilador tinha dobrado cada acesso em um endereço absoluto, varreu o intervalo inteiro, encontrou mais de 200 e provou em runtime que a tabela é o painel de controle da IA. A descoberta errada não foi apagada. Foi marcada como superada, e a regra "varra o intervalo, não a base" entrou no processo.
Construir
O remake é Rust sobre Bevy ECS, três crates: dados, simulação, IA. Os agentes escrevem com base nas páginas canônicas, não na memória que têm do jogo. Quando falta um fato, a construção para e o loop volta para coletar.
Testar no lab
O remake tem um lab de cenários headless. Um agente escreve um cenário em JSON: unidades, seed, telemetria a registrar, quando parar, o que verificar. Ele roda a simulação real pelo caminho normal de comandos e recebe linhas JSON de volta.
scenario.json ──▶ ee_sim (headless) ──▶ events.jsonl
seed: 42 damage
units: 30 v 30 moved
stop_when: t > 120s trained
assert: vermelho vence tick_ms
Sem pixels, sem mouse, sem me pedir para jogar. Combate, pathfinding, economia, formações, comportamento da IA e performance são todas perguntas respondidas em segundos. Qualquer coisa que um dia achou um bug fica como regressão. Um fuzzer gera streams de comandos aleatórios e checa invariantes como conservação de recursos e "nenhuma unidade fica presa em um estado que não faz progresso".
A interface tem o mesmo tratamento. O HUD do original é dado, não imagem, então um lint compara a árvore de UI do remake com as tabelas do original, clica em controles reais e faz diff de screenshots por controle.
Comparar com o original
Há uma máquina Windows na rede com o jogo instalado. Do Mac, os agentes conseguem abrir o jogo, tirar screenshot, ler a memória dele e, como a camada de mensagens foi decodificada, controlá-lo por código.
mac (agente) windows (jogo original)
───────────── ───────────────────────
ee-inject select 0x01000001 ──▶ mesma mensagem que a UI envia
ee-inject move 388 196 ──▶ fila lockstep ──▶ engine
ee-scan selected ◀── lê a memória: posição, estado
ee-call FindUnitById ◀── pergunta direto à engine
Vinte comandos nativos estão provados: selecionar, mover, coletar, atacar, construir, treinar, pesquisar, patrulhar, formações, velocidade do jogo. O mesmo cenário JSON roda nas duas engines e um comparador normaliza tick rate, origem e escala do mapa antes de comparar movimento, combate e economia.
Foi assim que o remake conseguiu seus números. Madeira rende 15 unidades por viagem a 0,209 por segundo. Um cidadão acerta um hipopótamo por 3,75 a cada 1,955 segundos. Prédios sobem do chão durante a construção, não escalam. Um zero na matriz de dano significa imune, não um de dano. Muralhas são um corte no caminho até a capital inimiga, não uma cerca.
Corrigir
Uma correção só é correção se o número se move. Em uma caçada a bugs, onze correções saíram com medições de antes e depois. Uma correção proposta foi medida, piorou as coisas e foi rejeitada no mesmo documento. Esse documento é a evidência para o próximo agente que se perguntar por que a correção óbvia não está lá.
Registrar
Cada descoberta é um arquivo com tópicos, evidência, status e links. Uma ferramenta de consulta carrega só o tópico em questão e sinaliza entradas que depois foram refutadas, superadas ou revertidas. As páginas canônicas guardam o que é verdade hoje; o log guarda como chegamos lá.
pergunta ──▶ findings_query --topic combat
│
├── F-0212 atual ← citar
├── F-0198 superada ← não citar, veja por quê
└── canon/combat.md ← a verdade hoje
Isso substituiu um único relatório que tinha crescido até 375k tokens, que ninguém, humano ou agente, conseguia ler. As contradições vinham se acumulando porque a regra dizia "leia tudo" e nada conseguia.
Melhorar
Duas regras fazem o loop compor. Qualquer tarefa feita duas vezes vira uma ferramenta. Qualquer erro que custou tempo vira uma linha nas regras de operação, ao lado da checagem que o pega.
O arquivo do projeto tem uma lista dessas: uma tela modal congela o jogo enquanto o contador de ticks continua subindo, então exija que a flag de modal seja falsa antes de concluir que um comando falhou. A máquina Windows compartilhada devolve screenshots contaminados em silêncio, então verifique a imagem, não o nome dela. Um teste que passa sob um tuning que o jogo real nunca usa não prova nada, então sempre declare qual tuning está em uso.
Cada uma custou um dia uma vez. Nenhuma custou duas.
Por que isso não é sobre jogos
Troque os substantivos e o loop é um processo de negócio.
jogo qualquer empresa
───────────────────── ─────────────────────────────
executável original o sistema legado, ou a forma
como o trabalho é feito hoje
descobertas + cânone o que é de fato verdade sobre ele
parsers e pipelines ferramentas que seus agentes criam
lab de cenários um teste que devolve um número
comparação com o original o mesmo teste contra a realidade
descobertas superadas erros mantidos, não escondidos
regras no CLAUDE.md o processo melhorando a si mesmo
A parte difícil nunca foi o modelo. Foi dar aos agentes uma memória que não mente, ferramentas que eles podem estender e um jeito de checar o próprio trabalho contra a realidade antes de eu olhar. São as mesmas três coisas para um agente de QA, um agente de contratação ou um pipeline de conteúdo.
O que eu fiz
Eu não escrevi o pathfinder nem o código de posicionamento da IA. Eu projetei o loop, construí a primeira versão de cada ferramenta, escrevi as regras, revisei o que saiu e medi. Depois garanti que cada lição terminasse em uma ferramenta ou em uma regra, não na minha cabeça.
Onde está agora: 96 pipelines reutilizáveis em 26 suítes, 275+ descobertas, 20 comandos nativos no original, 27+ cenários de regressão preservados e um remake em que a IA constrói uma base, cerca com muralhas e avança uma época sozinha.