IAs de pessoas diferentes, medindo forças no mesmo mapa
Jogo limpo só pode ser garantido por um árbitro, não pela boa vontade do Bot. O Bot nunca toca na memória compartilhada — só recebe observações que o árbitro filtrou pela visão, só pode enviar ações, e cada ação tem a propriedade verificada antes.
Ordenados por “dá para garantir o jogo limpo?”
A capacidade de observação deste projeto vem justamente de “o cliente guarda o estado de todos os jogadores” — e isso vale também para a sua máquina. Por isso, o jogo limpo só pode vir de um árbitro em quem todos confiam. Primeiro fazemos o A; o mesmo protocolo evolui direto para o B; o C é só diversão.
Arena local
Uma máquina, uma partida; cada Bot ocupa um slot, e o processo árbitro dá as ordens por eles
Ladder hospedada
O A vai para um servidor; jogadores enviam seus Bots e o servidor os roda em sandbox
Cada um na sua máquina
Cada pessoa roda jogo + Bot na própria máquina, com partida em rede local
Um processo dá as ordens pelos dois lados
- Orquestração Escolhe o mapa, inicia a partida, define raça e dificuldade por slot
- Ticks Um tick a cada T milissegundos de jogo (padrão 200); no modo lockstep, o jogo pausa enquanto as observações são enviadas
- Observação Snapshot → filtrado pela visão de cada slot → JSON
- Ações Verifica “a unidade pertence a este slot” → orçamento (comandos por tick, APM) → envio em lote pela via rápida
- Registro Resumo da observação de cada tick + cada ação — dá para reproduzir, revisar e usar como dados de treino
- Resultado Um slot sem construções → eliminado; tempo esgotado → decide-se pelo poder de combate / recursos restantes
Observações entram, ações saem
Qualquer programa que envie e receba JSON pode entrar em campo — inclusive um LLM gerando ações tick a tick. Bots em Python escritos com o SDK não precisam de alterações.
| Observação | Só unidades próprias + unidades inimigas na visão; creeps e minas de ouro pela visão ou públicos desde o início |
| Limite de ações | No máximo 32 por tick; o excedente é cortado (para evitar que o spam de comandos atrase o árbitro) |
| Mesma unidade | No mesmo tick, só vale a última |
| Orçamento de tempo | Duração do tick × 0.9; se estourar, o tick passa em branco — o lado lento sai perdendo, sem atrasar o outro |
| Lockstep | Quando é preciso jogo limpo estrito, o jogo pausa ao enviar as observações e só continua depois de receber todas as respostas (ou do timeout) — a velocidade das máquinas não afeta o resultado |
| Troca de posições iniciais | O mesmo par de Bots joga uma vez de cada lado, eliminando a assimetria do mapa |
| Ladder | Elo / TrueSkill; pelo menos 20 partidas por par antes de concluir algo (com 2 partidas, a menor diferença de taxa de vitória detectável é de ~60 pontos percentuais) |
{"t": "obs", "tick": 57, "gameMs": 11400, "me": 1,
"res": {"gold": 320, "lumber": 150, "food": [18, 30]},
"units": [
{"id": 101, "type": "hfoo", "owner": 1,
"x": -4500, "y": 2200, "hp": 380, "hpMax": 420}],
"visibleEnemies": [
{"id": 733, "type": "ogru", "owner": 2,
"x": -3900, "y": 2500, "hp": 700}],
"deadlineMs": 180} id é um identificador estável atribuído pelo árbitro, que não muda durante a partida. {"t": "act", "tick": 57, "actions": [
{"do": "attack", "unit": 101, "target": 733},
{"do": "train", "unit": 5, "code": "hfoo"},
{"do": "cast", "unit": 7, "spell": "thunderbolt", "target": 733},
{"do": "move", "unit": 102, "x": -5000, "y": 2000}]} category == "command" no api.json, com parâmetros de mesmo nome. Um act atrasado é descartado, e o tick conta como passado em branco. from openwar3 import Bot
class MyBot(Bot):
def on_tick(self, g): # mesmo código: localmente g é Game, na Arena g é ArenaGame
for w in g.idle_workers():
g.gather(w, g.nearest(g.gold_mines(), w))
# Local: python tools/play.py --bot my_bot.py --fair
# Arena: mesmos métodos, com WebSocket por baixo — o código do Bot não muda uma linha Game por ArenaGame. Em ordem — se um não passa, o próximo não começa
| # | Experimento | Critério | Já sabemos |
|---|---|---|---|
| 1 | Abrir 2 slots numa partida, sem IA embutida | As unidades dos dois lados não se mexem | A flag de IA e o campo de dificuldade dos slots são conhecidos |
| 2 | Dar ordens a slots que não são o local | Os camponeses do slot inimigo realmente vão minerar | O mais crítico — se falhar, só resta recuar para “um cliente por Bot, com conexão de rede real” |
| 3 | Consultar a visão para qualquer número de jogador | O mesmo ponto dá resultados diferentes para os dois slots | A verificação de visibilidade do motor aceita qualquer número de jogador |
| 4 | Decidir o resultado por programa | Saber em até 1 segundo quando um lado perdeu todas as construções | O barramento de eventos tem uma entrada mais direta |
| 5 | Pausar / continuar em lockstep | Receber todas as ações durante a pausa e aplicá-las ao continuar | A pausa já foi verificada |
Um teste de brinde: hoje, o cérebro de referência depende muito da informação do mapa inteiro (como o alvo de ataque do computador). Para ir à Arena, ele precisa migrar para a fachada e usar só as unidades na visão — sem a informação do mapa inteiro, quão forte ele continua?