# Tu primer Bot

> Empieza con un Bot mínimo de 10 líneas, añade campesinos, comida, tropas, un héroe y un ataque, y aprende a leer los recibos.

Fuente: https://war3ai.com/es/docs/first-bot/

Un Bot es una clase que hereda de `openwar3.Bot`. Solo tienes que sobrescribir los hooks que necesites; `g` (`Game`) se encarga de «observar» y «actuar».

## El Bot mínimo

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

class MyBot(Bot):
    def on_start(self, g):          # se llama una vez al entrar en la partida
        g.message("¡Ya estoy aquí!")

    def on_tick(self, g):           # unas 5 veces por segundo
        for w in g.idle_workers():
            g.gather(w, g.nearest(g.gold_mines(), w))
```

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

Los campesinos ociosos irán a la mina de oro más cercana. Estos son los cuatro hooks:

| Hook | Cuándo se llama |
|---|---|
| `on_start(g)` | Una vez, al entrar en la partida y antes del primer tick |
| `on_tick(g)` | En cada tick (por defecto, 5 veces por segundo). Si un tick se pasa de tiempo, el siguiente se aplaza automáticamente; no se acumulan |
| `on_event(g, ev)` | Antes de cada `on_tick`; te entrega uno a uno los eventos ocurridos desde el tick anterior |
| `on_end(g, reason)` | Una vez, cuando termina la partida (el proceso del juego desaparece / no nos quedan unidades / parada manual) |

> **Consejo**
>
> Una excepción dentro de `on_tick` no interrumpe la partida: el ejecutor imprime el stack trace y sigue en el siguiente tick; solo se detiene tras **20 ticks seguidos con error**.

## Añade la economía: campesinos y comida

```python
from openwar3 import Bot

class Economy(Bot):
    def on_tick(self, g):
        res = g.resources()                       # si no se puede leer es None, no 0
        halls = g.my_buildings({"htow", "hkee", "hcas"})
        if res is None or not halls:
            return
        home = halls[0]

        # 1. Los campesinos ociosos van a por oro
        for w in g.idle_workers():
            mine = g.nearest(g.gold_mines(), w)
            if mine:
                g.gather(w, mine)

        # 2. Entrenar campesinos: solo 1 en la cola (llenarla deja el dinero bloqueado en la cola)
        if len(g.my_workers()) < 15 and not g.queue(home):
            g.train(home, "hpea")

        # 3. Comida casi al límite: busca un campesino que no esté construyendo y levanta una granja cerca del ayuntamiento
        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)
```

Tres detalles que vale la pena destacar:

- **Entrena solo cuando `g.queue(home)` está vacía.** Dar la orden de entrenar en cada tick llena la cola de 7 casillas y bloquea el dinero (medido: el ayuntamiento acabó con 4 campesinos en cola y 300 de oro bloqueados, y la apertura se retrasó muchísimo).
- **`build_near` en lugar de coordenadas fijas.** Busca por sí solo, de cerca a lejos, un sitio donde quepa el edificio y sigue el resultado entre ticks; si falta dinero, no hace nada. Unas coordenadas fijas pueden caer justo en un bosque.
- **No elijas a un campesino que ya esté construyendo.** Una granja humana tarda 35 segundos en construirse; si te llevas al trabajador a mitad de obra, los cimientos se quedan parados.

La versión completa, que funciona con las cuatro razas, es `brains/examples/hello_bot.py`: 5 por mina, a talar cuando la mina está llena y retomar los cimientos abandonados.

## Añade cuartel, héroe y ataque

```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éroe: si hay altar y no hay héroe -> primero intenta revivir; si no se puede, entrena (el héroe es único; entrenar otro tras su muerte se rechaza)
        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")                # Luz sagrada

        # El cuartel saca soldados sin parar (solo 1 en la cola)
        for b in g.my_buildings({"hbar"}):
            if not g.queue(b):
                g.train(b, "hfoo")

        # Con una oleada completa, al ataque; si queda destrozada, a casa
        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)]      # órdenes solo a las ociosas
                g.attack_move(idle, target.x, target.y)
```

La versión completa está en `brains/examples/rush_bot.py` (hereda de `hello_bot` y construye el cuartel / altar si no existen).

## Aprende a leer los recibos

Cada comando devuelve un recibo. `if r:` significa «el motor lo aceptó»; si no lo aceptó, `r.reason` dice por qué:

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

Códigos de motivo frecuentes: `3` falta comida, `8` falta oro, `9` falta madera, `32` cola llena, `183` falta un requisito previo, `221` no existe esa opción / en construcción / ese héroe ya existe, `1001` el objetivo no es visible. La tabla completa está en [Recibos y códigos de motivo](https://war3ai.com/es/docs/reason-codes/).

> **Aceptado ≠ conseguido**
>
> El recibo solo indica que «el motor aceptó el comando». El motor también acepta en el acto un punto de construcción dentro de un bosque, y el trabajador solo falla al llegar; un hechizo puede ser interrumpido. Para ver el efecto, mira la instantánea y los eventos: para construir usa `build_near` (sigue si aparecen los cimientos) y, para los hechizos, comprueba si `g.cooldown()` entró en enfriamiento.

## Siguientes pasos

  - [Modelo mental](https://war3ai.com/es/docs/concepts/): Instantánea, comando, evento, tick y lote: por qué está diseñado así.
  - [Recetario de jugadas profesionales](https://war3ai.com/es/docs/cookbook/): 21 recetas: minas saturadas, comida sin atascos, fuego concentrado, retirar heridos, creepear de noche…
