COREDUEL

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

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

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:

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

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

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?