Docs Guías

Recetario de jugadas profesionales

La ventaja de los mejores jugadores viene sobre todo de decenas de pequeños hábitos. Esta página lleva las jugadas profesionales más comunes, una a una, a código del SDK; cada fragmento se puede copiar tal cual en on_tick.

Convenciones: g es Game, home es nuestra base principal (g.my_buildings({"htow", "hkee", "hcas"})[0]), now = g.clock(). Los detalles de cada interfaz están en el catálogo de la API, y los ejemplos completos que se pueden ejecutar, en Bots de ejemplo.

Cuando le pidas a un LLM que añada una jugada, pégale esa receta junto con su código: funciona mucho mejor que pedirle que «juegue más como un profesional».

I. Economía

1. Campesinos nunca ociosos, 5 por mina

for w in g.idle_workers():                                   # solo los ociosos (reasignar a uno ocupado interrumpe la recolección)
    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])

Lleva tú la cuenta de cuántos has mandado a cada mina (crew): los trabajadores que están dentro de una mina de oro no aparecen en la instantánea. El ejemplo completo está en hello_bot.py.

2. Solo 1 en la cola para no bloquear el dinero

for b in g.my_buildings({"hbar"}):
    if not g.queue(b):                                       # encola el siguiente solo cuando la cola está vacía
        g.train(b, "hfoo")

3. Que la comida nunca te bloquee

stuck = any(p.blocked for _b, p in g.all_production("me"))  # hay cola pero no ha empezado = falta comida
res = g.resources()
if stuck or res["food_cap"] - res["food_used"] <= 6:
    g.build_near(builder, "hhou", home.x, home.y)

blocked se adelanta a «casi lleno»: si pierdes un montón de unidades en una pelea y la cola se atasca al reponerlas, lo sabes al momento.

4. Orden de construcción + volver a la mina al terminar (Shift)

spot = g.build_near(w, "hbar", home.x, home.y)
if spot:
    g.gather(w, mine, queue="after")                         # al terminar vuelve a minar; no hay que buscarlo en el siguiente tick

Un campesino que construye varios edificios seguidos: g.build_queue(w, [("hhou", x1, y1), ("hhou", x2, y2)]). El dinero se descuenta al empezar cada obra.

5. Cuándo subir de tier; mejoras de ataque y armadura

if not g.queue(hall) and g.can_do(hall, "hkee") in (0, 220):   # solo se puede mejorar con la cola del ayuntamiento vacía (si no, 185)
    g.upgrade(hall, "hkee")
p = g.production(hall)                                          # progreso de la subida de tier
if p and p.kind == "upgrade":
    print(f"Faltan {p.remaining:.0f} s para el Torreón")
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. Expandirse: elegir la mina más cercana por distancia a pie

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)   # las minas en islas devuelven None -> van al final

II. Exploración e información

7. Ver qué está haciendo el rival

for b, p in g.all_production("enemy"):          # qué entrenan / investigan / mejoran los edificios rivales que ves
    print(b.type, p.kind, p.queue, f"{p.progress:.0%}" if p.progress is not None else "atascado")

Combínalo con eventos: ev.kind == "production.done" and ev.owner != g.me() indica qué acaba de sacar el rival.

8. Recordar lo que has visto (niebla de guerra)

for u, t, age in g.last_seen(max_age=60):       # enemigos vistos en los últimos 60 segundos de juego (última posición y vida)
    ...
hero_seen = [r for r in g.last_seen() if r[0].is_hero]   # dónde se vio por última vez al héroe rival

En modo justo es tu única fuente de información sobre el rival, igual que para un jugador humano.

9. Adónde va a atacar el ordenador (solo con la IA del ordenador)

plan = g.enemy_ai_plan(some_enemy_soldier)      # adónde va su capitán del ordenador

El ordenador fija el punto objetivo antes de salir: lleva tus tropas allí con antelación.

III. Creeping

10. Creepear de noche

if g.is_night():                                 # de 18:00 a 6:00: los creeps duermen (atacas primero sin que te rodeen) y la visión de todos se reduce
    ...
