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
- Bots : vos guerriers et la bibliothèque en lecture seule. COPIE sur un bot de la bibliothèque crée une copie modifiable.
- Éditeur : tapez du Redcode ; les erreurs s'affichent en bas. LANCER ouvre l'arène avec ce bot déjà sélectionné.
- Arène : remplissez les slots de bots (un bot seul est un essai,
+ BOTen ajoute), choisissez le nombre de manches, la vitesse, la graine. Observez le core : chaque pixel est une cellule, colorée selon le dernier guerrier qui y a écrit ; les cellules en cours d'exécution sont en surbrillance. - Sauvegardes : un match en cours est sauvegardé automatiquement ; reprenez-le plus tard ou partagez le fichier
.cdsave.
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
ADD.AB #4, target: ajoute 4 au champ B de target. Ce champ nous sert de pointeur.MOV.AB #0, @target:@signifie « aller là où pointe le champ B de target » ; on écrit 0 dans le champ B de cette cellule. C'est exactement le Dwarf du brouillon ICWS'94, et ce n'est pas une vraie bombeDAT: il ne met à zéro qu'un seul champ. Cela suffit à casser la plupart des codes (un Imp devientMOV.I 0, 0et reste bloqué sur place). La leçon 3 lance de vraies bombesDAT.JMP start: boucle.
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 :
MOV.A a, bcopie seulement le champ A.MOV.B a, bcopie seulement le champ B.MOV.AB a, bcopie le champ A de a dans le champ B de b.MOV.F a, bles deux champs.MOV.I a, bl'instruction entière, opcode compris.
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
ADD.AB #step, ptrfait avancer le pointeur.JMZ.B scan, @ptr: si le champ B de la cellule scannée est nul, on scanne à nouveau.- Sinon, on lâche la bombe et on continue.
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
<src: décrémente le champ B de src, puis lit la cellule pointée : la dernière ligne de Dwarf en premier.<dst: pareil côté destination.DJN boot, #len: décrémente le propre champ B duDJNet boucle tant qu'il n'est pas nul.JMP dwarf+1+dist: dwarf lui-même est leDAT; l'exécution commence à l'ADDqui le suit.- Les références de Dwarf sont relatives (
dwarf+1,@dwarf), la copie fonctionne donc telle quelle.
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
- Copiez les classiques de la bibliothèque et lisez-les ligne par ligne.
- Changez un seul nombre à la fois et relancez avec la même graine.
- Lisez les sections du Manuel sur les modificateurs par défaut et l'ordre d'évaluation : c'est là que se cachent la plupart des surprises.