Esta é uma adaptação do Core War , uma programação KOTH que remonta ao século XX. Para ser mais específico, está usando um conjunto de instruções incrivelmente simplificado, baseado principalmente na proposta original .
fundo
No Core War, existem dois programas que lutam pelo controle do computador. O objetivo de cada programa é vencer localizando e encerrando o programa adversário.
A batalha ocorre na memória principal do computador. Essa memória é chamada de núcleo e contém 8192 endereços. Quando a batalha começa, o código de cada competidor (chamado de guerreiro) é colocado em um pedaço aleatório de memória. A execução do programa alterna entre guerreiros, executando uma instrução de cada um. Cada instrução é capaz de modificar uma parte do Core, levando à possibilidade de programas auto-modificáveis.
O objetivo é encerrar o programa adversário. Um programa termina quando tenta executar uma instrução inválida, que é qualquer DAT
instrução.
O conjunto de instruções
Cada programa consiste em uma série de instruções de baixo nível, cada uma das quais possui dois campos, chamados de campos A e B.
Este conjunto de instruções baseia-se fortemente nas especificações originais. As principais alterações são 1) esclarecimentos sobre a adição / subtração de comandos e 2) uma alteração no #
modo de endereçamento para permitir que seja usado em qualquer lugar. A maioria das versões completas do Core Wars possui mais de 20 opcodes, 8 modos de endereçamento e um conjunto de "modificadores de instruções".
Opcodes
Cada instrução deve ter um dos sete opcodes diferentes.
DAT A B
- (dados) - Isso simplesmente contém os númerosA
eB
. Importante, um processo morre quando tenta executar uma instrução DAT.MOV A B
- (mover) - Move o conteúdo da localização da memóriaA
para a localização da memóriaB
. Aqui está uma demonstração do antes e depois:MOV 2 1 ADD @4 #5 JMP #1 -1
MOV 2 1 JMP #1 -1 JMP #1 -1
ADD A B
- (adicionar) - Adiciona o conteúdo da localização da memória na localizaçãoA
da memóriaB
. Os dois primeiros campos de ambos são adicionados e os segundos são adicionados.ADD 2 1 MOV @4 #5 JMP #1 -1
ADD 2 1 MOV @5 #4 JMP #1 -1
SUB A B
- (subtrair) - Subtrai o conteúdo da localização da memóriaA
(e armazena o resultado) na localização da memóriaB
.SUB 2 1 MOV @4 #5 JMP #1 -1
SUB 2 1 MOV @3 #6 JMP #1 -1
JMP A B
- (pular) - Pula para o localA
, que será executado no próximo ciclo.B
deve ser um número, mas não faz nada (você pode usá-lo para armazenar informações).JMP 2 1337 ADD 1 2 ADD 2 3
O salto significa que
ADD 2 3
será executado no próximo ciclo.JMZ A B
- (pule se zero) - Se os dois campos da linhaB
forem 0, o programa saltará para o localA
.JMZ 2 1 SUB 0 @0 DAT 23 45
Como os dois campos da instrução 1 são 0, o comando DAT será executado no próximo turno, levando à morte iminente.
CMP A B
- (compare e pule se não for igual) - Se os campos nas instruçõesA
eB
não forem iguais, pule a próxima instrução.CMP #1 2 ADD 2 #3 SUB @2 3
Como os dois campos das instruções 1 e 2 são iguais em valor, o comando ADD não é ignorado e é executado no próximo turno.
Quando duas instruções são adicionadas / subtraídas, os dois campos (A e B) são adicionados / subtraídos aos pares. O modo de endereçamento e o código de operação não são alterados.
Modos de endereçamento
Existem três tipos de modos de endereçamento. Cada um dos dois campos de uma instrução possui um desses três modos de endereçamento.
Imediato
#X
-X
é a linha a ser usada diretamente no cálculo. Por exemplo,#0
é a primeira linha do programa. Linhas negativas referem-se a linhas no núcleo antes do início do programa.... //just a space-filler ... ADD #3 #4 DAT 0 1 DAT 2 4
Isso adicionará a primeira das duas linhas DAT à segunda, já que elas estão nas linhas 3 e 4, respectivamente. Você não gostaria de usar esse código, no entanto, porque o DAT matará seu bot no próximo ciclo.
Relativo
X
- O númeroX
representa a localização de um endereço de memória de destino, relativo ao endereço atual. O número neste local é usado na computação. Se a linha#35
estiver sendo executada e contiver-5
, a linha#30
será usada.... //just a space-filler ... ADD 2 1 DAT 0 1 DAT 2 4
Isso adicionará a segunda linha DAT à primeira.
Indireto
@X
- O númeroX
representa um endereço relativo. O conteúdo desse local é adicionado temporariamente ao número X para formar um novo endereço relativo, do qual o número é recuperado. Se a linha#35
estiver sendo executada e seu segundo campo for@4
, e o segundo campo da linha#39
contiver o número-7
, a linha#32
será usada.... //just a space-filler ... ADD @1 @1 DAT 0 1 DAT 2 4
Isso adicionará o primeiro DAT ao segundo, mas de uma maneira mais complicada. O primeiro campo é @ 1, que obtém os dados desse endereço relativo, que é o primeiro campo do primeiro DAT, um 0. Isso é interpretado como um segundo endereço relativo desse local, então 1 + 0 = 1 fornece o total deslocamento da instrução original. Para o segundo campo, @ 1 obtém o valor desse endereço relativo (o 1 no segundo campo do primeiro DAT) e o adiciona a si mesmo da mesma maneira. O deslocamento total é então 1 + 1 = 2. Portanto, esta instrução é executada de maneira semelhante a
ADD 1 2
.
Cada programa pode conter até 64 instruções.
Quando uma rodada inicia, os dois programas são colocados aleatoriamente em um banco de memória com 8192 localizações. O ponteiro de instruções para cada programa inicia no início do programa e é incrementado após cada ciclo de execução. O programa morre quando o ponteiro da instrução tenta executar uma DAT
instrução.
Parâmetros do núcleo
O tamanho do núcleo é 8192, com um tempo limite de 8192 * 8 = 65536 ticks. O núcleo é cíclico, portanto, escrever no endereço 8195 é o mesmo que escrever no endereço 3. Todos os endereços não utilizados são inicializados DAT #0 #0
.
Cada competidor não deve ter mais de 64 linhas. Os números inteiros serão armazenados como números inteiros assinados de 32 bits.
Análise
Para facilitar a programação para os concorrentes, adicionarei um recurso de etiqueta de linha ao analisador. Quaisquer palavras que ocorram em uma linha antes de um código de operação serão interpretadas como rótulos de linha. Por exemplo, tree mov 4 6
tem o rótulo de linha tree
. Se, em qualquer lugar do programa, houver um campo que contenha tree
#tree
ou @tree
, um número será substituído. Além disso, a capitalização é ignorada.
Aqui está um exemplo de como os rótulos de linha são substituídos:
labelA add labelB @labelC
labelB add #labelC labelC
labelC sub labelA @labelB
Aqui, os rótulos A, B e C estão nas linhas 0, 1 e 2. As instâncias de #label
serão substituídas pelo número da linha do rótulo. Instâncias label
ou @label
são substituídas pela localização relativa do rótulo. Os modos de endereçamento são preservados.
ADD 1 @2
ADD #2 1
SUB -2 @-1
Pontuação
Para cada par de competidores, todas as batalhas possíveis são realizadas. Como o resultado de uma batalha depende das compensações relativas dos dois programas, todas as compensações possíveis (cerca de 8.000 delas) são tentadas. Além disso, cada programa tem a chance de se mover primeiro em cada deslocamento. O programa que vence a maioria dessas compensações é o vencedor do par.
Para cada dupla que um guerreiro vencer, ele recebe 2 pontos. Para cada empate, um guerreiro recebe 1 ponto.
Você tem permissão para enviar mais de um guerreiro. Aplicam-se as regras típicas para vários envios, como nenhuma formação de equipes, cooperação, criação de rei etc. Não há realmente espaço para isso no Core War, portanto não deve ser um grande problema.
O controlador
O código para o controlador, junto com dois exemplos fáceis de bots, está localizado aqui . Como essa competição (quando realizada com as configurações oficiais) é completamente determinística, a tabela de classificação que você criar será exatamente a mesma que a tabela de classificação oficial.
Bot de exemplo
Aqui está um exemplo de bot que demonstra alguns recursos do idioma.
main mov bomb #-1
add @main main
jmp #main 0
bomb dat 0 -1
Esse bot opera apagando lentamente todas as outras memórias do núcleo, substituindo-as por uma "bomba". Como a bomba é uma DAT
instrução, qualquer programa que atinja uma bomba será destruído.
Existem duas etiquetas de linha, "principal" e "bomba", que servem para substituir os números. Após o pré-processamento, o programa fica assim:
MOV 3 #-1
ADD @-1 -1
JMP #0 0
DAT 0 -1
A primeira linha copia a bomba para a linha imediatamente acima do programa. A próxima linha adiciona o valor da bomba ( 0 -1
) ao comando move e também demonstra o uso do @
modo de endereçamento. Essa adição faz com que o comando move aponte para um novo destino. O próximo comando volta incondicionalmente para o início do programa.
Classificação atual
24 - Turbo
22 - DwarvenEngineer
20 - HanShotFirst
18 - Anão
14 - ScanBomber
10 - Paranoid
10 - FirstTimer
10 - Janitor
10 - Evolved
6 - EasterBunny
6 - CopyPasta
4 - Imp
2 - Slug
Resultados emparelhados:
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
A atualização mais recente (novas versões do Turbo e Paranoid) levou cerca de 5 minutos para ser executada em um laptop antigo. Gostaria de agradecer a Ilmari Karonen por suas melhorias no controlador . Se você possui uma cópia local do controlador, atualize seus arquivos.