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.
fonte
Respostas:
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.
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
JMZ
para 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 qualquerDAT
s.)Por outro lado, a
CMP
digitalizaçã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:No primeiro ciclo, executamos
MOV bomb @aim
. Isso copia asbomb
instruções para onde quer que esteja no núcleo o campo B dosaim
pontos (inicialmente, exatamente 6326 instruções antesaim
ou 6328 instruções anterioresstep
; você verá por que esses números são importantes mais tarde).Na próxima etapa, executamos a
aim
própria instrução! Na primeira passagem, ele se parece com isso:MOV bomb @-6326
. Assim, ele copiabomb
para 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ópiabomb
que acabamos de colocar lá um ciclo antes! Por acaso, organizamos as coisas para que o campo Bbomb
tenha 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).No próximo ciclo, ajustamos nosso objetivo com
SUB step aim
, o qual subtrai os valores dastep
instrução (que também é o salto que vamos executar a seguir, embora possa ter sido apenas um simplesDAT
lugar)aim
.(Um detalhe a ser observado aqui é que tipo de desejar que o A-valor
step
a 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.)Em seguida, a
JMZ
verificaçã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 aJMZ
apenas uma iteração antes de atingirmos aJMZ
si própria. Portanto, quandoJMZ
detecta 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.Quanto à estratégia de backup, é apenas um velho
MOV 0 1
travesso, 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!fonte
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.java
que exibe automaticamente o GraphView.PhiNotPi e Ilmari Karonen recentemente mudaram o Controller. Ilmari Karonen teve a gentileza de fornecer um GameView atualizado neste local .
Game.java modificado:
fonte
./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[]
printCore()
método.Turbo
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!
fonte
Anão
Um programa comum e simples que representa um anão jogando pedras. Ele coloca uma
DAT
instrução a cada quatro endereços.EDIT: corrige o endereçamento. Aparentemente, os modos de endereçamento são diferentes das especificações às quais o OP está vinculado.
fonte
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úmero4
ao segundo valor no endereço que fica3
após o endereço atual.#
modo de endereçamento no desafio. Conforme declarado na especificação, fiz uma alteração no#
modo de endereçamento.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
DAT
instruçõ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.fonte
Primeiro tempo
Se funcionar, deve tentar tomar posição no início do núcleo e criar uma defesa
fonte
#0
refere-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 aMOV #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).#0
com o início do núcleo. As 5 primeiras instruções são completamente inúteis então.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.
fonte
JMP loop 0
). Então, quando ele pula para onde o início da cópia deve estar, é apenas espaço vazio e perde.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
JMZ
comando e a@
referência corretamente).fonte
ADD 3 -2
, mas você está certo de que ele deve mudar isso, eu acho.JMZ
e pensei queJMZ A B
estava checandoA
e pulando paraB
se 0 quando aparentemente é o contrário. Obrigado por reparar, porque eu não :)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.
fonte
#
completamente diferente da especificação (leia o link ao qual ele se vinculou). Ainda tenho que corrigir esse programa.#
antes de qualquer referênciazero
? Sim, eu acho que eu preciso ...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.
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 o
aim
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.)
fonte
Criança levada
Simplesmente avança no programa.
fonte
Slug
Rasteja pelo espaço de memória para trás. Ocasionalmente joga uma bomba longe.
fonte
coelhinho da Páscoa
Ele gosta de pular para trás :)
fonte
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.
fonte