A guerra Nano Core


21

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 DATinstruçã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úmeros Ae B. Importante, um processo morre quando tenta executar uma instrução DAT.
  • MOV A B- (mover) - Move o conteúdo da localização da memória Apara a localização da memória B. 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ção Ada memória B. 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ória A(e armazena o resultado) na localização da memória B.

    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 local A, que será executado no próximo ciclo. Bdeve 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 3será executado no próximo ciclo.

  • JMZ A B- (pule se zero) - Se os dois campos da linha Bforem 0, o programa saltará para o local A.

    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ções Ae Bnã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.

  • RelativoX - O número Xrepresenta 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 #35estiver sendo executada e contiver -5, a linha #30será 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úmero Xrepresenta 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 #35estiver sendo executada e seu segundo campo for @4, e o segundo campo da linha #39contiver o número -7, a linha #32será 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 DATinstruçã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 6tem o rótulo de linha tree. Se, em qualquer lugar do programa, houver um campo que contenha tree #treeou @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 #labelserão substituídas pelo número da linha do rótulo. Instâncias labelou @labelsã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 DATinstruçã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.


O que acontece se dois robôs concorrentes tentarem usar o mesmo rótulo?
mbomb007

11
@ mbomb007 Os rótulos são coisa de pré-processamento e são calculados à medida que o arquivo de origem do bot está sendo analisado. Seus rótulos não interagirão com nenhum rótulo de concorrente.
PhiNotPi 9/03/2015

11
@ mbomb007 Para que os programas não se sobreponham. Além disso, não pretendo adicionar mais recursos a esta versão, exceto os do Micro Core War.
PhiNotPi

11
@ mbomb007 O endereçamento indireto faz referência ao mesmo campo que está fazendo a referência (1º ou 2º). Não há modificadores de instruções. Não estou baseando esse desafio fora do padrão de 94.
PhiNotPi 9/03/2015

2
@Thrax, vou dizer não, que você não está limitado a uma submissão. As regras típicas de envio múltiplo se aplicam (sem formação de equipes etc.), embora não haja muito espaço para cooperação nas principais guerras.
PhiNotPi 11/03/2015

Respostas:


9

Engenheiro Anão

Um anão novo e aprimorado. Ganha contra todo o resto enviado até agora. O tamanho ideal da etapa otimizada por corestep é provavelmente um exagero aqui.

        MOV bomb    @aim
aim     MOV bomb    @-6326
        SUB step    aim
step    JMZ #0      6328
        MOV 0       1
bomb    DAT 0       3164

Recursos notáveis ​​incluem o ciclo de bombardeio rápido que lança duas bombas em quatro ciclos, para uma velocidade média de 0,5 c no antigo jargão da Guerra do Núcleo, e o uso de JMZpara detectar quando a execução do bombardeio está concluída e é hora de mudar para o plano B ( aqui, um Imp).


Eu costumava jogar Core War nos anos 90 (alguns de vocês podem ter visto o guia básico que escrevi em 97), então pensei que seria interessante ver quais estratégias antigas do mundo RedCode '88 / '94 poderiam seja útil nesta variante.

Meus primeiros pensamentos foram:

  • Não há SPL, portanto, não há replicadores (e não há anéis / espirais). Isso deve fortalecer os bombardeiros. (Além disso, todas essas estratégias sofisticadas de bombardeio projetadas para lidar com replicadores e espirais de impaciais? Totalmente desnecessário e inútil aqui. Apenas bombardeie com qualquer DATs.)

  • Por outro lado, a CMPdigitalização ainda é potencialmente mais rápida que o bombardeio, portanto, um scanner rápido pode ter uma chance.

  • A ausência de in / decrements torna o core clear muito lento. De fato, um núcleo claro nessa variante é praticamente um bombardeiro com um tamanho de passo (abaixo do ideal) de ± 1. Novamente, isso também prejudica os scanners; uma estratégia de scanner único → bombardeiro pode funcionar, no entanto.

  • O Quickscanners / quickbombers (uma estratégia inicial do jogo usando um loop de varredura / bombardeio desenrolado, para aqueles que não são tão familiarizados com o jargão do Core War) ainda são potencialmente úteis, mas apenas contra programas longos (que eles mesmos são, então há uma espécie de feedback) efeito aqui). Difícil dizer se realmente vale a pena.

  • O sistema de pontuação é interessante. Os laços marcam metade dos pontos que uma vitória (em vez de 1/3, como na tradicional Guerra Central), tornando-os mais atraentes. Por outro lado, sobre o único programa que pode marcar muitos laços sob essas regras é um diabinho. (Além disso, a ausência de de / incrementos faz portões imp duro, diabinhos por isso mesmo simples realmente fazer ter uma chance de marcar um empate, se chegarem ao seu oponente vivo.)

  • Também, porque a classificação final depende apenas os programas que vencer, e não como muito você vencê-los por, ele tende a favorecer entradas generalistas. É melhor apenas vencer todos os seus oponentes do que destruir totalmente metade deles e apenas perder para o resto.

  • Como o código é público, sempre é possível encontrar um programa que possa superar qualquer envio anterior - possivelmente até vários deles - não importa quão bons eles sejam em geral. Porém, esses truques (como ajustar o tamanho do seu passo para acertar seu oponente antes de acertá-lo) podem parecer facilmente baratos. E, é claro, o jogador alvo sempre pode enviar uma nova versão com constantes diferentes.

