MANUEL REDCODE
CoreDuel implémente la norme ICWS'94 avec les extensions pMARS (p-space, FOR/ROF, expressions EQU). Si vous connaissez pMARS, rien ici ne devrait vous surprendre.
0. De quoi s'agit-il
Vous écrivez de petits programmes, des bots, et vous les envoyez dans l'Arène affronter d'autres bots. Un bot s'écrit en Redcode, un langage proche de l'assembleur avec 19 instructions. Tous les bots d'un match sont chargés dans une mémoire commune, le core : un anneau de 8000 cellules (la fin rejoint le début) dans un ordinateur émulé très simple appelé MARS. Il n'y a rien d'autre dans cet ordinateur : pas d'écran, pas de fichiers, seulement des instructions dans des cellules et quelques processus qui les exécutent.
Chaque bot démarre avec un processus à une position aléatoire, à au moins 100 cellules des autres. L'émulateur donne la main aux bots à tour de rôle ; à son tour, un bot exécute une instruction d'un de ses processus. Un processus meurt en exécutant une instruction DAT (ou en divisant par zéro) ; un bot meurt avec son dernier processus. Comme la mémoire est partagée, on gagne en écrasant le code adverse avec des bombes DAT, en le cherchant par balayage du core ou en inondant le core de copies de soi-même, tout en gardant son propre code en vie.
Une manche se termine quand il ne reste qu'un bot, ou après 80000 cycles. Un match compte une ou plusieurs manches ; les survivants de chaque manche marquent des points et le meilleur total l'emporte. C'est tout le jeu : écrire un meilleur programme que l'adversaire.
Pour commencer :
- Bots : copiez un classique de la bibliothèque (Imp, Dwarf) ou écrivez-en un nouveau ; l'éditeur vérifie le code pendant la saisie.
- Arène : remplissez les slots de bots (un bot seul est un essai), réglez manches et vitesse, DÉMARRER, regardez le core.
- Tutoriel : huit courtes leçons qui construisent un bot fonctionnel pas à pas. Le reste de ce manuel est la référence du langage.
1. Le core
Le core est un anneau de 8000 cellules. Chaque cellule contient une instruction. Les adresses bouclent : cellule 7999 + 1 = cellule 0. Redcode n'a pas d'adresses absolues ; toute adresse est relative à l'instruction qui l'utilise.
Avant chaque manche, toutes les cellules valent DAT.F $0, $0. Les guerriers sont placés à des positions aléatoires, séparés d'au moins MINDISTANCE (100) cellules. Chaque guerrier démarre avec un seul processus, sur son ORG.
Les processus jouent à tour de rôle : le guerrier A exécute une instruction, puis le guerrier B, et ainsi de suite. Un guerrier qui a plusieurs processus les exécute en tourniquet, un par tour.
Un processus meurt quand il exécute un DAT ou quand il divise par zéro. Un guerrier meurt quand son dernier processus meurt. La manche se termine quand il ne reste qu'un guerrier, ou après MAXCYCLES (80000) cycles.
2. Structure d'un programme
;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
- Tout ce qui suit
;est un commentaire. Les lignes;name,;authoret;strategysont affichées dans la liste des bots et dans l'arène. ;assert EXPRdoit être vraie, sinon le guerrier est refusé. Servez-vous-en pour indiquer la taille de core pour laquelle vous l'avez réglé.- Le texte situé avant
;redcode(en-têtes de mail, prose) est ignoré. - Les labels sont en début de ligne. Un deux-points final est accepté (
start: ADD ...). Les labels sont sensibles à la casse. - Les opcodes, modificateurs et modes ne sont pas sensibles à la casse.
- Un programme compte au plus
MAXLENGTH(100) instructions.
3. Syntaxe des instructions
label OPCODE.MOD A-mode A-value, B-mode B-value ; comment
Chaque instruction a un opcode, un modificateur, un opérande A et un opérande B. Chaque opérande se compose d'un mode d'adressage et d'une valeur. Les valeurs sont des expressions (voir 8), évaluées relativement à l'adresse de l'instruction elle-même.
Si vous n'écrivez qu'un seul opérande, l'assembleur complète l'autre :
DAT xdevientDAT #0, x.- Pour tous les autres opcodes, l'opérande unique est A, et B devient
$0.
4. Modes d'adressage
Chaque opérande est résolu en pointeur (une adresse de cellule) avant l'exécution de l'opcode. Les deux opérandes sont toujours évalués, A d'abord, même quand l'opcode en ignore un. C'est pourquoi les effets de bord de pré/post-incrémentation se produisent même dans un JMP.
#immédiat. Le pointeur est l'instruction courante elle-même ; la valeur est utilisée comme un nombre.$direct (par défaut). Pointeur = ici + valeur.@indirect par B. Pointeur = (ici + valeur) + champ B de cette cellule.<indirect par B avec prédécrémentation. Décrémente d'abord le champ B de la cellule (ici + valeur), puis agit comme@.>indirect par B avec postincrémentation. Agit comme@, puis incrémente le champ B de cette cellule.*indirect par A. Comme@, mais via le champ A.{indirect par A avec prédécrémentation. Comme<, via le champ A.}indirect par A avec postincrémentation. Comme>, via le champ A.
Remarque : en ICWS'94, l'opérande immédiat a tout de même un pointeur, l'instruction elle-même. MOV.I #0, 1 copie l'instruction entière une cellule plus loin. C'est l'Imp classique.
5. Modificateurs
Le modificateur indique quels champs l'opcode lit et écrit. Pour une instruction de pointeur A a et de pointeur B b :
.Achamp A de a vers champ A de b..Bchamp B de a vers champ B de b..ABchamp A de a vers champ B de b..BAchamp B de a vers champ A de b..Fles deux champs, A vers A et B vers B..Xles deux champs croisés, A vers B et B vers A..Il'instruction entière, opcode et modes compris.MOVuniquement ; pour l'arithmétique,.Ise comporte comme.F.
Modificateur par défaut quand vous l'omettez :
DAT,NOP:.FMOV,CMP,SEQ,SNE:.ABsi A est immédiat,.Bsi B est immédiat, sinon.IADD,SUB,MUL,DIV,MOD:.ABsi A est immédiat,.Bsi B est immédiat, sinon.FSLT,LDP,STP:.ABsi A est immédiat, sinon.BJMP,JMZ,JMN,DJN,SPL:.B
6. Opcodes
DATTue le processus qui l'exécute. C'est aussi la cellule de données habituelle, et la bombe.MOVCopie de A vers B.ADDB = B + A (selon le modificateur).SUBB = B - A.MULB = B * A.DIVB = B / A. Une division par zéro tue le processus ; avec.F/.X/.I, l'autre champ est quand même écrit si son diviseur n'est pas nul.MODB = B mod A. Même règle pour zéro queDIV.JMPSaute au pointeur A.JMZSaute à A si le ou les champs B testés sont nuls. Avec.F/.X/.I, les deux champs doivent être nuls.JMNSaute à A si le ou les champs testés ne sont pas nuls. Avec.F/.X/.I: si l'un des deux n'est pas nul.DJNDécrémente le ou les champs testés de B, puis saute à A si le résultat n'est pas nul. Le compteur de boucle classique.SPLAjoute un nouveau processus au pointeur A, placé en file après les processus existants. Le processus courant continue à l'instruction suivante. Jusqu'àMAXPROCESSES(8000) par guerrier ; au-delà,SPLne fait rien.SEQSaute l'instruction suivante si A est égal à B (selon le modificateur).CMPen est un synonyme.SNESaute l'instruction suivante si A diffère de B.SLTSaute l'instruction suivante si A est inférieur à B (non signé, 0..7999).LDPLecture en p-space : la valeur de p-space[valeur A] est écrite dans la cible B.STPÉcriture en p-space : la valeur A est écrite dans p-space[valeur B].NOPNe fait rien (mais évalue quand même les deux opérandes).
Toute l'arithmétique se fait modulo CORESIZE. Les nombres du core sont toujours compris entre 0 et 7999 ; -1 est stocké sous la forme 7999.
7. Directives
ORG labelPoint de départ de l'exécution. Par défaut : la première instruction.END [label]Fin du source ; un label optionnel joue le rôle deORG.name EQU exprConstante textuelle : chaque utilisation ultérieure de name est remplacée par expr. Peut contenir un opérande avec son mode (EQU #7), voire un opcode.FOR n ... ROFRépète le bloc n fois. Un label sur la ligne duFOR, ou sur la ligne juste avant, sert de compteur (à partir de 1). Dans le bloc,&counterest remplacé par le compteur sur deux chiffres :label&idonnelabel01,label02, ... Les blocsFOR 0sont ignorés, pratique pour de longs commentaires.PIN nIdentité p-space. Deux guerriers ayant le mêmePINpartagent leur p-space (sauf la cellule 0).
ORG loop
loop FOR 3
MOV.I #0, &loop
ROF
8. Expressions
Opérateurs, du plus prioritaire au moins prioritaire : - + ! unaires, puis * / % (entiers, avec troncature), puis + -, puis les comparaisons == != < > <= >=, puis && et ||. Les parenthèses sont autorisées. Les comparaisons renvoient 1 ou 0.
Constantes prédéfinies : CORESIZE, MAXCYCLES, MAXPROCESSES, MAXLENGTH, MINDISTANCE, PSPACESIZE, ROUNDS, WARRIORS (2), VERSION (92), CURLINE (indice de l'instruction courante).
Dans une expression, un label vaut sa distance par rapport à l'instruction courante : label+1 désigne la cellule qui suit label, et start-target est un simple nombre de cellules.
9. P-space
Chaque guerrier dispose d'un tableau privé de PSPACESIZE (500) cellules qui persiste d'une manche à l'autre au cours d'un match. Les indices bouclent. La cellule 0 est spéciale et toujours privée : au début de chaque manche, elle contient le résultat précédent, 0 si le guerrier est mort, sinon le nombre de guerriers survivants (1 = victoire, 2 = égalité). Lors de la première manche, elle contient CORESIZE-1.
LDP.AB #0, x charge ce résultat dans le champ B de x. STP.B x, #1 stocke le champ B de x dans la cellule 1. Un guerrier peut s'en servir pour changer de stratégie après une défaite.
10. Règles du match
- Taille du core 8000, cycles max 80000, processus max 8000, longueur max 100, distance min 100, p-space 500.
- Un match se joue en N manches. Les positions de départ dépendent de la graine du match et du numéro de manche ; la même graine rejoue le même match.
- Score : chaque survivant d'une manche reçoit (W*W-1)/S points, W = nombre de guerriers, S = survivants ; les autres 0. À deux guerriers, cela fait 3 pour une victoire, 1 chacun pour une égalité, 0 pour une défaite.
- Jusqu'à 36 guerriers peuvent partager le core (les Réglages fixent la limite de slots) ; ils démarrent à au moins MINDISTANCE l'un de l'autre et le même bot peut occuper plusieurs slots.
- Le guerrier qui joue en premier alterne à chaque manche.
- Le mode test (un seul bot) indique SURVIVANT ou MORT à
MAXCYCLES.
11. Motifs courants
- Imp :
MOV.I #0, 1. Invulnérable aux bombes, mais ne gagne jamais seul. - Porte anti-imp :
JMP 0, <gate. Décrémente la cellule devant elle à chaque cycle, ce qui transforme les imps qui arrivent enMOV.I 0, 0. - Bombardier (Dwarf) : boucle
ADD,MOV,JMPqui lâche desDATavec un pas qui couvre tout le core. - Scanner : compare des cellules à zéro et bombarde ce qu'il trouve.
- Réplicateur (paper) : se recopie ailleurs avec une boucle
MOV, puis y lance unSPL. - Bombe SPL : un tapis de
SPL 0; un guerrier qui tombe dessus remplit sa file de processus de boucles inutiles. - Boot (amorçage) : copier le guerrier loin et exécuter la copie, pour que le code d'origine serve de leurre.