COREDUEL

TUTORIEL

Bienvenue dans Core War

C'est un jeu de programmation. Vous écrivez un bot en Redcode, un assembleur minuscule, et vous le lâchez dans le core : un anneau de 8000 cellules de mémoire partagé avec les bots ennemis. Chaque bot exécute une instruction par tour. Pas de murs, pas de règles d'engagement : celui qui écrase le code de l'autre, ou qui survit le plus longtemps, gagne la manche. L'Arène affiche tout le core en pixels : vous voyez votre programme ramper, bombarder et balayer en temps réel.

Les leçons commencent par un bot d'une ligne et finissent par un guerrier qui change de stratégie entre les manches. Vous pouvez sauter en avant ou revenir en arrière à tout moment grâce à la liste des étapes. Chaque leçon contient un bot complet : ouvrez-le dans l'éditeur, lancez-le, changez un nombre et relancez.

Leçon 0 : comment jouer

Leçon 1 : l'Imp

Le core est un anneau de 8000 cellules. Un processus exécute une cellule par tour, puis passe à la suivante. MOV copie une cellule. Avec le modificateur .I, il copie l'instruction entière, et #0 signifie « cette instruction-ci ».

;redcode-94
;name Imp
;author Tutorial
;strategy Copies itself one cell forward, forever.
        MOV.I   #0, 1
        END

À chaque tour, l'Imp se recopie dans la cellule suivante, puis exécute cette copie. Il rampe à travers tout le core en écrasant tout sur son passage. Un simple DAT ne peut pas le tuer (il se recopie par-dessus), mais il ne tue jamais rien par lui-même : la partie se termine généralement par une égalité.

Opposez-le à Dwarf, dans la bibliothèque, et regardez la traînée verte.

Leçon 2 : Dwarf, le bombardier

Un DAT tue le processus qui l'exécute. Dwarf lance des DAT sur une cellule sur quatre : tout guerrier de plus de 3 cellules finit donc par être touché.

;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

Pourquoi 4 ? 8000 est divisible par 4 : le pointeur parcourt 2000 cellules et revient exactement sur target, qui est déjà un DAT. Dwarf ne bombarde jamais ses trois lignes de code.

Leçon 3 : les modificateurs

Le modificateur choisit les champs déplacés. Comparez :

Le Dwarf ci-dessus utilise ADD.AB pour que la valeur immédiate 4 (un champ A) arrive dans le champ B de target. Voici un Dwarf qui lance des instructions DAT entières avec MOV.I. Sa bombe a des opérandes à prédécrémentation : quand un processus ennemi l'exécute, les deux opérandes sont quand même évalués avant qu'il meure, ce qui endommage aussi deux autres cellules, loin de là.

;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

Opposez plusieurs fois Dwarf à Dwarf Plus avec la même graine.

Leçon 4 : modes d'adressage et porte anti-imp

Les deux opérandes sont évalués à chaque instruction, même ceux que JMP ignore. < décrémente le champ B de la cellule visée avant de l'utiliser. Un JMP qui décrémente sans cesse une cellule forme une porte anti-imp : quand une copie d'Imp (MOV.I 0, 1) arrive dessus, son champ B passe à 0 ; elle se recopie alors sur elle-même, et le processus finit par tomber dans le core vide et meurt.

;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

Opposez Gated Dwarf à Imp : l'Imp meurt désormais le plus souvent au lieu d'obtenir l'égalité (la porte agit une fois par boucle, il lui faut donc quelques tours). Comme target et start sont situés 1 et 2 cellules après gate, et que les bombes tombent à des multiples de 4 à partir de target, la porte n'est jamais bombardée.

Leçon 5 : SPL et l'anneau d'imps

SPL lance un second processus. Les processus d'un même guerrier s'exécutent à tour de rôle. Trois imps espacés de 2667 cellules (3 x 2667 = 8001, soit une de plus que le core) se suivent et avancent d'une cellule toutes les trois copies : c'est un anneau d'imps. Il est bien plus difficile à arrêter avec une porte qu'un imp seul.

;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 définit une constante. imp+2*step est une expression, évaluée par l'assembleur. Essayez step = 4001 avec deux imps.

Leçon 6 : les scanners

Un scanner cherche l'ennemi avant de bombarder. Le core vide vaut DAT 0, 0 : un champ B non nul signifie donc « il y a quelque chose ici ». JMZ saute si le champ testé est nul.

;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

Contre Dwarf, le scanner trouve vite le code du bombardier. Contre un Imp, il gaspille ses bombes sur la traînée. Un vrai scanner utilise un pas impair qui ne repasse jamais sur la même cellule, et il saute par-dessus son propre code.

Leçon 7 : l'amorçage (boot)

Le code que vous chargez est une cible immobile. Copiez le vrai guerrier loin de là, sautez-y, et laissez l'ennemi gaspiller ses bombes sur le lanceur. Une boucle MOV avec des pointeurs à prédécrémentation copie à rebours ; un DJN avec une valeur immédiate compte les itérations dans son propre champ B.

;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

Leçon 8 : la p-space, apprendre d'une manche à l'autre

Chaque guerrier conserve 500 cellules privées tout au long des manches d'un match. La cellule 0 indique l'issue de la manche précédente : 0 = mort, 1 = victoire, 2 = égalité. LDP lit une cellule, STP en écrit une. Ce guerrier retient dans la cellule 1 la stratégie qu'il a jouée, et en change chaque fois qu'il a perdu.

;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

Lancez un match de 10 manches contre Scanner et regardez la stratégie basculer après chaque défaite. Les vrais guerriers à p-space (les « pswitchers ») choisissent parmi plusieurs composants et occupent une grande place dans les tournois modernes.

Pour aller plus loin