De qualquer forma, o resultado disso é que eu decidi que deveria tentar escrever um bombardeiro rápido ou um scanner muito rápido, e talvez colocar um quickscanner / bombardeiro nele. Dessas opções, um bombardeiro rápido parecia o mais simples e com maior probabilidade de funcionar.

Nesse ponto, gastei muito tempo aprimorando e otimizando o código de intérprete do PhiNotPi, porque achei que provavelmente estaria executando muitos testes de força bruta para otimizar as constantes. Por acaso, eu nunca precisei fazer isso - o código acima é praticamente a primeira versão que realmente funcionou (depois de algumas tentativas fracassadas que cometeram suicídio devido a erros tolos).


O truque que torna meu bombardeiro rápido é usar endereços indiretos para lançar duas bombas para cada uma ADD. Veja como funciona:

  1. No primeiro ciclo, executamos MOV bomb @aim. Isso copia as bombinstruções para onde quer que esteja no núcleo o campo B dos aimpontos (inicialmente, exatamente 6326 instruções antes aimou 6328 instruções anteriores step; você verá por que esses números são importantes mais tarde).

  2. Na próxima etapa, executamos a aimprópria instrução! Na primeira passagem, ele se parece com isso: MOV bomb @-6326. Assim, ele copia bombpara o local para o qual o campo B da instrução em 6326 linhas antes aponta.

    Então, o que há nas 6326 linhas antes aim? Ora, é a cópia bombque acabamos de colocar lá um ciclo antes! Por acaso, organizamos as coisas para que o campo B bombtenha um valor diferente de zero, para que a nova bomba não seja copiada em cima da antiga, mas a alguma distância (na verdade, aqui a distância é 3164, que é metade do tamanho da etapa nominal 6328; mas outras compensações podem funcionar, talvez até melhor).

  3. No próximo ciclo, ajustamos nosso objetivo com SUB step aim, o qual subtrai os valores da stepinstrução (que também é o salto que vamos executar a seguir, embora possa ter sido apenas um simples DATlugar) aim.

    (Um detalhe a ser observado aqui é que tipo de desejar que o A-valor stepa ser zero, de modo que ainda vou jogar as mesmas bombas sobre a próxima iteração Mesmo que não seja estritamente necessário, embora;. Unicamente as bombas jogadas pela primeira instrução precisa ter seu campo B igual a 3164, o restante pode ser qualquer coisa.)

  4. Em seguida, a JMZverificação de que a instrução 6328 se afasta dela ainda é zero e, nesse caso, retorna à parte superior do código. Agora, 6328 é o tamanho da etapa do nosso bombardeiro e é divisível por 8 (mas não 16); assim, se continuássemos jogando bombas a cada 6328 passos, finalmente voltaríamos ao ponto em que começamos, bombardeando todas as oitavas instruções no núcleo (e com as bombas extras compensadas em 3163 = 6328/2 ≡ 4 (mod 8) , teríamos atingido a cada quarta instrução).

    Mas começamos nosso bombardeio com instruções 6328 antes da JMZ, e recuamos -6328 a cada iteração, por isso vamos bombardear o local 6328 etapas após a JMZapenas uma iteração antes de atingirmos a JMZsi própria. Portanto, quando JMZdetecta uma bomba nas instruções 6328, é um sinal de que cobrimos o máximo possível do núcleo sem nos acertarmos e devemos mudar para uma estratégia de backup antes de nos matarmos.

  5. Quanto à estratégia de backup, é apenas um velho MOV 0 1travesso, porque não consegui pensar em nada melhor por enquanto. Do meu ponto de vista, se bombardeamos todos os quartos locais do núcleo e ainda não vencemos, provavelmente estamos lutando contra algo muito pequeno ou muito defensivo, e também podemos tentar sobreviver e resolver um empate. Tudo bem, porque esses programas pequenos ou defensivos geralmente não são muito bons em matar qualquer outra coisa e, mesmo que apenas vencamos algumas lutas por acaso, provavelmente ainda estaremos à frente.


