COREDUEL

REDCODE-HANDBUCH

CoreDuel implementiert den Standard ICWS'94 mit den pMARS-Erweiterungen (P-Space, FOR/ROF, EQU-Ausdrücke). Wer pMARS kennt, findet sich hier sofort zurecht.

0. Worum es geht

Du schreibst kleine Programme, Bots, und schickst sie in die Arena, wo sie gegen andere Bots kämpfen. Ein Bot ist in Redcode geschrieben, einer Assembler-ähnlichen Sprache mit 19 Befehlen. Alle Bots eines Matches werden in einen gemeinsamen Speicher geladen, den Core: einen Ring aus 8000 Zellen (das Ende geht in den Anfang über) in einem einfachen emulierten Rechner namens MARS. Mehr gibt es in diesem Rechner nicht: keinen Bildschirm, keine Dateien, nur Befehle in Zellen und ein paar Prozesse, die sie ausführen.

Jeder Bot startet mit einem Prozess an einer zufälligen Stelle, mindestens 100 Zellen von den anderen entfernt. Der Emulator gibt den Bots reihum einen Zug; in seinem Zug führt ein Bot einen Befehl eines seiner Prozesse aus. Ein Prozess stirbt, wenn er einen DAT-Befehl ausführt (oder durch null teilt); ein Bot stirbt mit seinem letzten Prozess. Weil sich alle den Speicher teilen, gewinnt man so: den Code des Gegners mit DAT-Bomben überschreiben, ihn durch Scannen des Cores aufspüren oder den Core mit Kopien von sich selbst fluten, und dabei den eigenen Code am Leben halten.

Eine Runde endet, wenn nur ein Bot übrig ist, oder nach 80000 Zyklen. Ein Match besteht aus einer oder mehreren Runden; die Überlebenden jeder Runde bekommen Punkte, die beste Summe gewinnt. Das ist das ganze Spiel: ein besseres Programm schreiben als der andere.

So geht es los:

1. Der Core

Der Core ist ein Ring aus 8000 Zellen. Jede Zelle enthält einen Befehl. Adressen laufen im Kreis: Zelle 7999 + 1 = Zelle 0. Absolute Adressen gibt es in Redcode nicht; jede Adresse ist relativ zu dem Befehl, der sie verwendet.

Vor jeder Runde ist jede Zelle DAT.F $0, $0. Die Krieger werden an zufälligen Positionen platziert, mindestens MINDISTANCE (100) Zellen voneinander entfernt. Jeder Krieger startet mit einem Prozess an seinem ORG.

Die Prozesse sind abwechselnd am Zug: Krieger A führt einen Befehl aus, dann Krieger B und so weiter. Hat ein Krieger mehrere Prozesse, laufen sie reihum (Round-Robin), einer pro Zug.

Ein Prozess stirbt, wenn er DAT ausführt oder durch null teilt. Ein Krieger stirbt, wenn sein letzter Prozess stirbt. Die Runde endet, wenn nur noch ein Krieger übrig ist, oder nach MAXCYCLES (80000) Zyklen.

2. Aufbau eines Programms

;redcode-94
;name Dwarf
;author A. K. Dewdney
;strategy Bombs every 4th cell.
;assert CORESIZE == 8000
        ORG     start
target  DAT.F   #0, #0
start   ADD.AB  #4, target
        MOV.AB  #0, @target
        JMP     start
        END

3. Befehlssyntax

label   OPCODE.MOD   A-mode A-value, B-mode B-value   ; comment

Jeder Befehl hat einen Opcode, einen Modifikator, einen A-Operanden und einen B-Operanden. Jeder Operand besteht aus einer Adressierungsart und einem Wert. Werte sind Ausdrücke (siehe 8), die relativ zur Adresse des Befehls selbst ausgewertet werden.

Wird nur ein Operand angegeben, ergänzt der Assembler den anderen:

4. Adressierungsarten

Jeder Operand wird zu einem Zeiger (einer Zelladresse) aufgelöst, bevor der Opcode ausgeführt wird. Beide Operanden werden immer ausgewertet, A zuerst, auch wenn der Opcode einen davon ignoriert. Deshalb treten die Nebeneffekte von Prä-/Post-Inkrement sogar bei JMP auf.

Hinweis: In ICWS'94 hat auch der unmittelbare Operand einen Zeiger, nämlich den Befehl selbst. MOV.I #0, 1 kopiert den ganzen Befehl eine Zelle weiter. Das ist der klassische Imp.

5. Modifikatoren

Der Modifikator legt fest, welche Felder der Opcode liest und schreibt. Für einen Befehl mit A-Zeiger a und B-Zeiger b:

Standard-Modifikator, wenn keiner angegeben ist:

6. Opcodes

Alle Arithmetik erfolgt modulo CORESIZE. Zahlen im Core liegen immer im Bereich 0..7999; -1 wird als 7999 gespeichert.

7. Direktiven

        ORG     loop
loop    FOR     3
        MOV.I   #0, &loop
        ROF

8. Ausdrücke

Operatoren, höchste Priorität zuerst: unäres - + !, dann * / % (ganzzahlig, abschneidend), dann + -, dann Vergleiche == != < > <= >=, dann && und ||. Klammern sind erlaubt. Vergleiche liefern 1 oder 0.

Vordefinierte Konstanten: CORESIZE, MAXCYCLES, MAXPROCESSES, MAXLENGTH, MINDISTANCE, PSPACESIZE, ROUNDS, WARRIORS (2), VERSION (92), CURLINE (Index des aktuellen Befehls).

Ein Label in einem Ausdruck steht für seinen Abstand zum aktuellen Befehl, label+1 ist also die Zelle nach label, und start-target ist schlicht eine Anzahl von Zellen.

9. P-Space

Jeder Krieger hat ein privates Array aus PSPACESIZE (500) Zellen, das zwischen den Runden eines Matches erhalten bleibt. Indizes laufen im Kreis. Zelle 0 ist besonders und immer privat: Zu Beginn jeder Runde enthält sie das vorige Ergebnis, 0, wenn der Krieger gestorben ist, sonst die Zahl der überlebenden Krieger (1 = Sieg, 2 = Unentschieden). In der ersten Runde enthält sie CORESIZE-1.

LDP.AB #0, x lädt dieses Ergebnis ins B-Feld von x. STP.B x, #1 speichert das B-Feld von x in Zelle 1. So kann ein Krieger nach einer Niederlage die Strategie wechseln.

10. Match-Regeln

11. Typische Muster