TUTORIAL
Willkommen bei Core War
Das ist ein Spiel für Programmierer. Du schreibst einen Bot in Redcode, einem winzigen Assembler, und setzt ihn in den Core: einen Ring aus 8000 Speicherzellen, den du mit den gegnerischen Bots teilst. Jeder Bot führt pro Zug einen Befehl aus. Es gibt keine Wände und keine Kampfregeln: Wer den Code des anderen überschreibt oder einfach länger am Leben bleibt, gewinnt die Runde. Die Arena zeigt den ganzen Core als Pixel, du siehst also in Echtzeit, wie dein Programm kriecht, bombardiert und scannt.
Die Lektionen beginnen mit einem Ein-Zeilen-Bot und enden mit einem Krieger, der zwischen den Runden die Strategie wechselt. Über die Lektionsliste kannst du jederzeit vorspringen oder zurückgehen. Jede Lektion enthält einen fertigen Bot: im Editor öffnen, laufen lassen, dann eine Zahl ändern und noch einmal laufen lassen.
Lektion 0: So wird gespielt
- Bots: die eigenen Krieger und die schreibgeschützte Bibliothek. Mit KOPIE lässt sich ein Bibliotheks-Bot als bearbeitbare Kopie anlegen.
- Editor: Redcode eintippen; Fehler erscheinen unten. LOS öffnet die Arena mit diesem Bot vorausgewählt.
- Arena: die Bot-Slots füllen (ein Bot allein ist ein Testlauf,
+ BOTfügt weitere hinzu), Runden, Tempo und Seed wählen. Dann den Core beobachten: Jeder Pixel ist eine Zelle, gefärbt nach dem Krieger, der zuletzt hineingeschrieben hat; hell leuchtende Zellen werden gerade ausgeführt. - Spielstände: Ein laufendes Match wird automatisch gesichert; später fortsetzen oder die
.cdsave-Datei teilen.
Lektion 1: Der Imp
Der Core ist ein Ring aus 8000 Zellen. Ein Prozess führt pro Zug eine Zelle aus und geht zur nächsten. MOV kopiert eine Zelle. Mit dem Modifikator .I kopiert er den ganzen Befehl, und #0 bedeutet „genau dieser Befehl“.
;redcode-94
;name Imp
;author Tutorial
;strategy Copies itself one cell forward, forever.
MOV.I #0, 1
END
In jedem Zug kopiert sich der Imp in die nächste Zelle und führt dann diese Kopie aus. So kriecht er durch den ganzen Core und überschreibt alles auf seinem Weg. Ein einzelnes DAT kann ihn nicht töten (er kopiert sich einfach darüber), aber er selbst tötet auch nie etwas: Meist endet es unentschieden.
Einfach gegen Dwarf aus der Bibliothek antreten lassen und die grüne Spur verfolgen.
Lektion 2: Dwarf, der Bomber
Ein DAT beendet den Prozess, der ihn ausführt. Dwarf wirft DATs auf jede vierte Zelle, daher wird jeder Krieger, der länger als 3 Zellen ist, irgendwann getroffen.
;redcode-94
;name Dwarf
;author Tutorial
;strategy Drops a DAT every 4 cells.
ORG start
target DAT.F #0, #0
start ADD.AB #4, target
MOV.AB #0, @target
JMP start
END
ADD.AB #4, target: 4 zum B-Feld von target addieren. Dieses Feld ist unser Zeiger.MOV.AB #0, @target:@bedeutet „dorthin, wohin das B-Feld von target zeigt“; 0 ins B-Feld dieser Zelle schreiben. Das ist genau der Dwarf aus dem ICWS'94-Entwurf, und er wirft keine echteDAT-Bombe: Er setzt nur ein Feld auf null. Das reicht trotzdem, um den meisten Code lahmzulegen (ein Imp wird zuMOV.I 0, 0und bleibt stehen). In Lektion 3 fliegen vollständigeDAT-Bomben.JMP start: Schleife.
Warum 4? 8000 ist durch 4 teilbar, also läuft der Zeiger 2000 Zellen weit und landet exakt wieder bei target, das ohnehin schon ein DAT ist. Dwarf bombardiert nie seine eigenen drei Codezeilen.
Lektion 3: Modifikatoren
Der Modifikator bestimmt, welche Felder bewegt werden. Zum Vergleich:
MOV.A a, bkopiert nur das A-Feld.MOV.B a, bkopiert nur das B-Feld.MOV.AB a, bkopiert das A-Feld von a ins B-Feld von b.MOV.F a, bbeide Felder.MOV.I a, bden ganzen Befehl, einschließlich Opcode.
Der Dwarf oben verwendet ADD.AB, damit die unmittelbare 4 (ein A-Feld) im B-Feld von target landet. Hier ein Dwarf, der mit MOV.I ganze DAT-Befehle wirft. Seine Bombe hat Prädekrement-Operanden: Führt ein gegnerischer Prozess sie aus, werden vor seinem Tod noch beide Operanden ausgewertet, sodass zwei weitere, weit entfernte Zellen ebenfalls beschädigt werden.
;redcode-94
;name Dwarf Plus
;author Tutorial
;strategy Full-instruction DAT bombs with decrementing fields.
ORG start
ptr DAT #0, #0
bomb DAT <2667, <5334
start ADD.AB #4, ptr
MOV.I bomb, @ptr
JMP start
END
Dwarf mehrmals mit demselben Seed gegen Dwarf Plus antreten lassen und vergleichen.
Lektion 4: Adressierungsarten und das Imp-Gate
Bei jedem Befehl werden beide Operanden ausgewertet, auch solche, die JMP ignoriert. < dekrementiert das B-Feld der adressierten Zelle, bevor es verwendet wird. Ein JMP, der ständig eine Zelle dekrementiert, ist ein Imp-Gate: Landet dort eine Imp-Kopie (MOV.I 0, 1), wird ihr B-Feld 0, sie kopiert sich also auf sich selbst, und der Prozess läuft in leeren Core und stirbt.
;redcode-94
;name Gated Dwarf
;author Tutorial
;strategy Dwarf whose loop instruction also gates the cell before it.
ORG start
gate DAT #0, #0
target DAT #0, #0
start ADD.AB #4, target
MOV.AB #0, @target
JMP start, <gate
END
Gated Dwarf gegen Imp antreten lassen: Der Imp stirbt jetzt meistens, statt ein Unentschieden zu erreichen (das Gate greift einmal pro Schleife, braucht also ein paar Durchläufe). Da target und start 1 bzw. 2 Zellen hinter gate liegen und die Bomben in Vielfachen von 4 ab target fallen, wird das Gate nie getroffen.
Lektion 5: SPL und der Imp-Ring
SPL startet einen zweiten Prozess. Die Prozesse eines Kriegers sind abwechselnd am Zug. Drei Imps im Abstand von 2667 Zellen (3 x 2667 = 8001, eins mehr als der Core) jagen einander und rücken alle drei Kopien um eine Zelle vor: ein Imp-Ring. Er ist viel schwerer abzufangen als ein einzelner Imp.
;redcode-94
;name Imp Ring
;author Tutorial
;strategy 3-point imp ring launched with SPL.
step EQU 2667
ORG start
start MOV.I imp, imp+step
MOV.I imp, imp+2*step
SPL imp+step
SPL imp+2*step
JMP imp
imp MOV.I #0, step
END
EQU definiert eine Konstante. imp+2*step ist ein Ausdruck, den der Assembler auswertet. Zum Ausprobieren: step = 4001 mit zwei Imps.
Lektion 6: Scanner
Ein Scanner sucht den Gegner, bevor er bombardiert. Leerer Core ist DAT 0, 0, ein B-Feld ungleich null bedeutet also „hier ist etwas“. JMZ springt, wenn das geprüfte Feld null ist.
;redcode-94
;name Scanner
;author Tutorial
;strategy Scan every 3039th cell for a non-zero B-field, bomb it.
step EQU 3039
ORG scan
ptr DAT #0, #0
bomb DAT #0, #0
scan ADD.AB #step, ptr
JMZ.B scan, @ptr
MOV.I bomb, @ptr
JMP scan
END
ADD.AB #step, ptrrückt den Zeiger vor.JMZ.B scan, @ptr: Ist das B-Feld der gescannten Zelle null, weiter scannen.- Sonst die Bombe abwerfen und weitermachen.
Gegen Dwarf findet der Scanner den Code des Bombers schnell. Gegen einen Imp verschwendet er Bomben auf dessen Spur. Ein echter Scanner verwendet eine ungerade Schrittweite, die keine Zelle zweimal besucht, und überspringt seinen eigenen Code.
Lektion 7: Bootstrapping
Der geladene Code ist ein feststehendes Ziel. Also den eigentlichen Krieger weit weg kopieren, dorthin springen und den Gegner seine Bomben am Starter verschwenden lassen. Eine MOV-Schleife mit Prädekrement-Zeigern kopiert rückwärts; DJN mit unmittelbarem Operanden zählt die Durchläufe im eigenen B-Feld.
;redcode-94
;name Boot Dwarf
;author Tutorial
;strategy Copies Dwarf 3000 cells away and runs the copy.
dist EQU 3000
len EQU 4
ORG boot
boot MOV.I <src, <dst
DJN boot, #len
JMP dwarf+1+dist
src DAT #0, dwarf+len
dst DAT #0, dwarf+len+dist
dwarf DAT.F #0, #0
ADD.AB #4, dwarf
MOV.AB #0, @dwarf
JMP dwarf+1
END
<src: das B-Feld von src dekrementieren, dann die Zelle lesen, auf die es zeigt: zuerst die letzte Zeile von Dwarf.<dst: dasselbe auf der Zielseite.DJN boot, #len: das eigene B-Feld desDJNdekrementieren, Schleife, solange es nicht null ist.JMP dwarf+1+dist: dwarf selbst ist dasDAT; die Ausführung beginnt beimADDdahinter.- Die Verweise in Dwarf sind relativ (
dwarf+1,@dwarf), daher funktioniert die Kopie unverändert.
Lektion 8: P-Space, Lernen zwischen Runden
Jeder Krieger behält 500 private Zellen über die Runden eines Matches hinweg. Zelle 0 verrät, wie die letzte Runde ausging: 0 = wir sind gestorben, 1 = wir haben gewonnen, 2 = Unentschieden. LDP lädt eine Zelle, STP speichert eine. Dieser Krieger merkt sich in Zelle 1, welche Strategie er gespielt hat, und wechselt nach jeder Niederlage.
;redcode-94
;name Switcher
;author Tutorial
;strategy Runs Dwarf or an imp ring; switches after a lost round.
step EQU 2667
ORG start
res DAT #0, #0
cur DAT #0, #0
start LDP.AB #0, res ; res.B = last result
LDP.AB #1, cur ; cur.B = strategy 0 or 1
JMN.B keep, res ; did not lose: keep it
MUL.AB #-1, cur ; toggle 0 <-> 1
ADD.AB #1, cur
keep STP.B cur, #1 ; remember it
JMZ.B dwarf, cur
MOV.I imp, imp+step
MOV.I imp, imp+2*step
SPL imp+step
SPL imp+2*step
JMP imp
imp MOV.I #0, step
target DAT.F #0, #0
dwarf ADD.AB #4, target
MOV.AB #0, @target
JMP dwarf
END
Ein Match über 10 Runden gegen Scanner starten und zusehen, wie die Strategie nach Niederlagen wechselt. Echte P-Space-Krieger („Pswitcher“) wählen aus mehreren Komponenten und machen einen großen Teil des modernen Turnierspiels aus.
Wie geht es weiter?
- Die Klassiker aus der Bibliothek kopieren und Zeile für Zeile lesen.
- Immer nur eine Zahl ändern und mit demselben Seed erneut laufen lassen.
- Im Handbuch die Abschnitte zu Standard-Modifikatoren und zur Auswertungsreihenfolge lesen; dort verstecken sich die meisten Überraschungen.