wait = g.seconds_until(18)                       # segundos de juego que faltan para el anochecer (un día = 480 s)

11. Atacar solo los campamentos que puedes ganar

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)                                 # cuánto tardamos en limpiar el campamento (estimación)
theirs = g.time_to_kill(camp, weakest_of_mine) or 1e9            # cuánto tardan ellos en matar a nuestra unidad más débil
if ours < theirs and g.reachable(center, camp[0]):
    g.attack_move(army, camp[0].x, camp[0].y)

Ejemplo completo: _maybe_creep en micro_bot.py.

IV. Micro

12. Concentrar fuego: al que «muere más rápido», no al más cercano

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)

Da la orden solo a quienes «no le están atacando ya» (current_target); no interrumpas a los que ya lo hacen.

13. Retirar las unidades heridas

for u in army:
    if u.hp < u.hp_max * 0.35:
        g.move(u, *toward(home, u, 500))       # retrocede 500 hacia casa; no la vuelvas a retirar en 3 segundos

Cómo saber si te concentran el fuego: en los eventos damage, una misma unidad recibe golpes de varias fuentes en poco tiempo = la están rodeando.

14. Mantener vivo al héroe y no regalar experiencia

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"))       # poción de curación: busca la casilla con inventory(h)

15. Counters: la unidad adecuada contra el objetivo adecuado

s = g.stats(u)
best = max(enemies, key=lambda e: s.dps_vs(g.stats(e)))   # fusileros contra grifos (perforante contra armadura ligera ×2), grifos contra soldados (mágico contra armadura pesada ×2)

La tabla de counters sale de los datos del juego: combat.damage_multiplier("pierce", "small") == 2.0.

16. Flanqueos y rutas: rodear las torres

route = g.walk_path(army_center, target)        # puntos de giro del camino terrestre más corto
g.path(army, route, attack=True)                # atacar-mover pasando por cada punto en orden

Para esquivar las torres, marca como intransitable la zona que las rodea en la cuadrícula de pathfinding y calcula de nuevo:

grid = g.grid().copy()
for t in towers:
    grid.block_area(t.x, t.y, 800)             # alcance de la torre 700 + margen
route = grid.path((army_x, army_y), (target.x, target.y))

17. Todos los comandos de un tick en un solo lote

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

Decenas de comandos esperan una sola vez al hilo del juego (medido: 8 movimientos, de 68 ms a 6.5 ms).

18. Asedio: artillería contra el suelo

g.attack_ground(mortars, tower.x, tower.y)       # morteros / catapultas disparan a una zona (detrás de un bosque, unidades invisibles)

V. Héroes

19. Tabla de habilidades

SKILLS = {"Hamg": ["AHwe", "AHbz", "AHwe", "AHbz", "AHwe", "AHmt"]}   # Elemental de agua, Ventisca… y en el nivel 6 la definitiva, Teletransporte en masa
info = g.hero_info(h)
if info and info["skill_points"]:
    g.learn(h, SKILLS[h.type][learned_count])  # si la rechaza (nivel insuficiente para la definitiva), espera al siguiente nivel

20. ¿Se lanzó el hechizo de verdad?

r = g.cast(h, "thunderbolt", target=enemy_hero)
# en el siguiente tick:
if g.cooldown(h, "AHtb"):                         # entró en enfriamiento = se lanzó de verdad; aceptado ≠ lanzado
    ...

21. Comprar pociones y volver a la base

g.buy(shop, "phea")                               # el héroe tiene que estar junto a la tienda
g.use_item(hero, slot, x=home.x, y=home.y)        # pergamino de portal a la ciudad (usar objeto sobre un punto)

VI. Análisis de partidas

  • Escribe en el log las decisiones de cada tick (print va a la ventana de ejecución) y combínalo con g.say(unidad, "¡Retirada!") para verlo dentro del juego;
  • El evento production.done incluye «cuántos segundos tardó»: construye tu propia línea de tiempo de producción (en qué segundo sale el primer héroe, en qué segundo subes de tier) y compárala con la de los mejores jugadores;
  • Deja que el Agent analice sus propias partidas: consulta el informe de partida en Iteración autónoma del Agent.