# Modèle mental

> Instantané, commande, reçu, événement, tick, lot. Comprenez ces six concepts et vous comprendrez pourquoi l'API a cette forme, et comment écrire du code rapide.

Source: https://war3ai.com/fr/docs/concepts/

## Instantané : lecture, zéro attente

Toutes les **50 ms**, le runtime relève l'ensemble du monde sur le thread du jeu et l'écrit en mémoire partagée. `g.snapshot()` vous donne un monde **complet et cohérent** :

- 16 emplacements de joueur : or, bois, nourriture, plafond de nourriture, total récolté, race ;
- jusqu'à 1024 unités : type, propriétaire, coordonnées, PV / mana (avec leurs maximums), ordre en cours et cible de l'ordre, **ce qu'elle attaque réellement** (cible de tâche), niveau / expérience / points de compétence des héros, visibilité de l'unité pour chaque joueur ;
- jusqu'à 256 fiches de détail d'unité : 12 capacités (niveau, recharge restante), 8 buffs, 6 emplacements d'inventaire ;
- objets au sol, arbres (rafraîchis toutes les 2 secondes), table de production (progression des entraînements / recherches / constructions / améliorations), horloge de jeu, heure du jour dans le jeu.

Lire un instantané prend environ **0.4 ms** (analyse en Python), sans attendre le thread du jeu. Donc : **lisez sans compter**. Les méthodes comme `g.units()`, `g.my_army()`, `g.cooldown()` ou `g.inventory()` puisent toutes dans le même instantané ; les appeler autant de fois que vous voulez dans un tick ne coûte presque rien.

> **Astuce**
>
> La période de publication est réglable : `g.set_publish_period(ms)`, de 16 à 1000 millisecondes. Un relevé prend environ 0.5 ~ 0.9 ms sur le thread du jeu, 33 ms ne pose donc aucun problème. La valeur est partagée par toute la machine : la dernière écrite l'emporte.

## Commande : écriture, environ une frame

`g.move / attack / gather / build / train / cast …` sont exécutées par le thread du jeu. Le runtime exécute par lots les commandes soumises par les clients dans la **distribution d'événements** du thread du jeu ; une commande attend donc environ **une frame** (environ 0.1 ms si elle tombe dans une salve d'événements, sinon jusqu'à la distribution suivante).

- Une commande accepte **une unité ou une liste** ; les unités d'une liste reçoivent l'ordre dans la même frame ;
- Ajouter `queue='after'` équivaut à Shift : l'unité termine sa tâche en cours avant de faire celle-ci ;
- Dans une commande, utilisez simplement les objets unité tirés de l'instantané : le SDK vérifie leur identité par **paire de handles** (une adresse peut être réutilisée par une nouvelle unité, un handle non).

## Reçu : pour chaque commande

```python
r = g.build(worker, "hbar", x, y)
if r:                      # le moteur l'a acceptée
    ...
else:
    r.reason               # 'rejected（金不够）'  (= or insuffisant)
    r.verdict              # 8
r.exec_us                  # microsecondes d'exécution de cette commande sur le thread du jeu
```

Le reçu est relu **dans la même frame** : l'ordre de l'unité avant et après la commande, la valeur de retour de la fonction du moteur, le code de motif de la vérification de faisabilité. Il répond à « le moteur a-t-il accepté cette commande, et sinon pourquoi », mais **pas** à « l'action a-t-elle finalement abouti » — pour cela, regardez l'instantané et les événements.

Tous les codes d'état et codes de motif sont listés dans [Reçus et codes de motif](https://war3ai.com/fr/docs/reason-codes/).

## Événement : ce qui s'est passé

Avant chaque `on_tick`, `on_event(g, ev)` vous transmet un par un les événements survenus depuis le tick précédent :

