Profi-Kochbuch
Der Vorsprung guter Spieler steckt größtenteils in Dutzenden kleiner Gewohnheiten. Diese Seite übersetzt gängige Profi-Techniken eine nach der anderen in SDK-Code – jeder Abschnitt lässt sich direkt in on_tick kopieren.
Konventionen: g ist das Game, home ist unsere Hauptbasis (g.my_buildings({"htow", "hkee", "hcas"})[0]), now = g.clock(). Details zur API findest du in der API-Referenz, vollständige lauffähige Beispiele unter Beispiel-Bots.
Wenn ein LLM eine bestimmte Technik einbauen soll, gib ihm das passende Rezept samt Code mit. Das wirkt viel besser als die Bitte, „professioneller zu spielen“.
I. Wirtschaft
1. Arbeiter nie untätig, 5 pro Mine
for w in g.idle_workers(): # nur untätige zuweisen (neue Befehle an beschäftigte unterbrechen das Sammeln)
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])
Merk dir selbst, wie viele du jeder Mine zugewiesen hast (crew): Arbeiter in einer Goldmine sind nicht im Snapshot. Vollständiges Beispiel in hello_bot.py.
2. Nur 1 in die Warteschlange, kein gebundenes Gold
for b in g.my_buildings({"hbar"}):
if not g.queue(b): # erst den nächsten einreihen, wenn sie leer ist
g.train(b, "hfoo")
3. Nie an der Nahrung hängen bleiben
stuck = any(p.blocked for _b, p in g.all_production("me")) # eingereiht, aber nicht gestartet = nicht genug Nahrung
res = g.resources()
if stuck or res["food_cap"] - res["food_used"] <= 6:
g.build_near(builder, "hhou", home.x, home.y)
blocked greift einen Schritt früher als „fast voll“: Verlierst du im Kampf einen Teil der Armee und stockt beim Nachbauen die Warteschlange, weißt du es sofort.
4. Bauabfolge + danach selbst zur Mine zurück (Shift-Rückkehr zur Mine)
spot = g.build_near(w, "hbar", home.x, home.y)
if spot:
g.gather(w, mine, queue="after") # nach dem Bau zurück zur Mine, im nächsten Tick nicht erneut suchen
Ein Bauer baut mehrere Gebäude nacheinander: g.build_queue(w, [("hhou", x1, y1), ("hhou", x2, y2)]). Das Gold wird erst bei Baubeginn abgezogen.
5. Tier-up-Timing, Angriffs-/Rüstungs-Upgrades
if not g.queue(hall) and g.can_do(hall, "hkee") in (0, 220): # das Hauptgebäude kann nur mit leerer Warteschlange ausbauen (sonst 185)
g.upgrade(hall, "hkee")
p = g.production(hall) # Tier-up-Fortschritt
if p and p.kind == "upgrade":
print(f"Hauptgebäude braucht noch {p.remaining:.0f} Sekunden")
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. Expansion: die nächste Mine nach Laufweg wählen
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) # Minen auf Inseln liefern None -> landen ganz hinten
II. Aufklärung und Informationen
7. Sehen, was der Gegner macht
for b, p in g.all_production("enemy"): # was sichtbare gegnerische Gebäude trainieren / erforschen / aufwerten
print(b.type, p.kind, p.queue, f"{p.progress:.0%}" if p.progress is not None else "hängt")
Kombiniert mit Events: ev.kind == "production.done" and ev.owner != g.me() – was der Gegner gerade fertiggestellt hat.
8. Merken, was du gesehen hast (Kriegsnebel)
for u, t, age in g.last_seen(max_age=60): # Gegner, die in den letzten 60 Spielsekunden gesehen wurden (letzte Position und TP)
...
hero_seen = [r for r in g.last_seen() if r[0].is_hero] # wo der gegnerische Held zuletzt war
Im Fair-Modus ist das deine einzige Informationsquelle über den Gegner – genau wie bei einem menschlichen Spieler.
9. Wo der Computergegner angreifen will (nur Computer-KI)
plan = g.enemy_ai_plan(some_enemy_soldier) # wohin sein Computer-Captain unterwegs ist
Der Computer legt seinen Zielpunkt fest, bevor er losmarschiert – zieh deine Armee rechtzeitig dorthin zurück.
III. Creepen
10. Nachts creepen
if g.is_night(): # 18 ~ 6 Uhr: Creeps schlafen (du triffst zuerst, ohne umzingelt zu werden), alle sehen weniger weit
...
wait = g.seconds_until(18) # Spielsekunden bis zum Einbruch der Nacht (ein Tag hat 480 Sekunden)
11. Nur Lager angreifen, die du schaffst
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) # wie lange es dauert, dieses Lager zu räumen (grob)
theirs = g.time_to_kill(camp, weakest_of_mine) or 1e9 # wie lange sie brauchen, um unsere schwächste Einheit zu töten
if ours < theirs and g.reachable(center, camp[0]):
g.attack_move(army, camp[0].x, camp[0].y)
Vollständiges Beispiel: _maybe_creep in micro_bot.py.
IV. Mikro
12. Fokusfeuer: das angreifen, was am schnellsten stirbt, nicht das Nächste
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)
Befiehl nur den Einheiten, die es noch nicht angreifen (current_target), damit du die anderen nicht unterbrichst.
13. Verwundete zurückziehen
for u in army:
if u.hp < u.hp_max * 0.35:
g.move(u, *toward(home, u, 500)) # 500 Richtung Basis zurückziehen; dieselbe Einheit 3 Sekunden lang nicht erneut ziehen
Fokusfeuer erkennen: Trifft dieselbe Einheit in damage-Events kurz hintereinander Schaden aus mehreren Quellen, ist sie umzingelt.
14. Helden am Leben halten, keine EP verschenken
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")) # Heiltrank: Platznummer über inventory(h) finden
15. Konter: die richtige Einheit auf das richtige Ziel
s = g.stats(u)
best = max(enemies, key=lambda e: s.dps_vs(g.stats(e))) # Büchsenschützen auf Greifenreiter (Durchbohrend gegen leichte Rüstung ×2), Greifenreiter auf Fußsoldaten (Magie gegen schwere Rüstung ×2)
Die Kontertabelle stammt aus den Spieldaten: combat.damage_multiplier("pierce", "small") == 2.0.
16. Flanken und Wegführung: Türme umgehen
route = g.walk_path(army_center, target) # Knickpunkte des kürzesten Bodenwegs
g.path(army, route, attack=True) # nacheinander per Angriffsbewegung durch jeden Punkt
Um Türme zu umgehen, markierst du den Bereich um jeden Turm im Wegfindungsraster als unpassierbar und berechnest dann die Route:
grid = g.grid().copy()
for t in towers:
grid.block_area(t.x, t.y, 800) # Turmreichweite 700 + Puffer
route = grid.path((army_x, army_y), (target.x, target.y))
17. Die Befehle eines Ticks als ein Batch senden
with g.batch():
g.attack(melee, target_a)
g.attack(ranged, target_b)
g.move(wounded, *home_xy)
g.cast(hero, "thunderclap")
Dutzende Befehle warten nur einmal auf den Spiel-Thread (gemessen: 8 Bewegungen 68 ms → 6.5 ms).
18. Belagerung: Artillerie greift den Boden an
g.attack_ground(mortars, tower.x, tower.y) # Mörsertrupps / Katapulte feuern auf eine Fläche (hinter Bäumen, unsichtbare Einheiten)
V. Helden
19. Skill-Reihenfolge
SKILLS = {"Hamg": ["AHwe", "AHbz", "AHwe", "AHbz", "AHwe", "AHmt"]} # Wasserelementar, Blizzard … Stufe-6-Ultimate Massenteleportation
info = g.hero_info(h)
if info and info["skill_points"]:
g.learn(h, SKILLS[h.type][learned_count]) # abgelehnt (Stufe zu niedrig fürs Ultimate)? auf die nächste Stufe warten
20. Wurde der Zauber wirklich gewirkt?
r = g.cast(h, "thunderbolt", target=enemy_hero)
# im nächsten Tick:
if g.cooldown(h, "AHtb"): # Abklingzeit läuft = wirklich gewirkt; angenommen ≠ gewirkt
...
21. Tränke kaufen, zurück in die Stadt
g.buy(shop, "phea") # der Held steht neben dem Laden
g.use_item(hero, slot, x=home.x, y=home.y) # Stadtportal-Schriftrolle (Gegenstand auf einen Punkt benutzen)
VI. Nachbesprechung
- Schreib die Entscheidungen jedes Ticks ins Log (
printlandet im Ausführungsfenster) und nutzeg.say(einheit, "Rückzug"), um sie im Spiel zu sehen; production.done-Events enthalten „wie viele Sekunden es gedauert hat“ – erstelle daraus deine eigene Bau-Timeline (in welcher Sekunde der erste Held kam, wann dein Tier-up fertig war) und vergleiche sie mit der von Top-Spielern;- Lass den Agent sich selbst auswerten: siehe den Spielbericht in Agent-Selbstiteration.