# Modelo mental

> Snapshots, comandos, recibos, eventos, ticks e lotes. Entenda esses seis conceitos e você vai entender por que a API tem esse formato e como escrever código rápido.

Fonte: https://war3ai.com/pt/docs/concepts/

## Snapshots: leitura sem espera

A cada **50 ms**, o runtime coleta o mundo inteiro na thread do jogo e grava tudo em memória compartilhada. O que `g.snapshot()` entrega é um mundo **completo e consistente**:

- 16 slots de jogador: ouro, madeira, comida, limite de comida, total coletado, raça;
- Até 1024 unidades: tipo, dono, coordenadas, vida / mana (com os máximos), ordem atual e alvo da ordem, **o que ela está atacando de fato** (alvo da tarefa), nível / XP / pontos de habilidade do herói e a visibilidade dela para cada jogador;
- Até 256 detalhes de unidade: 12 habilidades (nível, recarga restante), 8 buffs, 6 espaços de inventário;
- Itens no chão, árvores (atualizadas a cada 2 segundos), tabela de produção (progresso de treino / pesquisa / construção / melhoria), relógio do jogo, hora do dia no jogo.

Ler um snapshot leva cerca de **0.4 ms** (parse em Python), sem esperar a thread do jogo. Então: **leia à vontade**. APIs como `g.units()`, `g.my_army()`, `g.cooldown()` e `g.inventory()` tiram tudo do mesmo snapshot, então chamá-las muitas vezes em um tick custa pouco.

> **Dica**
>
> O período de publicação é ajustável: `g.set_publish_period(ms)`, de 16 a 1000 ms. Uma coleta leva cerca de 0.5 ~ 0.9 ms na thread do jogo, então até 33 ms funciona. Há um único valor compartilhado pela máquina inteira; vale a última escrita.

## Comandos: escrita, cerca de um frame

`g.move / attack / gather / build / train / cast …` são entregues à thread do jogo para execução. O runtime executa em lotes os comandos enviados pelos clientes dentro do **despacho de eventos** da thread do jogo, então um comando espera cerca de **um frame** (cerca de 0.1 ms quando cai num grupo de eventos; caso contrário, espera o próximo despacho).

- Comandos aceitam **uma unidade ou uma lista**; as unidades de uma lista recebem a ordem no mesmo frame;
- Adicionar `queue='after'` equivale ao Shift: faz isto depois de terminar o que está fazendo;
- Você pode passar os objetos de unidade que pegou do snapshot; o SDK confere a identidade pelo **par de handles** (endereços são reutilizados por unidades novas, handles não).

## Recibos: todo comando tem um

```python
r = g.build(worker, "hbar", x, y)
if r:                      # o engine aceitou
    ...
else:
    r.reason               # 'rejected（金不够）'  (= falta ouro)
    r.verdict              # 8
r.exec_us                  # quantos microssegundos este comando levou na thread do jogo
```

Os recibos são lidos **no mesmo frame**: a ordem da unidade antes e depois do comando, o valor de retorno da função do engine e o código de motivo da verificação de viabilidade. Um recibo responde "o engine aceitou este comando e, se não, por quê", mas **não responde** "no fim deu certo?" — para isso, olhe os snapshots e os eventos.

