Dein erster Bot
Starte mit einem minimalen Bot aus 10 Zeilen, ergänze Bauern, Nahrung, Truppen, Helden und Angriffe und lerne zum Schluss, Quittungen zu lesen.
Ein Bot ist eine Klasse, die von openwar3.Bot erbt. Du überschreibst nur die Hooks, die du brauchst; g (Game) ist fürs „Sehen“ und „Handeln“ zuständig.
Der minimale Bot
from openwar3 import Bot
class MyBot(Bot):
def on_start(self, g): # einmal nach Partiebeginn aufgerufen
g.message("Da bin ich")
def on_tick(self, g): # etwa 5-mal pro Sekunde
for w in g.idle_workers():
g.gather(w, g.nearest(g.gold_mines(), w))
python tools/play.py --bot my_bot.py
Untätige Bauern gehen zur nächsten Goldmine. Die vier Hooks:
| Hook | Wann er aufgerufen wird |
|---|---|
on_start(g) | Einmal nach Partiebeginn, vor dem ersten Tick |
on_tick(g) | In jedem Tick (standardmäßig 5-mal pro Sekunde). Dauert ein Tick zu lange, verschiebt sich der nächste automatisch, statt dass sich Ticks aufstauen |
on_event(g, ev) | Vor jedem on_tick; übergibt dir alle Events seit dem letzten Tick, eines nach dem anderen |
on_end(g, reason) | Einmal, wenn die Partie endet (Spielprozess beendet / keine eigenen Einheiten mehr / manuell gestoppt) |
Eine Exception in on_tick bricht nicht die ganze Partie ab: Der Runner gibt den Stacktrace aus und macht im nächsten Tick weiter; erst nach 20 Fehlern in Folge hält er an.
Wirtschaft ergänzen: Bauern und Nahrung
from openwar3 import Bot
class Economy(Bot):
def on_tick(self, g):
res = g.resources() # nicht lesbar = None, nicht 0
halls = g.my_buildings({"htow", "hkee", "hcas"})
if res is None or not halls:
return
home = halls[0]
# 1. Untätige Bauern sammeln Gold
for w in g.idle_workers():
mine = g.nearest(g.gold_mines(), w)
if mine:
g.gather(w, mine)
# 2. Bauern trainieren: nur 1 in die Warteschlange (eine volle Warteschlange bindet Gold)
if len(g.my_workers()) < 15 and not g.queue(home):
g.train(home, "hpea")
# 3. Nahrung fast voll: einen Bauern suchen, der gerade nicht baut, und beim Hauptgebäude einen Bauernhof bauen
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)
Drei Muster, auf die es ankommt:
- Nur trainieren, wenn
g.queue(home)leer ist. Gibst du jeden Tick einen Trainingsbefehl, füllst du die 7 Plätze der Warteschlange und bindest dein Gold (gemessen: 4 Bauern in der Warteschlange des Hauptgebäudes, 300 Gold gebunden – die Eröffnung wird deutlich langsamer). build_nearstatt fester Koordinaten. Es sucht von nah nach fern einen freien Platz und verfolgt das Ergebnis über mehrere Ticks; reicht das Geld nicht, tut es nichts. Feste Koordinaten liegen womöglich genau in einem Wald.- Keinen Bauern nehmen, der gerade baut. Ein Bauernhof der Menschen braucht 35 Sekunden; wird der Arbeiter unterwegs abgezogen, steht die Baustelle still.
Eine vollständige Version, die mit allen vier Völkern läuft, ist brains/examples/hello_bot.py: 5 Arbeiter pro Mine, bei voller Mine Holz fällen, stillstehende Baustellen weiterbauen.
Kaserne, Held und Angriff ergänzen
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]
# Held: Altar vorhanden, aber kein Held -> zuerst wiederbeleben, sonst trainieren (Helden sind einzigartig; einen toten neu trainieren wird abgelehnt)
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") # Heiliges Licht
# Die Kaserne produziert ständig Fußsoldaten (nur 1 in der Warteschlange)
for b in g.my_buildings({"hbar"}):
if not g.queue(b):
g.train(b, "hfoo")
# Bei voller Welle angreifen; nach schweren Verlusten zurück zur Basis
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)] # nur untätigen Einheiten Befehle geben
g.attack_move(idle, target.x, target.y)
Die vollständige Version findest du in brains/examples/rush_bot.py (erbt von hello_bot und baut Kaserne / Altar, falls sie fehlen).
Quittungen verstehen
Jeder Befehl liefert eine Quittung. if r: heißt „die Engine hat angenommen“; wenn nicht, steht in r.reason der Grund:
r = g.train(barracks, "hfoo")
if not r:
print(r.reason) # rejected(人口不够) (= nicht genug Nahrung)
print(r.verdict) # 3
Häufige Grundcodes: 3 nicht genug Nahrung, 8 nicht genug Gold, 9 nicht genug Holz, 32 Warteschlange voll, 183 Voraussetzung fehlt, 221 nicht vorhanden / im Bau / Held existiert bereits, 1001 Ziel nicht sichtbar. Die vollständige Tabelle findest du unter Quittungen und Grundcodes.
Angenommen ≠ erledigt
Die Quittung sagt nur, dass „die Engine diesen Befehl angenommen hat“. Auch einen Bauplatz im Wald nimmt die Engine sofort an, der Arbeiter scheitert erst, wenn er dort ankommt; Zauber können unterbrochen werden. Die Wirkung siehst du in Snapshot und Events: Zum Bauen build_near verwenden (es verfolgt, ob die Baustelle erscheint), bei Zaubern prüfen, ob g.cooldown() eine Abklingzeit zeigt.