# Votre premier Bot

> Partez d'un Bot minimal de 10 lignes, ajoutez la production de paysans, la nourriture, l'armée, le héros et l'attaque, puis apprenez à lire les reçus.

Source: https://war3ai.com/fr/docs/first-bot/

Un Bot est simplement une classe qui hérite de `openwar3.Bot`. Vous ne surchargez que les hooks dont vous avez besoin ; `g` (`Game`) se charge de « voir » et d'« agir ».

## Le Bot minimal

```python title="my_bot.py"
from openwar3 import Bot

class MyBot(Bot):
    def on_start(self, g):          # appelé une fois au début de la partie
        g.message("Me voilà !")

    def on_tick(self, g):           # environ 5 fois par seconde
        for w in g.idle_workers():
            g.gather(w, g.nearest(g.gold_mines(), w))
```

```bash
python tools/play.py --bot my_bot.py
```

Les paysans inactifs partent vers la mine d'or la plus proche. Les quatre hooks :

| Hook | Quand il est appelé |
|---|---|
| `on_start(g)` | Une fois, au début de la partie, avant le premier tick |
| `on_tick(g)` | À chaque tick (5 fois par seconde par défaut). Un tick qui déborde décale automatiquement le suivant, sans accumulation |
| `on_event(g, ev)` | Avant chaque `on_tick`, vous transmet un par un les événements survenus depuis le tick précédent |
| `on_end(g, reason)` | Une fois, à la fin de la partie (processus du jeu disparu / plus aucune unité de notre côté / arrêt manuel) |

> **Astuce**
>
> Une exception levée dans `on_tick` n'interrompt pas la partie : l'exécuteur affiche la pile d'appels et continue au tick suivant ; il ne s'arrête qu'après **20 ticks d'erreurs consécutives**.

## Ajouter l'économie : paysans et nourriture

```python
from openwar3 import Bot

class Economy(Bot):
    def on_tick(self, g):
        res = g.resources()                       # une valeur illisible vaut None, pas 0
        halls = g.my_buildings({"htow", "hkee", "hcas"})
        if res is None or not halls:
            return
        home = halls[0]

        # 1. les paysans inactifs vont récolter de l'or
        for w in g.idle_workers():
            mine = g.nearest(g.gold_mines(), w)
            if mine:
                g.gather(w, mine)

        # 2. produire des paysans : un seul à la fois dans la file (une file pleine immobilise l'argent)
        if len(g.my_workers()) < 15 and not g.queue(home):
            g.train(home, "hpea")

        # 3. nourriture presque au plafond : prendre un paysan qui ne construit pas et bâtir une ferme près du hall
        if res["food_cap"] - res["food_used"] <= 6:
            builder = next((w for w in g.my_workers() if not g.is_constructing(w)), None)
            if builder:
                g.build_near(builder, "hhou", home.x, home.y)
```

Trois points à noter :

- **N'entraîner que si `g.queue(home)` est vide.** Donner l'ordre d'entraînement à chaque tick remplit la file de 7 emplacements et immobilise l'argent (mesuré : 4 paysans en file dans le hall, 300 d'or bloqués, un début de partie nettement plus lent).
- **`build_near` plutôt que des coordonnées en dur.** Il cherche lui-même un emplacement libre, du plus proche au plus éloigné, et suit le résultat d'un tick à l'autre ; si l'argent manque, il ne fait rien. Des coordonnées en dur tombent facilement en pleine forêt.
- **Ne pas choisir un paysan en train de construire.** Une ferme humaine demande 35 secondes ; si vous retirez l'ouvrier en cours de route, le chantier s'arrête.

La version complète, qui fonctionne pour les quatre races, est `brains/examples/hello_bot.py` : 5 ouvriers par mine, les suivants partent couper du bois une fois la mine pleine, et les chantiers arrêtés sont repris.

## Ajouter la caserne, le héros et l'attaque

```python
from openwar3 import Bot

WAVE = 8

class Rush(Bot):
    def on_start(self, g):
        self.attacking = False

    def on_tick(self, g):
        halls = g.my_buildings({"htow", "hkee", "hcas"})
        if not halls:
            return
        home = halls[0]

        # héros : un autel mais pas de héros -> ressusciter d'abord, sinon entraîner (un héros est unique : en entraîner un autre après sa mort est rejeté)
        altars = g.my_buildings({"halt"})
        if altars and not g.my_heroes():
            if not g.revive(altars[0]):
                g.train(altars[0], "Hpal")
        for h in g.my_heroes():
            info = g.hero_info(h)
            if info and info["skill_points"]:
                g.learn(h, "AHhb")                # Lumière sacrée

        # la caserne produit des fantassins en continu (un seul à la fois dans la file)
        for b in g.my_buildings({"hbar"}):
            if not g.queue(b):
                g.train(b, "hfoo")

        # attaquer quand une vague est prête ; rentrer après de lourdes pertes
        army = g.my_army()
        if len(army) >= WAVE:
            self.attacking = True
        elif len(army) < WAVE // 2:
            self.attacking = False
        if self.attacking:
            target = g.nearest([e for e in g.enemies() if g.is_building(e)], home)
            if target:
                idle = [u for u in army if not g.order_of(u)]      # ne donner des ordres qu'aux unités inactives
                g.attack_move(idle, target.x, target.y)
```

Version complète : `brains/examples/rush_bot.py` (elle hérite de `hello_bot` et construit la caserne / l'autel s'ils manquent).

## Lire les reçus

Chaque commande renvoie un reçu. `if r:` signifie « le moteur l'a acceptée » ; sinon, `r.reason` en donne la raison :

```python
r = g.train(barracks, "hfoo")
if not r:
    print(r.reason)        # rejected（人口不够）  (= nourriture insuffisante)
    print(r.verdict)       # 3
```

Codes de motif courants : `3` nourriture insuffisante, `8` or insuffisant, `9` bois insuffisant, `32` file pleine, `183` prérequis manquant, `221` élément absent / en construction / héros déjà présent, `1001` cible invisible. Liste complète dans [Reçus et codes de motif](https://war3ai.com/fr/docs/reason-codes/).

> **Accepté ≠ réussi**
>
> Un reçu indique seulement que « le moteur a accepté cette commande ». Le moteur accepte aussi sur le moment un emplacement de construction en pleine forêt ; l'ouvrier n'échoue qu'une fois sur place. Un sort peut être interrompu. Pour connaître l'effet réel, regardez l'instantané et les événements : pour construire, utilisez `build_near` (qui vérifie que les fondations apparaissent) ; pour un sort, vérifiez avec `g.cooldown()` qu'il est bien en recharge.

## Étapes suivantes

  - [Modèle mental](https://war3ai.com/fr/docs/concepts/): Instantané, commande, événement, tick, lot — pourquoi cette conception.
  - [Recettes des joueurs pros](https://war3ai.com/fr/docs/cookbook/): 21 recettes : récolte saturée, jamais bloqué par la nourriture, tir concentré, retrait des blessés, creeping de nuit…
