Il s'agit d'une adaptation de Core War , une programmation KOTH datant du 20e siècle. Pour être plus précis, il utilise un ensemble d'instructions incroyablement simplifié basé principalement sur la proposition d'origine .
Contexte
Dans Core War, deux programmes se battent pour contrôler l'ordinateur. Le but de chaque programme est de gagner en localisant et en terminant le programme opposé.
La bataille se déroule dans la mémoire principale de l'ordinateur. Cette mémoire est appelée Core et contient 8192 adresses. Lorsque la bataille commence, le code de chaque compétiteur (appelé guerrier) est placé dans un morceau de mémoire aléatoire. L'exécution du programme alterne entre les guerriers, exécutant une instruction de chacun. Chaque instruction est capable de modifier une partie du Core, conduisant à la possibilité de programmes auto-modifiés.
Le but est de mettre fin au programme adverse. Un programme se termine lorsqu'il tente d'exécuter une instruction non valide, qui est n'importe quelle DAT
instruction.
L'ensemble d'instructions
Chaque programme se compose d'une série d'instructions de bas niveau, chacune prenant deux champs, appelés champs A et B.
Ce jeu d'instructions s'inspire largement des spécifications d'origine. Les principaux changements sont 1) des précisions sur l'ajout / la soustraction de commandes et 2) un changement du #
mode d'adressage pour permettre son utilisation n'importe où. La plupart des versions complètes de Core Wars ont plus de 20 opcodes, 8 modes d'adressage et un ensemble de "modificateurs d'instructions".
Opcodes
Chaque instruction doit avoir l'un des sept opcodes différents.
DAT A B
- (données) - Cela contient simplement les chiffresA
etB
. Surtout, un processus meurt lorsqu'il tente d'exécuter une instruction DAT.MOV A B
- (déplacer) - Ceci déplace le contenu de l'emplacementA
mémoire vers l'emplacement mémoireB
. Voici une démonstration d'avant-après:MOV 2 1 ADD @4 #5 JMP #1 -1
MOV 2 1 JMP #1 -1 JMP #1 -1
ADD A B
- (ajouter) - Ceci ajoute le contenu de l'emplacementA
mémoire à l'emplacement mémoireB
. Les deux premiers champs des deux sont ajoutés et les seconds champs sont ajoutés.ADD 2 1 MOV @4 #5 JMP #1 -1
ADD 2 1 MOV @5 #4 JMP #1 -1
SUB A B
- (soustraire) - Ceci soustrait le contenu de l'emplacement mémoireA
de (et stocke le résultat dans) l'emplacement mémoireB
.SUB 2 1 MOV @4 #5 JMP #1 -1
SUB 2 1 MOV @3 #6 JMP #1 -1
JMP A B
- (saut) - Aller à l'emplacementA
, qui sera exécuté au cycle suivant.B
doit être un nombre mais ne fait rien (vous pouvez cependant l'utiliser pour stocker des informations).JMP 2 1337 ADD 1 2 ADD 2 3
Le saut signifie que
ADD 2 3
sera exécuté le cycle suivant.JMZ A B
- (saut si zéro) - Si les deux champs de ligneB
sont à 0, alors le programme saute à l'emplacementA
.JMZ 2 1 SUB 0 @0 DAT 23 45
Comme les deux champs de l'instruction 1 sont 0, la commande DAT sera exécutée au tour suivant, entraînant une mort imminente.
CMP A B
- (comparer et ignorer s'ils ne sont pas égaux) - Si les champs des instructionsA
etB
ne sont pas égaux, ignorer l'instruction suivante.CMP #1 2 ADD 2 #3 SUB @2 3
Étant donné que les deux champs des instructions 1 et 2 ont une valeur égale, la commande ADD n'est pas ignorée et est exécutée au tour suivant.
Lorsque deux instructions sont ajoutées / soustraites, les deux champs (A et B) sont ajoutés / soustraits par paire. Le mode d'adressage et l'opcode ne sont pas modifiés.
Modes d'adressage
Il existe trois types de modes d'adressage. Chacun des deux champs d'une instruction possède l'un de ces trois modes d'adressage.
Immédiat
#X
-X
est la ligne à utiliser directement dans le calcul. Par exemple,#0
est la première ligne du programme. Les lignes négatives font référence aux lignes du noyau avant le début du programme.... //just a space-filler ... ADD #3 #4 DAT 0 1 DAT 2 4
Cela ajoutera la première des deux lignes DAT à la seconde, car celles-ci se trouvent respectivement sur les lignes 3 et 4. Cependant, vous ne voudriez pas utiliser ce code, car le DAT va tuer votre bot au cycle suivant.
Relative
X
- Le nombreX
représente l'emplacement d'une adresse de mémoire cible, par rapport à l'adresse actuelle. Le nombre à cet emplacement est utilisé dans le calcul. Si la ligne#35
est en cours d'exécution et contient-5
, alors la ligne#30
est utilisée.... //just a space-filler ... ADD 2 1 DAT 0 1 DAT 2 4
Cela ajoutera la deuxième ligne DAT à la première.
Indirect
@X
- Le nombreX
représente une adresse relative. Le contenu à cet emplacement est temporairement ajouté au numéro X pour former une nouvelle adresse relative, à partir de laquelle le numéro est récupéré. Si la ligne#35
est en cours d'exécution et que son deuxième champ l'est@4
et que le deuxième champ de la ligne#39
contient le nombre-7
, alors la ligne#32
est utilisée.... //just a space-filler ... ADD @1 @1 DAT 0 1 DAT 2 4
Cela ajoutera le premier DAT au second, mais d'une manière plus compliquée. Le premier champ est @ 1, qui obtient les données de cette adresse relative, qui est le premier champ du premier DAT, un 0. Ceci est interprété comme une deuxième adresse relative de cet emplacement, donc 1 + 0 = 1 donne le total décalage par rapport à l'instruction d'origine. Pour le deuxième champ, @ 1 obtient la valeur de cette adresse relative (le 1 dans le deuxième champ du premier DAT) et l'ajoute à lui-même de la même manière. Le décalage total est alors 1 + 1 = 2. Ainsi, cette instruction est exécutée de manière similaire à
ADD 1 2
.
Chaque programme peut contenir jusqu'à 64 instructions.
Lorsqu'un tour commence, les deux programmes sont placés de manière aléatoire dans une banque de mémoire avec 8192 emplacements. Le pointeur d'instruction pour chaque programme commence au début du programme et est incrémenté après chaque cycle d'exécution. Le programme meurt une fois que son pointeur d'instruction a tenté d'exécuter une DAT
instruction.
Paramètres du noyau
La taille de base est 8192, avec un délai d'attente de 8192 * 8 = 65536 ticks. Le cœur est cyclique, donc l'écriture à l'adresse 8195 équivaut à l'écriture à l'adresse 3. Toutes les adresses inutilisées sont initialisées à DAT #0 #0
.
Chaque concurrent ne doit pas dépasser 64 lignes. Les entiers seront stockés sous forme d'entiers signés 32 bits.
Analyse
Afin de faciliter la programmation pour les concurrents, j'ajouterai une fonction d'étiquette de ligne à l'analyseur. Tous les mots qui apparaissent sur une ligne avant un opcode seront interprétés comme des étiquettes de ligne. Par exemple, tree mov 4 6
a l'étiquette de ligne tree
. Si, n'importe où dans le programme, un champ contient tree
#tree
ou @tree
, un nombre sera substitué. De plus, la capitalisation est ignorée.
Voici un exemple de substitution d'étiquettes de ligne:
labelA add labelB @labelC
labelB add #labelC labelC
labelC sub labelA @labelB
Ici, les étiquettes A, B et C sont sur les lignes 0, 1 et 2. Les instances de #label
seront remplacées par le numéro de ligne de l'étiquette. Les instances de label
ou @label
sont remplacées par l'emplacement relatif de l'étiquette. Les modes d'adressage sont conservés.
ADD 1 @2
ADD #2 1
SUB -2 @-1
Notation
Pour chaque paire de concurrents, chaque bataille possible est effectuée. Étant donné que l'issue d'une bataille dépend des décalages relatifs des deux programmes, chaque décalage possible (environ 8 000 d'entre eux) est essayé. De plus, chaque programme a la possibilité de se déplacer en premier dans chaque décalage. Le programme qui remporte la majorité de ces offsets est le vainqueur de la paire.
Pour chaque paire gagnée par un guerrier, il obtient 2 points. Pour chaque égalité, un guerrier reçoit 1 point.
Vous êtes autorisé à soumettre plus d'un guerrier. Les règles typiques pour les soumissions multiples s'appliquent, telles que pas de tag-teaming, pas de coopération, pas de création de roi, etc.
Le controlle
Le code du contrôleur, avec deux exemples de robots simples, se trouve ici . Étant donné que cette compétition (lorsqu'elle est exécutée à l'aide des paramètres officiels) est complètement déterministe, le classement que vous créez sera exactement le même que le classement officiel.
Exemple Bot
Voici un exemple de bot qui montre certaines fonctionnalités du langage.
main mov bomb #-1
add @main main
jmp #main 0
bomb dat 0 -1
Ce bot fonctionne en effaçant lentement toutes les autres mémoires du noyau en le remplaçant par une "bombe". Puisque la bombe est une DAT
instruction, tout programme qui atteint une bombe sera détruit.
Il y a deux étiquettes de ligne, "principale" et "bombe" qui remplacent les numéros. Après le prétraitement, le programme ressemble à ceci:
MOV 3 #-1
ADD @-1 -1
JMP #0 0
DAT 0 -1
La première ligne copie la bombe sur la ligne immédiatement au-dessus du programme. La ligne suivante ajoute la valeur de la bombe ( 0 -1
) à la commande de déplacement, et elle montre également une utilisation du @
mode d'adressage. Cet ajout fait que la commande de déplacement pointe vers une nouvelle cible. La commande suivante retourne inconditionnellement au début du programme.
Classement actuel
24 - Turbo
22 - DwarvenEngineer
20 - HanShotFirst
18 - Dwarf
14 - ScanBomber
10 - Paranoid
10 - FirstTimer
10 - Janitor
10 - Evolved
6 - EasterBunny
6 - CopyPasta
4 - Imp
2 - Slug
Résultats par paire:
Dwarf > Imp
CopyPasta > Imp
Evolved > Imp
FirstTimer > Imp
Imp > Janitor
Imp > ScanBomber
Slug > Imp
DwarvenEngineer > Imp
HanShotFirst > Imp
Turbo > Imp
EasterBunny > Imp
Paranoid > Imp
Dwarf > CopyPasta
Dwarf > Evolved
Dwarf > FirstTimer
Dwarf > Janitor
Dwarf > ScanBomber
Dwarf > Slug
DwarvenEngineer > Dwarf
HanShotFirst > Dwarf
Turbo > Dwarf
Dwarf > EasterBunny
Dwarf > Paranoid
Evolved > CopyPasta
FirstTimer > CopyPasta
Janitor > CopyPasta
ScanBomber > CopyPasta
CopyPasta > Slug
DwarvenEngineer > CopyPasta
HanShotFirst > CopyPasta
Turbo > CopyPasta
CopyPasta > EasterBunny
Paranoid > CopyPasta
Evolved > FirstTimer
Evolved > Janitor
ScanBomber > Evolved
Evolved > Slug
DwarvenEngineer > Evolved
HanShotFirst > Evolved
Turbo > Evolved
EasterBunny > Evolved
Paranoid > Evolved
Janitor > FirstTimer
ScanBomber > FirstTimer
FirstTimer > Slug
DwarvenEngineer > FirstTimer
HanShotFirst > FirstTimer
Turbo > FirstTimer
FirstTimer > EasterBunny
FirstTimer > Paranoid
ScanBomber > Janitor
Janitor > Slug
DwarvenEngineer > Janitor
HanShotFirst > Janitor
Turbo > Janitor
Janitor > EasterBunny
Janitor > Paranoid
ScanBomber > Slug
DwarvenEngineer > ScanBomber
HanShotFirst > ScanBomber
Turbo > ScanBomber
ScanBomber > EasterBunny
ScanBomber > Paranoid
DwarvenEngineer > Slug
HanShotFirst > Slug
Turbo > Slug
EasterBunny > Slug
Paranoid > Slug
DwarvenEngineer > HanShotFirst
Turbo > DwarvenEngineer
DwarvenEngineer > EasterBunny
DwarvenEngineer > Paranoid
Turbo > HanShotFirst
HanShotFirst > EasterBunny
HanShotFirst > Paranoid
Turbo > EasterBunny
Turbo > Paranoid
Paranoid > EasterBunny
La dernière mise à jour (nouvelles versions de Turbo et Paranoid) a pris environ 5 minutes pour fonctionner sur un ancien ordinateur portable. Je tiens à remercier Ilmari Karonen pour ses améliorations au contrôleur . Si vous avez une copie locale du contrôleur, vous devez mettre à jour vos fichiers.