Ps. Caso alguém queira, aqui está o meu fork ligeiramente melhorado do código de torneio do PhiNotPi . É duas vezes mais rápido, salva os resultados das batalhas antigas para que você não precise executá-las novamente e corrige o que eu acredito ser um bug menor no cálculo dos resultados das batalhas. As alterações foram mescladas na versão principal pelo PhiNotPi. Obrigado!


11
Só para você saber, a pontuação testa TODAS as combinações possíveis de locais de início do programa e o programa que pontua mais pontos ganha. Isso torna os laços impossíveis ou completamente desfavoráveis, desde que um programa nunca se mate e bombardeie pelo menos um endereço uma vez;
mbomb007

9

Visualização de gráfico

Isso pode ser usado como uma ferramenta de depuração. Ele exibe o núcleo e mostra a localização do jogador. Para usá-lo, você deve chamá-lo a partir do código. Também forneci um modificado Game.javaque exibe automaticamente o GraphView.

PhiNotPi e Ilmari Karonen recentemente mudaram o Controller. Ilmari Karonen teve a gentileza de fornecer um GameView atualizado neste local .

import javax.swing.*;
import java.awt.*;

public class GameView extends JComponent{

    final static Color[] commandColors = new Color[]{
            Color.black, //DAT
            Color.blue,  //MOV
            Color.blue,  //ADD
            Color.blue,  //SUB
            Color.blue,  //JMP
            Color.blue,  //JMZ
            Color.blue,  //CMP
    };

    final static Color[] specialColors = new Color[]{
            new Color(0,0,0),
            new Color(190, 255, 152),
            Color.yellow,
            new Color(0, 93, 14),
            new Color(96, 92, 4),
            new Color(0, 93, 14),
            new Color(96, 92, 4),
            new Color(0, 93, 14),
            new Color(96, 92, 4)
    };

    final static Color playerOneColor = Color.green;
    final static Color playerTwoColor = Color.white;

    final Game game;

    int playerOneLocation;
    int playerTwoLocation;

    final static int width = 128;
    final static int height = 64;

    public GameView(Game game) {
        this.game = game;
    }

    @Override
    public void paint(Graphics g) {
        int pixelWidth = getSize().width;
        int pixelHeight = getSize().height;
        if (width > pixelWidth){
            pixelWidth = width;
            setSize(width, pixelHeight);
        }
        if (height > pixelHeight){
            pixelHeight = height;
            setSize(pixelWidth, height);
        }
        int squareWidth = Math.min(pixelWidth / width, pixelHeight / height);
        for (int x = 0; x < squareWidth * width; x += squareWidth){
            for (int y = 0; y < squareWidth * height; y += squareWidth){
                int index = (y / squareWidth) * width + (x / squareWidth);
                Color color = commandColors[game.core[index][0]];
                if (game.coreData[index] != 0){
                    color = specialColors[game.coreData[index]];
                }
                if (index == playerOneLocation){
                    color = playerOneColor;
                }
                if (index == playerTwoLocation){
                    color = playerTwoColor;
                }
                g.setColor(color);
                g.fillRect(x, y, squareWidth, squareWidth);
            }
        }
    }

    public void setLocations(int p1loc, int p2loc){
        this.playerOneLocation = p1loc;
        this.playerTwoLocation = p2loc;
    }
}

Game.java modificado:

import javax.swing.*;
import java.util.Random;
import java.util.ArrayList;
import java.util.Arrays;
/**
 * This runs a game of Core Wars between two players.  It can be called mutiple times.
 * 
 * @author PhiNotPi 
 * @version 3/10/15
 */