| Événement | Signification |
|---|---|
| `unit.appeared` / `unit.died` / `unit.removed` | Une unité apparaît, meurt, disparaît (entrer dans une mine d'or, être convertie ou un cadavre qui se décompose comptent aussi comme disparition, ce qui n'est pas une mort) |
| `unit.damaged` / `order.changed` / `owner.changed` | Perte de PV, changement d'ordre, changement de propriétaire |
| `hero.levelup` | Un héros monte de niveau |
| `item.appeared` / `item.removed` | Un objet au sol apparaît, est ramassé ou utilisé |
| `damage` | Niveau moteur : **chaque coup** porté. Unité source, type d'attaque, type de dégâts, PV réellement perdus, dégâts avant armure |
| `killed` | Niveau moteur : ce coup a tué l'unité, avec le tueur |
| `production.done` | Entraînement / recherche / construction / amélioration terminé, avec le code à quatre caractères et le nombre de secondes de jeu nécessaires. Émis aussi pour l'adversaire |
| `spell.cast` | Une unité a lancé un sort : code à quatre caractères du sort, niveau, recharge en secondes, point d'incantation |
| `message` | Une ligne est apparue dans un cadre de messages à l'écran : indication du jeu (« Il vous faut plus de fermes »), chat (`.chat` contient l'auteur et le texte), message système |
| `selection.changed` / `player.left` | La sélection du joueur local a changé / un joueur est parti ou a été retiré après sa défaite |
| `game.started` / `game.ended` | Une nouvelle partie commence / on quitte la partie |

Les événements d'entrée — clic sur un bouton du canevas, raccourci clavier, clic au sol — sont décrits dans [Interface et entrées](https://war3ai.com/fr/docs/ui-input/).

> **Attention**
>
> Le flux d'événements est **global** : il contient aussi les productions terminées de l'adversaire et la mort des creeps. Filtrez par `ev.owner` ou par handle d'unité.

## Tick : le rythme du Bot

Par défaut, `on_tick` est appelé 5 fois par seconde (en temps réel). La durée d'un tick correspond pour l'essentiel à vos propres calculs : l'instantané est sans attente, une commande prend environ une frame. Un tick qui dépasse sa période décale automatiquement le suivant, sans accumulation.

- **En vitesse ×2, n'attendez pas en temps réel.** Pour attendre 3 secondes de jeu, vérifiez que `g.clock()` a augmenté de 3, pas `sleep(1.5)`.
- **Pas de `sleep` dans `on_tick`.** Pour « faire quelque chose un peu plus tard », notez l'heure de jeu actuelle et vérifiez de nouveau au tick suivant.

## Lot : des dizaines de commandes, une seule attente

Quand un tick doit envoyer beaucoup de commandes, regroupez-les dans `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")
# à la fin du bloc, tout le lot est soumis : exécuté dans la même frame, une seule attente du thread du jeu
```

- Dans le bloc, les commandes renvoient `Pending`, qui devient un reçu à la fin du bloc ; le lire avant la fin du bloc lève une erreur ;
- Si une exception est levée dans le bloc, **tout le lot est annulé** (des commandes à moitié envoyées sont plus dangereuses que rien du tout) ;
- Mesuré sur 8 déplacements : 68 ~ 99 ms une par une, **6.5 ~ 10 ms** en un lot.

Le même principe vaut pour les requêtes : `g.can_do_many([(u, code), ...])` et `g.tech_many([...])` posent de nombreuses questions d'un coup.

## Relire ce que vous venez d'écrire

Dans un même tick, l'instantané ne voit pas encore la commande que vous venez de donner (il ne la rattrape qu'à la publication suivante). Deux parties de votre logique peuvent donc se disputer le même ouvrier : l'une vient de l'envoyer construire une ferme, l'autre le croit encore inactif d'après l'instantané.

`g.order_of(u)` règle ce problème : tant que l'instantané n'a pas rattrapé la commande, il se fie au nouvel ordre indiqué dans le reçu. **Pour savoir si une unité est inactive, utilisez `g.order_of(u)`, pas `u.order`.** `g.idle_workers()` exclut déjà les ouvriers « qui viennent de recevoir une tâche pendant ce tick ».

## Niveaux de latence

| Niveau | Canal | Latence | Usage |
|---|---|---|---|
| 0 | Instantané poussé + flux d'événements | Environ 0.4 ms par lecture ; données renouvelées toutes les 50 ms | Toutes les méthodes « d'observation » |
| 1 | Voie rapide | Environ 1 frame ; médiane de 0.06 ms avec 6 processus en parallèle | Toutes les commandes et requêtes (par défaut dans le SDK) |
| 2 | Canal de contrôle | 20 ~ 40 ms | Solution de repli et quelques opérations d'interface (vitesse de jeu, bulles, messages) |
| 3 | [Passerelle](https://war3ai.com/fr/docs/gateway/) (WebSocket / JSON) | Niveau 1 + environ 1 ms | N'importe quel langage, navigateur, LLM, programme sur une autre machine |

Le [catalogue de l'API](https://war3ai.com/fr/api/) indique pour chaque méthode le niveau qu'elle utilise.
