Esta es una adaptación de Core War , una programación KOTH que data del siglo XX. Para ser más específicos, está utilizando un conjunto de instrucciones increíblemente simplificado basado principalmente en la propuesta original .
Fondo
En Core War, hay dos programas que luchan por el control de la computadora. El objetivo de cada programa es ganar localizando y terminando el programa contrario.
La batalla tiene lugar dentro de la memoria principal de la computadora. Esta memoria se llama Core y contiene 8192 direcciones. Cuando comienza la batalla, el código para cada competidor (llamado guerrero) se coloca en un fragmento aleatorio de memoria. La ejecución del programa alterna entre guerreros, realizando una instrucción de cada uno. Cada instrucción es capaz de modificar una parte del Core, lo que lleva a la posibilidad de programas auto modificables.
El objetivo es terminar el programa contrario. Un programa finaliza cuando intenta ejecutar una instrucción no válida, que es cualquier DAT
instrucción.
El conjunto de instrucciones
Cada programa consta de una serie de instrucciones de bajo nivel, cada una de las cuales toma dos campos, llamados campos A y B.
Este conjunto de instrucciones se basa en gran medida de la especificación original. Los principales cambios son 1) aclaraciones sobre la suma / resta de comandos, y 2) un cambio del #
modo de direccionamiento para permitir que se use en cualquier lugar. La mayoría de las versiones completas de Core Wars tienen más de 20 códigos de operación, 8 modos de direccionamiento y un conjunto de "modificadores de instrucciones".
Opcodes
Cada instrucción debe tener uno de los siete códigos de operación diferentes.
DAT A B
- (datos) - Esto simplemente contiene los númerosA
yB
. Es importante destacar que un proceso muere cuando intenta ejecutar una instrucción DAT.MOV A B
- (mover): mueve el contenido de la ubicaciónA
de la memoria a la ubicación de la memoriaB
. Aquí hay una demostración de antes y después:MOV 2 1 ADD @4 #5 JMP #1 -1
MOV 2 1 JMP #1 -1 JMP #1 -1
ADD A B
- (agregar): agrega el contenido de la ubicaciónA
de la memoria a la ubicación de la memoriaB
. Se agregan los dos primeros campos de ambos, y se agregan los segundos campos.ADD 2 1 MOV @4 #5 JMP #1 -1
ADD 2 1 MOV @5 #4 JMP #1 -1
SUB A B
- (restar): resta el contenido de la ubicación de la memoriaA
(y almacena el resultado en) la ubicación de la memoriaB
.SUB 2 1 MOV @4 #5 JMP #1 -1
SUB 2 1 MOV @3 #6 JMP #1 -1
JMP A B
- (saltar): salta a la ubicaciónA
, que se ejecutará el próximo ciclo.B
debe ser un número pero no hace nada (sin embargo, puede usarlo para almacenar información).JMP 2 1337 ADD 1 2 ADD 2 3
El salto significa que
ADD 2 3
se ejecutará el próximo ciclo.JMZ A B
- (saltar si es cero) - Si ambos campos de líneaB
son 0, entonces el programa salta a la ubicaciónA
.JMZ 2 1 SUB 0 @0 DAT 23 45
Como los dos campos de la instrucción 1 son 0, el comando DAT se ejecutará el próximo turno, lo que provocará la muerte inminente.
CMP A B
- (comparar y omitir si no es igual) - Si los campos en las instruccionesA
yB
no son iguales, omita la siguiente instrucción.CMP #1 2 ADD 2 #3 SUB @2 3
Como los dos campos de las instrucciones 1 y 2 tienen el mismo valor, el comando AGREGAR no se omite y se ejecuta el próximo turno.
Cuando se suman / restan dos instrucciones, los dos campos (A y B) se suman / restan por pares. El modo de direccionamiento y el código de operación no cambian.
Modos de direccionamiento
Hay tres tipos de modos de direccionamiento. Cada uno de los dos campos de una instrucción tiene uno de estos tres modos de direccionamiento.
Inmediato
#X
:X
es la línea que se utilizará directamente en el cálculo. Por ejemplo,#0
es la primera línea del programa. Las líneas negativas se refieren a líneas en el núcleo antes del inicio del programa.... //just a space-filler ... ADD #3 #4 DAT 0 1 DAT 2 4
Esto agregará la primera de las dos líneas DAT a la segunda, ya que están en las líneas 3 y 4, respectivamente. Sin embargo, no querrá usar este código porque el DAT matará a su bot en el próximo ciclo.
Relativo
X
: el númeroX
representa la ubicación de una dirección de memoria de destino, en relación con la dirección actual. El número en esta ubicación se usa en el cálculo. Si la línea#35
se está ejecutando y contiene-5
, entonces#30
se usa la línea .... //just a space-filler ... ADD 2 1 DAT 0 1 DAT 2 4
Esto agregará la segunda línea DAT a la primera.
Indirecta
@X
: el númeroX
representa una dirección relativa. Los contenidos en esa ubicación se agregan temporalmente al número X para formar una nueva dirección relativa, de la que se recupera el número. Si#35
se está ejecutando la línea, y su segundo campo es@4
, y el segundo campo de la línea#39
contiene el número-7
, entonces#32
se usa la línea .... //just a space-filler ... ADD @1 @1 DAT 0 1 DAT 2 4
Esto agregará el primer DAT al segundo, pero de una manera más complicada. El primer campo es @ 1, que obtiene los datos de esa dirección relativa, que es el primer campo del primer DAT, un 0. Esto se interpreta como una segunda dirección relativa de esa ubicación, por lo que 1 + 0 = 1 da el total desplazamiento de la instrucción original. Para el segundo campo, @ 1 obtiene el valor de esa dirección relativa (el 1 en el segundo campo del primer DAT) y lo agrega a sí mismo de la misma manera. El desplazamiento total es entonces 1 + 1 = 2. Entonces, esta instrucción se ejecuta de manera similar a
ADD 1 2
.
Cada programa puede contener hasta 64 instrucciones.
Cuando comienza una ronda, los dos programas se colocan aleatoriamente en un banco de memoria con 8192 ubicaciones. El puntero de instrucción para cada programa comienza al comienzo del programa y se incrementa después de cada ciclo de ejecución. El programa muere una vez que su puntero de instrucción intenta ejecutar una DAT
instrucción.
Parámetros del núcleo
El tamaño del núcleo es 8192, con un tiempo de espera de 8192 * 8 = 65536 ticks. El núcleo es cíclico, por lo que escribir en la dirección 8195 es lo mismo que escribir en la dirección 3. Todas las direcciones no utilizadas se inicializan en DAT #0 #0
.
Cada competidor no debe tener más de 64 líneas. Los enteros se almacenarán como enteros con signo de 32 bits.
Analizando
Para facilitar la programación a los competidores, agregaré una función de etiqueta de línea al analizador. Cualquier palabra que aparezca en una línea antes de un código de operación se interpretará como etiquetas de línea. Por ejemplo, tree mov 4 6
tiene la etiqueta de línea tree
. Si, en cualquier parte del programa, hay un campo que contiene tree
#tree
o @tree
, se sustituirá un número. Además, se ignora la capitalización.
Aquí hay un ejemplo de cómo se sustituyen las etiquetas de línea:
labelA add labelB @labelC
labelB add #labelC labelC
labelC sub labelA @labelB
Aquí, las etiquetas A, B y C están en las líneas 0, 1 y 2. Las instancias de #label
serán sustituidas por el número de línea de la etiqueta. Las instancias de label
o @label
se sustituyen con la ubicación relativa de la etiqueta. Los modos de direccionamiento se conservan.
ADD 1 @2
ADD #2 1
SUB -2 @-1
Tanteo
Para cada par de concursantes, se realiza cada batalla posible. Dado que el resultado de una batalla depende de las compensaciones relativas de los dos programas, se intenta cada posible desplazamiento (aproximadamente 8000 de ellos). Además, cada programa tiene la oportunidad de moverse primero en cada desplazamiento. El programa que gana la mayoría de estas compensaciones es el ganador de la pareja.
Por cada pareja que gana un guerrero, se le otorgan 2 puntos. Por cada empate, un guerrero recibe 1 punto.
Puedes enviar más de un guerrero. Se aplican las reglas típicas para los envíos múltiples, como no trabajar en equipo, no cooperar, no hacer reyes, etc. De todos modos, en realidad no hay espacio para esto en Core War, por lo que no debería ser un gran problema.
El controlador
El código para el controlador, junto con dos bots de ejemplo fáciles, se encuentra aquí . Dado que esta competencia (cuando se ejecuta con la configuración oficial) es completamente determinista, la tabla de clasificación que cree será exactamente la misma que la tabla de clasificación oficial.
Ejemplo de bot
Aquí hay un ejemplo de bot que muestra algunas características del lenguaje.
main mov bomb #-1
add @main main
jmp #main 0
bomb dat 0 -1
Este bot funciona borrando lentamente toda la otra memoria en el núcleo al reemplazarlo con una "bomba". Como la bomba es una DAT
instrucción, cualquier programa que llegue a una bomba será destruido.
Hay dos etiquetas de línea, "principal" y "bomba" que sirven para reemplazar los números. Después del preprocesamiento, el programa se ve así:
MOV 3 #-1
ADD @-1 -1
JMP #0 0
DAT 0 -1
La primera línea copia la bomba en la línea inmediatamente superior al programa. La siguiente línea agrega el valor de la bomba ( 0 -1
) al comando de movimiento, y también demuestra el uso del @
modo de direccionamiento. Esta adición hace que el comando de movimiento apunte a un nuevo objetivo. El siguiente comando salta incondicionalmente al inicio del programa.
Tabla de clasificación actual
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
Resultados por pares:
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 última actualización (nuevas versiones de Turbo y Paranoid) tardó aproximadamente 5 minutos en ejecutarse en una computadora portátil vieja. Me gustaría agradecer a Ilmari Karonen por sus mejoras en el controlador . Si tiene una copia local del controlador, debe actualizar sus archivos.