public class Game
{
    final Player p1;
    final Player p2;
    final int coreSize;
    final int coreSizeM1;
    final int maxTime;
    final int debug;
    public int[][] core;
    public int[] coreData; //Used in debugging.
    int offset1;
    int offset2;
    Random rand;
    ArrayList<int[]> p1code;
    ArrayList<int[]> p2code;
    int p1size;
    int p2size;
    GameView gameView;
    int time = 1000000; //Time in nanoseconds between frames
    public Game(Player A, Player B, int coreSize, int maxTime, int debug)
    {
        p1 = A;
        p2 = B;

        coreSize--;
        coreSize |= coreSize >> 1;
        coreSize |= coreSize >> 2;
        coreSize |= coreSize >> 4;
        coreSize |= coreSize >> 8;
        coreSize |= coreSize >> 16;
        coreSize++;

        this.coreSize = coreSize;
        this.coreSizeM1 = coreSize - 1;
        this.maxTime = maxTime / 2;
        this.debug = debug;
        core = new int[coreSize][5];
        rand = new Random();
        p1code =  p1.getCode();
        p1size = p1code.size();
        p2code =  p2.getCode();
        p2size = p2code.size();
        if (debug == 1){
            gameView = new GameView(this);
            JFrame frame = new JFrame("Game");
            frame.add(gameView);
            frame.setVisible(true);
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            frame.setSize(128, 64);
            coreData = new int[coreSize];
        }
    }

    public int runAll()
    {
        int sum = 0;
        for(int i = 0; i < coreSize - p1size - p2size; i++)
        {
            sum += run(i) - 1;
        }
        if(sum > 0)
        {
            return 1;
        }
        if(sum < 0)
        {
            return -1;
        }
        return 0;
    }

    public int run()
    {
        return run(rand.nextInt(coreSize - p1size - p2size + 1));
    }