Para todos os códigos de status e códigos de motivo, veja [Recibos e códigos de motivo](https://war3ai.com/pt/docs/reason-codes/).

## Eventos: o que aconteceu

`on_event(g, ev)` roda antes de cada `on_tick` e entrega a você, um por um, todos os eventos desde o último tick:

| Evento | Significado |
|---|---|
| `unit.appeared` / `unit.died` / `unit.removed` | Uma unidade apareceu, morreu ou sumiu (entrar numa mina de ouro, ser convertida ou um cadáver se decompor também contam como sumir — não é o mesmo que morrer) |
| `unit.damaged` / `order.changed` / `owner.changed` | Perdeu vida, mudou de ordem, mudou de dono |
| `hero.levelup` | Um herói subiu de nível |
| `item.appeared` / `item.removed` | Um item no chão apareceu, foi pego ou foi usado |
| `damage` | Nível de engine: **cada golpe**. Unidade de origem, tipo de ataque, tipo de dano, vida realmente perdida, dano antes da armadura |
| `killed` | Nível de engine: este golpe a matou, com quem matou |
| `production.done` | Treino / pesquisa / construção / melhoria concluído, com o código de quatro caracteres e os segundos de jogo gastos. Também emitido para os adversários |
| `spell.cast` | Uma unidade lançou uma habilidade: código de quatro caracteres da habilidade, nível, recarga em segundos, ponto de lançamento |
| `message` | Apareceu uma linha numa caixa de mensagens da tela: dicas do jogo (“Você precisa de mais fazendas”), chat (`.chat` traz quem falou e o conteúdo), mensagens do sistema |
| `selection.changed` / `player.left` | A seleção do jogador local mudou / um jogador saiu ou foi removido por derrota |
| `game.started` / `game.ended` | Uma nova partida começou / saída da partida |

Eventos de entrada, como cliques em botões do canvas, teclas de atalho e cliques no chão, estão em [Interface e entrada](https://war3ai.com/pt/docs/ui-input/).

> **Atenção**
>
> O fluxo de eventos é **global**: as produções concluídas dos adversários e as mortes de creeps estão todas nele. Filtre por `ev.owner` ou pelo handle da unidade.

## Ticks: o ritmo do Bot

Por padrão, `on_tick` é chamado 5 vezes por segundo (relógio real). O custo de um tick é basicamente só a sua própria computação: snapshots não têm espera e comandos levam cerca de um frame. Se um tick passar do período, o próximo é adiado automaticamente — eles nunca se acumulam.

- **Na velocidade 2×, não espere pelo relógio real.** Para esperar 3 segundos de jogo, observe `g.clock()` avançar 3 — não use `sleep(1.5)`.
- **Não use `sleep` dentro do `on_tick`.** Se precisar "fazer daqui a pouco", anote o tempo de jogo atual e confira de novo no próximo tick.

## Lotes: dezenas de comandos, uma espera

Quando um tick envia muitos comandos, coloque-os dentro de `with g.batch():`:

```python
with g.batch():
    g.attack(melee, target_a)
    g.attack(ranged, target_b)
    g.move(wounded, home.x, home.y)
    g.cast(hero, "thunderclap")
# o lote inteiro é enviado quando o bloco termina: executado no mesmo frame, esperando a thread do jogo uma única vez
```

- Comandos dentro do bloco retornam `Pending`, que vira um recibo quando o bloco termina; lê-lo antes disso lança um erro;
- Se uma exceção for lançada dentro do bloco, **o lote inteiro é descartado** (meio conjunto de comandos é mais perigoso do que nenhum);
- Medido com 8 movimentos: 68 ~ 99 ms um por vez, **6.5 ~ 10 ms** em lote.

A mesma ideia vale para consultas: `g.can_do_many([(u, code), ...])` e `g.tech_many([...])` perguntam sobre muitas coisas de uma vez.

## Lendo o que você acabou de escrever

No mesmo tick, o snapshot ainda não reflete os comandos que você acabou de enviar (ele só alcança na próxima publicação). Duas partes da lógica podem acabar disputando o mesmo trabalhador: uma acabou de mandá-lo construir uma fazenda, enquanto a outra olha o snapshot e acha que ele ainda está ocioso.

`g.order_of(u)` resolve isso: até o snapshot alcançar, ele usa a nova ordem do recibo. **Para decidir se uma unidade está ociosa, use `g.order_of(u)`, não `u.order`.** `g.idle_workers()` já exclui os trabalhadores que receberam uma tarefa neste tick.

## Faixas de latência

| Faixa | Canal | Latência | Usado para |
|---|---|---|---|
| 0 | Snapshot enviado + fluxo de eventos | Cerca de 0.4 ms por leitura; dados novos a cada 50 ms | Todas as APIs de "observação" |
| 1 | Via rápida | Cerca de 1 frame; mediana de 0.06 ms com 6 processos em paralelo | Todos os comandos e consultas (padrão do SDK) |
| 2 | Canal de controle | 20 ~ 40 ms | Plano B e algumas operações de interface (velocidade do jogo, balões de fala, mensagens) |
| 3 | [Gateway](https://war3ai.com/pt/docs/gateway/) (WebSocket / JSON) | Faixa 1 + cerca de 1 ms | Qualquer linguagem, navegadores, LLMs, programas em outra máquina |

Cada API na [referência da API](https://war3ai.com/pt/api/) indica qual faixa ela usa.
