# Recettes des joueurs pros

> L'avantage des meilleurs joueurs tient surtout à des dizaines de « petites habitudes ». Cette page traduit une à une les techniques pro courantes en code SDK ; chaque extrait peut être copié tel quel dans on_tick.

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

Conventions : `g` est le `Game`, `home` est votre base principale (`g.my_buildings({"htow", "hkee", "hcas"})[0]`), `now = g.clock()`. Le détail des méthodes se trouve dans le [catalogue de l'API](https://war3ai.com/fr/api/), et des exemples complets et exécutables dans [Bots d'exemple](https://war3ai.com/fr/docs/examples/).

> **Astuce**
>
> Quand vous demandez à un LLM d'ajouter une technique, collez-lui la recette correspondante avec son code : c'est bien plus efficace que de lui demander de « jouer un peu plus pro ».

## I. Économie

### 1. Jamais de paysan inactif, 5 par mine

```python
for w in g.idle_workers():                                   # n'affecter que les inactifs (redonner un ordre à un ouvrier occupé interrompt sa récolte)
    mine = g.nearest([m for m in g.gold_mines() if crew[m.addr] < 5], w)
    g.gather(w, mine) if mine else g.gather(w, g.trees(w.x, w.y, limit=1)[0])
```

Tenez vous-même le compte des ouvriers envoyés à chaque mine (`crew`) : un ouvrier entré dans une mine d'or n'apparaît pas dans l'instantané. Exemple complet : `hello_bot.py`.

### 2. Une seule unité en file, l'argent n'est pas bloqué

```python
for b in g.my_buildings({"hbar"}):
    if not g.queue(b):                                       # n'en lancer une autre que quand la file est vide
        g.train(b, "hfoo")
```

### 3. Ne jamais être bloqué par la nourriture

```python
stuck = any(p.blocked for _b, p in g.all_production("me"))  # en file mais pas démarré = nourriture insuffisante
res = g.resources()
if stuck or res["food_cap"] - res["food_used"] <= 6:
    g.build_near(builder, "hhou", home.x, home.y)
```

`blocked` réagit un temps avant « presque plein » : après avoir perdu une bonne partie de l'armée en combat, vous le savez dès que la file se bloque au moment de reconstruire.

### 4. Ordre de construction + retour à la mine une fois le bâtiment fini (Shift + mine)

```python
spot = g.build_near(w, "hbar", home.x, home.y)
if spot:
    g.gather(w, mine, queue="after")                         # retourner récolter une fois le bâtiment terminé, inutile de le rechercher au tick suivant
```

Un paysan qui enchaîne plusieurs bâtiments : `g.build_queue(w, [("hhou", x1, y1), ("hhou", x2, y2)])`. L'argent n'est prélevé qu'au début de chaque construction.

### 5. Moment du passage de tier, améliorations d'attaque et d'armure

```python
if not g.queue(hall) and g.can_do(hall, "hkee") in (0, 220):   # l'amélioration n'est possible que si la file du hall est vide (sinon 185)
    g.upgrade(hall, "hkee")
p = g.production(hall)                                          # progression du passage de tier
if p and p.kind == "upgrade":
    print(f"Hall principal : encore {p.remaining:.0f} s")
for sm in g.my_buildings({"hbla"}):
    if not g.queue(sm):
        ok = [u for u, v in zip(UPS, g.can_do_many([(sm, u) for u in UPS])) if v in (0, 220)]
        if ok:
            g.research(sm, ok[0])
```

### 6. Prendre une expansion : choisir la mine la plus proche à pied

```python
mines = [m for m in g.gold_mines() if g.dist(m, home) > 1500 and not taken(m)]
best = min(mines, key=lambda m: g.path_distance(home, m) or 1e9)   # une mine sur une île renvoie None -> classée en dernier
```

## II. Éclaireurs et renseignement

### 7. Voir ce que fait l'adversaire

```python
for b, p in g.all_production("enemy"):          # ce que les bâtiments adverses visibles entraînent / recherchent / améliorent
    print(b.type, p.kind, p.queue, f"{p.progress:.0%}" if p.progress is not None else "bloqué")
```

Avec les événements : `ev.kind == "production.done" and ev.owner != g.me()` — ce que l'adversaire vient de produire.

### 8. Se souvenir de ce qu'on a vu (brouillard de guerre)

```python
for u, t, age in g.last_seen(max_age=60):       # ennemis vus dans les 60 dernières secondes de jeu (dernière position et derniers PV)
    ...
hero_seen = [r for r in g.last_seen() if r[0].is_hero]   # où se trouvait le héros adverse la dernière fois
```

En mode équitable, c'est votre seule source d'information sur l'adversaire, exactement comme pour un joueur humain.

### 9. Où l'ordinateur adverse va attaquer (IA de l'ordinateur uniquement)

```python
plan = g.enemy_ai_plan(some_enemy_soldier)      # la destination de son capitaine d'IA
```

L'ordinateur fixe son point cible avant même de sortir : ramenez vos troupes là-bas à l'avance.

## III. Creeping

### 10. Creeper la nuit

```python
if g.is_night():                                 # de 18 h à 6 h : les creeps dorment (vous frappez en premier sans être encerclé), la vision de tous diminue
    ...
wait = g.seconds_until(18)                       # secondes de jeu avant la tombée de la nuit (une journée = 480 s)
```

### 11. N'attaquer que les camps à votre portée

```python
from openwar3 import combat
mine = [g.stats(u) for u in army]
def ttk(target): return combat.time_to_kill(mine, g.stats(target), target_hp=target.hp) or 1e9
camp = [c for c in g.creeps() if g.dist(c, center) < 600]
ours = max(ttk(c) for c in camp)                                 # temps pour vider ce camp (estimation grossière)
theirs = g.time_to_kill(camp, weakest_of_mine) or 1e9            # temps qu'il leur faut pour tuer notre unité la plus faible
if ours < theirs and g.reachable(center, camp[0]):
    g.attack_move(army, camp[0].x, camp[0].y)
```

Exemple complet : `_maybe_creep` dans `micro_bot.py`.

## IV. Micro

### 12. Tir concentré : viser « ce qui meurt le plus vite », pas le plus proche

```python
target = min(visible_enemies, key=lambda e: g.time_to_kill(fighters, e) or 1e9)
g.attack([u for u in fighters if (g.current_target(u) or target).handle != target.handle], target)
```

Ne donnez l'ordre qu'aux unités qui « ne l'attaquent pas déjà » (`current_target`) ; n'interrompez pas celles qui le frappent.

### 13. Retirer les unités blessées

```python
for u in army:
    if u.hp < u.hp_max * 0.35:
        g.move(u, *toward(home, u, 500))       # reculer de 500 vers la base ; ne pas la retirer à nouveau dans les 3 s
```

Pour détecter un focus : dans les événements `damage`, une même unité touchée par plusieurs sources en peu de temps = elle est encerclée.

### 14. Garder le héros en vie, ne pas offrir d'expérience

```python
for h in g.my_heroes():
    if h.hp < h.hp_max * 0.4:
        g.move(h, home.x, home.y)
        g.use_item(h, slot_of(h, "phea"))       # potion de soins : trouver l'emplacement avec inventory(h)
```

### 15. Contres : les bonnes unités sur les bonnes cibles

```python
s = g.stats(u)
best = max(enemies, key=lambda e: s.dps_vs(g.stats(e)))   # fusiliers contre chevaucheurs de griffon (perforant contre armure légère ×2), griffons contre fantassins (magique contre armure lourde ×2)
```

La table des contres vient des données du jeu : `combat.damage_multiplier("pierce", "small") == 2.0`.

### 16. Prise en tenaille et itinéraires : contourner les tours

```python
route = g.walk_path(army_center, target)        # points d'inflexion du plus court chemin au sol
g.path(army, route, attack=True)                # attaque-déplacement par chaque point, dans l'ordre
```

Pour contourner les tours, marquez leurs abords comme infranchissables sur la grille de pathfinding avant de calculer :

```python
grid = g.grid().copy()
for t in towers:
    grid.block_area(t.x, t.y, 800)             # portée de la tour 700 + marge
route = grid.path((army_x, army_y), (target.x, target.y))
```

### 17. Envoyer toutes les commandes d'un tick en un lot

```python
with g.batch():
    g.attack(melee, target_a)
    g.attack(ranged, target_b)
    g.move(wounded, *home_xy)
    g.cast(hero, "thunderclap")
```

Des dizaines de commandes pour une seule attente du thread du jeu (mesuré : 8 déplacements, 68 ms → 6.5 ms).

### 18. Siège : l'artillerie tire au sol

```python
g.attack_ground(mortars, tower.x, tower.y)       # mortiers / engins de siège tirant sur une zone (derrière des arbres, unités invisibles)
```

## V. Héros

### 19. Ordre des compétences

```python
SKILLS = {"Hamg": ["AHwe", "AHbz", "AHwe", "AHbz", "AHwe", "AHmt"]}   # Élémentaire d'eau, Blizzard… ultime au niveau 6 : Téléportation de masse
info = g.hero_info(h)
if info and info["skill_points"]:
    g.learn(h, SKILLS[h.type][learned_count])  # si rejeté (niveau insuffisant pour l'ultime), attendre le niveau suivant
```

### 20. Le sort est-il vraiment parti ?

```python
r = g.cast(h, "thunderbolt", target=enemy_hero)
# au tick suivant :
if g.cooldown(h, "AHtb"):                         # en recharge = vraiment lancé ; accepté ≠ lancé
    ...
```

### 21. Acheter des potions, rentrer en ville

```python
g.buy(shop, "phea")                               # le héros se tient à côté de la boutique
g.use_item(hero, slot, x=home.x, y=home.y)        # parchemin de téléportation (objet utilisé sur un point)
```

## VI. Analyse d'après-partie

- À chaque tick, écrivez vos décisions dans le journal (`print` s'affiche dans la fenêtre d'exécution), et utilisez `g.say(unité, "Repli !")` pour les voir en jeu ;
- L'événement `production.done` indique « combien de secondes cela a pris » : établissez votre propre chronologie de construction (à quelle seconde sort le premier héros, à quelle seconde vous passez de tier) et comparez-la à celle des meilleurs joueurs ;
- Laissez l'agent analyser lui-même ses parties : voir le rapport de partie dans [Itération autonome d'un agent](https://war3ai.com/fr/docs/agent-loop/).