    public int run(int deltaOffset)
    {
        core = new int[coreSize][5];
        //offset1 = rand.nextInt(coreSize);
        offset1 = 0;
        for(int i = 0; i != p1size; i++)
        {
            //System.arraycopy(p1.getCode().get(i), 0, core[(offset1 + i) % coreSize], 0, 5 );
            int[] line = p1code.get(i);
            int loc = (offset1 + i) & coreSizeM1;
            core[loc][0] = line[0];
            core[loc][1] = line[1];
            core[loc][2] = line[2];
            core[loc][3] = line[3];
            core[loc][4] = line[4];
            if (debug != 0){
                coreData[loc] = 1;
            }
        }
        offset2 = offset1 + p1size + deltaOffset;
        for(int i = 0; i != p2size; i++)
        {
            //System.arraycopy(p2.getCode().get(i), 0, core[(offset2 + i) % coreSize], 0, 5 );
            int[] line = p2code.get(i);
            int loc = (offset2 + i) & coreSizeM1;
            core[loc][0] = line[0];
            core[loc][1] = line[1];
            core[loc][2] = line[2];
            core[loc][3] = line[3];
            core[loc][4] = line[4];
            if (debug != 0){
                coreData[loc] = 2;
            }
        }

        int p1loc = offset1 & coreSizeM1;
        int p2loc = offset2 & coreSizeM1;
        for(int time = 0; time != maxTime; time++)
        {
            if(debug != 0)
            {
                //printCore(p1loc,p2loc);
                //System.out.println("p1loc " + p1loc);
                //System.out.println("offset " + offset1);
                gameView.setLocations(p1loc, p2loc);
                gameView.repaint();
                try {
                    Thread.sleep(time / 1000000, time % 1000000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            if(core[p1loc][0] == 0)
            {
                return 0;
            }
            p1loc = execute(p1loc, offset1, 1);

            if(debug != 0)
            {
                //printCore(p1loc,p2loc);
                //System.out.println("p2loc " + p2loc);
                //System.out.println("offset " + offset2);
                gameView.setLocations(p1loc, p2loc);
                gameView.repaint();
                /*try {
                    Thread.sleep(time);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
            }
            if(core[p2loc][0] == 0)
            {
                return 2;
            }
            p2loc = execute(p2loc, offset2, 2);

        }
        return 1;
    }
    public int execute(int ploc, int offset, int player)
    {
        int line1 = offset + core[ploc][3];
        if(core[ploc][1] != 0)
        {
            line1 += ploc - offset;
        }
        if(core[ploc][1] == 2)
        {
            line1 += core[line1 & coreSizeM1][3];
        }
        int line2 = offset + core[ploc][4];
        if(core[ploc][2] != 0)
        {
            line2 += ploc - offset;
        }
        if(core[ploc][2] == 2)
        {
            line2 += core[line2 & coreSizeM1][4];
        }
        line1 = line1 & coreSizeM1;
        line2 = line2 & coreSizeM1;
        int opcode = core[ploc][0];
        ploc = (ploc + 1) & coreSizeM1;
        //String opDescription = "";
        if(opcode == 1)
        {
            core[line2][0] = core[line1][0];
            core[line2][1] = core[line1][1];
            core[line2][2] = core[line1][2];
            core[line2][3] = core[line1][3];
            core[line2][4] = core[line1][4];
            if (debug != 0) {
                coreData[line2] = player + 2;
            }
            return ploc;
            //opDescription = "Moved from " + line1 + " to " + line2;
        }
        if(opcode == 2)
        {
            core[line2][3] += core[line1][3];
            core[line2][4] += core[line1][4];
            if (debug != 0) {
                coreData[line2] = player + 4;
            }
            return ploc;
            //opDescription = "Added " + line1 + " to " + line2;
        }
        if(opcode == 3)
        {
            core[line2][3] -= core[line1][3];
            core[line2][4] -= core[line1][4];
            if (debug != 0) {
                coreData[line2] = player + 6;
            }
            return ploc;
                //opDescription = "Subtracted " + line1 + " to " + line2;
        }
        if(opcode == 4)
        {
            ploc = line1;
            return ploc;
                //opDescription = "Jumped to " + line1;
        }
        if(opcode == 5)
        {
                if(core[line2][3] == 0 && core[line2][4] == 0)
                {
                    ploc = line1;
                    //opDescription = "Jumped to " + line1;
                }
                else
                {
                    //opDescription = "Did not jump to " + line1;
                }
                return ploc;
        }
        if(opcode == 6)
        {
            if(core[line1][3] == core[line2][3] && core[line1][4] == core[line2][4])
            {
                //opDescription = "Did not skip because " + line1 + " and " + line2 + " were equal.";
            }
            else
            {
                ploc = (ploc + 1) & coreSizeM1;
                //opDescription = "Skipped because " + line1 + " and " + line2 + " were not equal.";
            }
            return ploc;
        }
        if(debug != 0)
        {
            //System.out.println(opDescription);
        }
        return ploc;
    }
    /*public void printCore(int p1loc, int p2loc)
    {
        int dupCount = 0;
        int[] dupLine = new int[]{0,0,0,0,0};
        for(int i = 0; i < core.length; i++)
        {
            int[] line = core[i];
            if(Arrays.equals(line, dupLine) && i != p1loc && i != p2loc)
            {
                if(dupCount == 0)
                {
                    System.out.println(Player.toString(line));
                }
                dupCount++;
            }
            else
            {
                if(dupCount == 2)
                {
                    System.out.println(Player.toString(dupLine));
                }
                else if(dupCount > 2)
                {
                    System.out.println("    " + (dupCount - 1) + " lines skipped.");
                }
                System.out.println(Player.toString(line));
                if(i == p1loc)
                {
                    System.out.print(" <- 1");
                }
                if(i == p2loc)
                {
                    System.out.print(" <- 2");
                }
                dupLine = line;
                dupCount = 1;
            }
        }
        if(dupCount == 2)
        {
            System.out.println(Player.toString(dupLine));
        }
        else if(dupCount > 2)
        {
            System.out.println("    " + (dupCount - 1) + " lines skipped.");
        }
    }*/
}

Parece que você também fez uma modificação no Player. Eu recebo./Game.java:275: error: method toString in class Object cannot be applied to given types; System.out.println(Player.toString(line)); ^ required: no arguments found: int[]
AShelly 13/03/2015

@ AShelly Desculpe por isso. Eu deveria ter comentado o printCore()método.
TheNumberOne

9

Turbo

main   add three target
test   jmz -1 @target
bomb   mov three @target
       sub j1 target 
       mov jump @target
       sub j1 target 
       mov copy @target
       sub j1 target
two    mov decr @target
j1     jmp @target 1
target dat -8 -8   
decr   sub #two 3
copy   mov 2 @2
jump   jmp -2 0
three dat -9 -9

Minha segunda tentativa de sempre no CoreWar. Projetado para vencer Dwarf. Procura dados de 3 em 1 e coloca uma bomba a cada 2. Cada estágio é executado em apenas 3 instruções, na esperança de que as bombas de Dwarf não atinjam.

NOVO Turbo ++ : Agora aprimorado. Ele digitaliza para trás até encontrar dados, depois se move para lá e depois bombeia para trás. A esperança é que o golpe derrube o oponente ou seja para um local já bombardeado e, portanto, seguro (ish).

... E uma edição para torná-lo mais escasso faz com que supere todos!


Parece bater muito mais do que apenas anão. Parabéns! Eu acho que você poderia alcançar o terceiro lugar se você também pudesse vencer Imp.
Ilmari Karonen 19/03/2015

Eu atualizei este, mas na verdade é uma evolução bastante grande da anterior. Eu deveria ter feito uma nova entrada?
AShelly

Não pretendo falar pelo PhiNotPi, mas acho que depende de você. Fazer uma atualização no local significa basicamente retirar sua entrada antiga. De qualquer forma, ainda mais parabéns por ter conseguido esquivar-se com sucesso do caminho para o terceiro lugar! Eu acho que a sua é a única entrada até agora para vencer o DwarvenEngineer aos pares.
Ilmari Karonen 19/03/2015

Bem feito ;). você é o único a vencer agora!
Hit

8

Anão

Um programa comum e simples que representa um anão jogando pedras. Ele coloca uma DATinstrução a cada quatro endereços.

add 2 3
mov 2 @2
jmp -2 #4
dat #0 #4

EDIT: corrige o endereçamento. Aparentemente, os modos de endereçamento são diferentes das especificações às quais o OP está vinculado.


Eu acho que é "adicione # 3 3" para a primeira linha, não é?
Hit

@Hit Nope. Eu quero acertar todos os endereços 4. Eu poderia usar add 3 3, mas depois dobraria cada loop em vez de adicionar, e isso não seria útil. #4é um imediato, portanto, adiciona o número 4ao segundo valor no endereço que fica 3após o endereço atual.
mbomb007

Eu acho que você está interpretando mal o #modo de endereçamento no desafio. Conforme declarado na especificação, fiz uma alteração no #modo de endereçamento.
PhiNotPi 11/03/2015

Você deve ir como: "add 2 3 mov 2 @ 2 jmp -2 4 dat 0 4"
Clique em

Com o comportamento correto ainda derrotas evoluiu
Hit

7

Evoluído

Sinceramente, não entendo como isso funciona. Parece construir seu código fonte antes de fazer qualquer coisa. Eu adoraria se alguém me desse uma explicação sobre como funciona.

Depois de estudá-lo, descobri que é simplesmente um anão modificado com um guarda-costas. Em vez de bombardear os inimigos com DATinstruções, embaralha o código dos inimigos. Também bombardeia a cada dois registros em vez de a cada quatro registros. Com tempo suficiente, sem dúvida se destruiria.

MOV -2 #-1
MOV #4 -9
SUB -5 #6
MOV #1 1
MOV #-6 #4
SUB @8 @7
JMP -3 @4
DAT #-4 8
JMP -1 9
JMP 5 #-10
CMP @-1 #0
SUB 3 #-10
JMP @10 #-9
JMZ #1 10
MOV #3 2
ADD @9 @-3
CMP #-3 @7
DAT @0 @-2
JMP @-7 #6
DAT @-8 -6
MOV @0 #9
MOV #2 1
DAT @6882 #-10
JMP @3 4
CMP @8 2
ADD -7 @11
ADD @1 #-9
JMZ @-5 7
CMP 11 5526
MOV @8 6
SUB -6 @0
JMP 1 11
ADD @-3 #-8
JMZ @-14 @-5
ADD 0 @-8
SUB #3 @9
JMP #-1 5
JMP #9 @1
CMP -9 @0
SUB #4 #-2
JMP #-8 5
DAT -1 @-10
MOV 6 #2
CMP @-11 #-14
ADD @4 @-3
MOV @5 #-6
SUB -3 -2
DAT @-10 #-1
MOV #-13 #-6
MOV #1 5
ADD 5 #-5
MOV -8 @-1
DAT 0 10
DAT #5 #7
JMZ 6 -5
JMZ -12 -11
JMP 5 @-7
MOV #7 -3
SUB #-7 @-3
JMP -4 @-11
CMP @-5 #-2
JMZ @-1 #0
ADD #3 #2
MOV #5 @-6

11
Então, onde você conseguiu isso?
PyRulez

4
@PyRulez É gerado por computador através de algoritmo genético.
TheNumberOne

11
Parece que a execução não progride mais do que a linha 6, porque aí ela volta ao programa. Acredito que a razão disso é que há mais movimentos / loop do que seus concorrentes.
PhiNotPi 11/0315

6

Primeiro tempo

Se funcionar, deve tentar tomar posição no início do núcleo e criar uma defesa

main MOV 5 #0
     ADD #data #main
     CMP #main #max
     JMP #0 0
     JMP #main 0
     MOV #data #100
     ADD #data -1
     JMP -2 0
data DAT 1 1
max  DAT 8 3

Não funciona da maneira que eu acho que você imaginou: #0refere-se ao início do seu programa (ou seja, o mesmo que #main), não ao início do núcleo (que não é realmente um conceito significativo de qualquer maneira - o núcleo é circular, seu código não pode dizer onde começa ou termina). O que acontece é que sua primeira instrução ( main) se sobrescreve com a MOV #data #100, após a qual seu código efetivamente se transforma em um núcleo avançado de 0,25c (= uma instrução por quatro ciclos).
Ilmari Karonen

@IlmariKaronen Oh, obrigado pela explicação. Eu confundi #0com o início do núcleo. As 5 primeiras instruções são completamente inúteis então.
Thrax

6

CopyPasta

Nunca participou de um CoreWar, este programa simples está apenas tentando copiar e colar e depois executar a cópia. Pode não ter o comportamento correto, por favor, diga-me se for o caso.

É pacifista demais e não pode vencer de fato.

MOV 6 0
MOV @-1 @-1
CMP @-2 3
JMP 4242 0
SUB -3 -4
JMP -4 0
DAT 0 4244

Esta edição atual provavelmente não estará na próxima atualização do placar (estou executando o torneio no momento). A versão antiga, no entanto, estava obtendo os resultados preliminares (tamanho pequeno do núcleo).
PhiNotPi 10/03/2015

Ok :) A versão mais antiga não sai do loop1, não é realmente o comportamento desejado, estou tentando corrigir isso.
Hit

A versão atual parece quebrada. Ainda não sei por que.
PhiNotPi 10/03/2015

11
Atualizei as ferramentas de depuração para poder diagnosticar o problema. O que está acontecendo é que o programa copia apenas a segunda metade de si (começando em JMP loop 0). Então, quando ele pula para onde o início da cópia deve estar, é apenas espaço vazio e perde.
PhiNotPi 10/03/2015

2
Por favor, ignore meu comentário anterior (agora excluído); Eu testei uma versão incorreta do seu código (ironicamente, devido a um erro de copiar e colar), e foi por isso que funcionou tão mal para mim.
Ilmari Karonen 18/03/2015

6

Zelador

Ele deve verificar se os seguintes endereços estão vazios e, se não estiverem, os limpa (assim, espero, apagando o bot do oponente).

Editar: Esta nova versão deve ser mais rápida (agora que entendi o JMZcomando e a @referência corretamente).

JMZ 2 6
MOV 4 @-1
ADD 2 -2
JMP -3 0
DAT 0 1
DAT 0 0

O zelador não está cometendo suicídio com o primeiro JMZ? Deve ser pelo menos JMZ 2 8. A propósito, usando @ você pode reduzir os dois add para apenas um. Algo como: "JMZ 2 @ 5 MOV 5 @ 4 ADD 2 3 JMP -3 0 0 1 DAT DAT DAT 2 0 0 0" (não testada)
Hit

@ Hit Não pula, porque o endereço 2 de lá existe ADD 3 -2, mas você está certo de que ele deve mudar isso, eu acho.
mbomb007

Sim, eu li mal as instruções JMZe pensei que JMZ A Bestava checando Ae pulando para Bse 0 quando aparentemente é o contrário. Obrigado por reparar, porque eu não :)
plannapus

5

ScanBomber

Remova meus comentários antes de compilar. Verifica por um tempo e depois bombardeia quando encontra um programa. Provavelmente ainda vai perder para o meu anão.

scan add #eight #range  ; scan
jmz #scan @range
sub #six #range
fire mov #zero @range   ; bombs away! (-6)
add #two #range
mov #zero @range
add #two #range
mov #zero @range
add #two #range
mov #zero @range        ; (+0)
add #two #range
mov #zero @range
add #two #range
mov #zero @range
add #two #range
mov #zero @range
add #two #range
mov #zero @range        ; (+8)
range jmp #scan 6
two dat 0 2
six dat 0 6
zero dat 0 0
eight dat 0 8

O OP foi definido de maneira #completamente diferente da especificação (leia o link ao qual ele se vinculou). Ainda tenho que corrigir esse programa.
mbomb007

@TheBestOne Acho que consertei. Parece que faz sentido agora? Ou preciso colocar #antes de qualquer referência zero? Sim, eu acho que eu preciso ...
mbomb007

Funciona bem agora. Ele supera todos os bot, exceto Dwarf e Imp.
TheNumberOne

O @TheBestOne Dwarf é muito pequeno e só seria detectado em 50% da possível veiculação do programa. Provavelmente só perde para o Imp, porque se bombardeia após vasculhar toda a memória.
mbomb007

5

Han Shot First (v2)

Imaginei que a competição poderia usar um pouco mais de diversidade, então aqui está a minha segunda entrada: uma tentativa CMP scanner de .

Esta é a versão 2 , com defesas anti-Imp aprimoradas - agora pode derrotar Imp, mesmo que apenas por um ponto. Ainda perde para o Dwarven Engineer, mas supera tudo o mais até agora, colocando-o atualmente em primeiro lugar.

scan    ADD bomb    aim
aim     CMP 17      12
        JMZ scan    #-3
loop    MOV bomb    @aim
        ADD step    aim
step    JMP loop    #2
bomb    DAT 10      10

Ele funciona comparando os locais centrais adjacentes separados por 5 etapas, em intervalos de 10 etapas, até encontrar uma diferença. Quando isso acontece, ele começa a lançar bombas em intervalos de 2 etapas até matar seu oponente ou dar laços em volta do núcleo para alcançar a si próprio.

Se a verificação não encontrar mais nada, ela eventualmente retornará e encontrará seu próprio código e o atacará. Isso seria suicídio, mas pela feliz coincidência de que a primeira bomba caia diretamente sobre oaim linha, fazendo com que a próxima bomba fosse lançada 12 posições (em vez das 2 usuais) no centro, pulando o código de maneira conveniente. (Isso também acontece com 50% de probabilidade se a varredura encontrar alguma coisa, mas falhar em matar o oponente.) Como o tamanho do núcleo é múltiplo de dois, isso também continuará acontecendo se a execução do bombardeio girar, eliminando a necessidade de um estratégia de backup adicional.

(Esse truque de auto-bombardeio era originalmente uma pura coincidência - eu havia planejado uma maneira completamente diferente de mudar do modo de varredura para o modo de bombardeio se nada fosse encontrado, mas quando testei o código pela primeira vez, as constantes estavam certas para fazê-lo trabalhar desta maneira, e eu decidi ficar com isso.)



4

Slug

     mov    ones    @-1024
     mov    from    -3
     mov    here    -3
loop mov    @-5 @-4
     add    ones  -5
     jmz    -17 -6
     add    ones  -8    
     jmp    loop    42
ones dat    1   1
from dat    2   2
here dat    -11 -11

Rasteja pelo espaço de memória para trás. Ocasionalmente joga uma bomba longe.


3

coelhinho da Páscoa

Ele gosta de pular para trás :)

loop mov 0 -10
     add data loop
     cmp -7 data
     jmp -13 0
     jmp loop 0
data dat 1 1

3

Paranóico

É como uma cópia-pasta, mas verificará se o código foi modificado por meio de bombardeio. Nesse caso, copie além de um anão e execute-o. Se conseguir fazer o GameView novamente, tentarei alterar algumas das constantes.

copy    MOV data copy
loop    MOV @-1 @-1
    CMP @copy end
out JMP check 0
    SUB loop copy
    JMP loop 0
data    DAT 0 4109
check   MOV data copy
loop2   CMP @copy @copy
    JMP ok 0
    MOV aah 2
ok  CMP @copy end
    JMP 4098 0
    SUB loop copy
    JMP loop2 0
panic   MOV end copy
    MOV jump out
    JMP loop 0
jump    JMP 4124 0
dwarf   ADD 2 bomb
    MOV bomb @bomb
    JMP dwarf 4
bomb    DAT 0 4
aah JMP 3 0
end DAT 19 4127

Ok, na verdade ele está trabalhando e eu estava apenas messign volta, obrigado pela nova execução;)
Hit
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.