Нано Ядро Войны


21

Это адаптация Core War , программирования KOTH, начиная с 20-го века. Чтобы быть более конкретным, он использует невероятно упрощенный набор инструкций, в основном на основе исходного предложения .

Задний план

В Core War есть две программы, сражающиеся за контроль над компьютером. Цель каждой программы - победить, найдя и остановив противоборствующую программу.

Битва происходит в основной памяти компьютера. Эта память называется Ядром и содержит 8192 адреса. Когда начинается битва, код для каждого участника (так называемый воин) помещается в случайный кусок памяти. Выполнение программы чередуется между воинами, выполняя одну инструкцию каждому. Каждая инструкция способна модифицировать часть Ядра, что приводит к возможности самоизменения программ.

Цель состоит в том, чтобы прекратить противоположную программу. Программа завершается, когда она пытается выполнить недопустимую инструкцию, которая является любой DATинструкцией.

Набор инструкций

Каждая программа состоит из серии низкоуровневых инструкций, каждое из которых занимает два поля, называемых полями A и B.

Этот набор инструкций в значительной степени опирается на оригинальную спецификацию. Основными изменениями являются: 1) уточнение при добавлении / вычитании команд и 2) изменение режима #адресации, позволяющее использовать его где угодно. Большинство полных версий Core Wars имеют более 20 кодов операций, 8 режимов адресации и набор «модификаторов команд».

Opcodes

Каждая инструкция должна иметь один из семи различных кодов операций.

  • DAT A B- (данные) - это просто содержит цифры Aи B. Важно отметить, что процесс умирает при попытке выполнить инструкцию DAT.
  • MOV A B- (переместить) - перемещает содержимое ячейки памяти Aв ячейку памяти B. Вот демонстрация до и после:

    MOV 2 1
    ADD @4 #5
    JMP #1 -1
    
    MOV 2 1
    JMP #1 -1
    JMP #1 -1
    
  • ADD A B- (добавить) - это добавляет содержимое ячейки памяти Aв ячейку памяти B. Два первых поля обоих добавляются, а вторые поля добавляются.

    ADD 2 1
    MOV @4 #5
    JMP #1 -1
    
    ADD 2 1
    MOV @5 #4
    JMP #1 -1
    
  • SUB A B- (вычитать) - вычитает содержимое ячейки памяти Aиз (и сохраняет результат в ячейке памяти) B.

    SUB 2 1
    MOV @4 #5
    JMP #1 -1
    
    SUB 2 1
    MOV @3 #6
    JMP #1 -1
    
  • JMP A B- (прыжок) - перейти к локации A, которая будет выполняться в следующем цикле. Bдолжно быть числом, но ничего не делает (хотя вы можете использовать его для хранения информации).

    JMP 2 1337
    ADD 1 2
    ADD 2 3
    

    Прыжок означает, что ADD 2 3будет выполнен следующий цикл.

  • JMZ A B- (перейти, если ноль) - Если оба поля строки Bравны 0, то программа переходит к месту A.

    JMZ 2 1
    SUB 0 @0
    DAT 23 45
    

    Поскольку два поля инструкции 1 равны 0, команда DAT будет выполнена на следующем ходу, что приведет к неизбежной смерти.

  • CMP A B- (сравните и пропустите, если не равны) - Если поля в инструкциях Aи Bне равны, пропустите следующую инструкцию.

    CMP #1 2
    ADD 2 #3
    SUB @2 3
    

    Поскольку два поля команд 1 и 2 имеют одинаковое значение, команда ADD не пропускается и выполняется в следующий ход.

Когда две инструкции складываются / вычитаются, два поля (A и B) складываются / вычитаются попарно. Режим адресации и код операции не изменяются.

Режимы адресации

Существует три вида режимов адресации. Каждое из двух полей инструкции имеет один из этих трех режимов адресации.

  • Немедленно#X - Xэто строка, которая будет использоваться непосредственно в вычислениях. Например, #0это первая строка программы. Отрицательные строки относятся к строкам в ядре перед запуском программы.

    ... //just a space-filler
    ...
    ADD #3 #4
    DAT 0 1
    DAT 2 4
    

    Это добавит первую из двух строк DAT ко второй, так как они находятся в строках 3 и 4 соответственно. Однако вы не захотите использовать этот код, потому что DAT убьет вашего бота в следующем цикле.

  • ОтносительныйX - число Xпредставляет местоположение целевого адреса памяти относительно текущего адреса. Число в этом месте используется в вычислениях. Если строка #35выполняется и содержит -5, то #30используется строка .

    ... //just a space-filler
    ...
    ADD 2 1
    DAT 0 1
    DAT 2 4
    

    Это добавит вторую строку DAT к первой.

  • Косвенный@X - число Xпредставляет относительный адрес. Содержимое в этом месте временно добавляется к номеру X, чтобы сформировать новый относительный адрес, из которого извлекается номер. Если строка #35выполняется, и ее второе поле равно @4, а второе поле строки #39содержит число -7, то #32используется строка .

    ... //just a space-filler
    ...
    ADD @1 @1
    DAT 0 1
    DAT 2 4
    

    Это добавит первый DAT ко второму, но в более запутанном виде. Первое поле @ 1, которое получает данные с этого относительного адреса, который является первым полем первого DAT, 0. Это интерпретируется как второй относительный адрес из этого местоположения, поэтому 1 + 0 = 1 дает общее смещение от оригинальной инструкции. Для второго поля @ 1 получает значение из этого относительного адреса (1 во втором поле первого DAT) и таким же образом добавляет его к себе. Общее смещение составляет 1 + 1 = 2. Итак, эта инструкция выполняется аналогично ADD 1 2.

Каждая программа может содержать до 64 инструкций.

Когда начинается раунд, две программы размещаются случайным образом в банке памяти с 8192 местоположениями. Указатель инструкций для каждой программы начинается в начале программы и увеличивается после каждого цикла выполнения. Программа умирает, как только указатель ее команды пытается выполнить DATинструкцию.

Параметры ядра

Размер ядра составляет 8192 с таймаутом 8192 * 8 = 65536 тиков. Ядро циклическое, поэтому запись по адресу 8195 аналогична записи по адресу 3. Все неиспользуемые адреса инициализируются в DAT #0 #0.

Каждый участник не должен быть длиннее 64 строк. Целые числа будут храниться как 32-разрядные целые числа со знаком.

анализ

Чтобы упростить программирование для конкурентов, я добавлю функцию разметки строк в анализатор. Любые слова, встречающиеся в строке перед кодом операции, будут интерпретироваться как метки строк. Например, tree mov 4 6имеет метку строки tree. Если где-либо в программе есть поле, содержащее tree #treeили @tree, будет подставлено число. Кроме того, капитализация игнорируется.

Вот пример того, как подставляются метки строк:

labelA add labelB @labelC
labelB add #labelC labelC
labelC sub labelA @labelB

Здесь метки A, B и C находятся в строках 0, 1 и 2. Экземпляры #labelбудут заменены номером строки метки. Экземпляры labelили @labelзаменены относительным расположением метки. Режимы адресации сохранены.

ADD 1 @2
ADD #2 1
SUB -2 @-1

счет

Для каждой пары участников проводятся все возможные битвы. Поскольку исход битвы зависит от относительных смещений двух программ, пробуется каждое возможное смещение (около 8000 из них). Кроме того, у каждой программы есть шанс двигаться первым в каждом смещении. Программа, которая выигрывает большинство этих смещений, является победителем пары.

За каждую пару, которую выигрывает воин, ему присуждается 2 очка. За каждый галстук воин получает 1 очко.

Вам разрешено представить более одного воина. Применяются типичные правила для нескольких представлений, такие как отсутствие объединения тегов, отсутствие сотрудничества, создание королей и т. Д. В Core War в любом случае нет места для этого, так что это не должно иметь большого значения.

Контроллер

Код для контроллера, вместе с двумя простыми примерами ботов, находится здесь . Поскольку это соревнование (при запуске с использованием официальных настроек) является полностью детерминированным, созданная вами таблица лидеров будет точно такой же, как и официальная таблица лидеров.

Пример бота

Вот пример бота, который демонстрирует некоторые особенности языка.

main mov bomb #-1
     add @main main
     jmp #main 0
bomb dat 0 -1

Этот бот работает, медленно стирая всю остальную память в ядре, заменяя ее «бомбой». Поскольку бомба является DATинструкцией, любая программа, которая достигает бомбы, будет уничтожена.

Есть две строчные метки, «главная» и «бомба», которые служат для замены чисел. После предварительной обработки программа выглядит так:

MOV 3 #-1
ADD @-1 -1
JMP #0 0
DAT 0 -1

Первая строка копирует бомбу в строку непосредственно над программой. Следующая строка добавляет значение бомбы ( 0 -1) к команде перемещения, а также демонстрирует использование режима @адресации. Это дополнение заставляет команду перемещения указывать на новую цель. Следующая команда безоговорочно возвращается к началу программы.


Текущий список лидеров

24 - Turbo
22 - DwarvenEngineer
20 - HanShotFirst
18 - Dwarf
14 - ScanBomber
10 - Параноик
10 - FirstTimer
10 - Дворник
10 - Развитый
6 - EasterBunny
6 - CopyPasta
4 - Imp
2 - Slug

Попарные результаты:

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

Последнее обновление (новые версии Turbo и Paranoid) заняло около 5 минут для запуска на старом ноутбуке. Я хотел бы поблагодарить Ilmari Karonen за его улучшения в контроллере . Если у вас есть локальная копия контроллера, вы должны обновить ваши файлы.


Что произойдет, если два конкурирующих бота попытаются использовать один и тот же ярлык?
mbomb007

1
@ mbomb007 Метки являются предметом предварительной обработки и рассчитываются при анализе исходного файла бота. Ваши ярлыки не будут взаимодействовать с ярлыками конкурентов.
PhiNotPi

1
@ mbomb007 чтобы программы не перекрывались. Кроме того, я не планирую добавлять какие-либо дополнительные функции в эту версию, за исключением тех, что для Micro Core War.
PhiNotPi

1
@ mbomb007 Косвенная адресация ссылается на то же поле, что и ссылка (первая или вторая). Там нет модификаторов инструкций. Я не основываю этот вызов на стандарте 94 года.
PhiNotPi

2
@ Thrax Я собираюсь сказать нет, что вы не ограничены одним представлением. Применяются типичные правила множественного представления (без объединения тегов и т. Д.), Хотя в основных войнах в любом случае не так много места для сотрудничества.
PhiNotPi

Ответы:


9

Дварфский Инженер

Новый и улучшенный гном. Побеждает все остальное, представленное до сих пор. Фантазии corestep -Оптимизация размер шага, вероятно , слишком здесь.

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

Примечательные особенности включают в себя быструю петлю бомбардировки, которая сбрасывает две бомбы за четыре цикла, для средней скорости бомбардировки 0,5 с на старом жаргоне Core War, и использование JMZдля определения, когда бомбардировка завершена, и пришло время переключиться на план B ( здесь, бес).


Я играл в Core War еще в 90-х годах (некоторые из вас, возможно, видели основной справочник, который я написал в 97-м), поэтому я подумал, что было бы интересно узнать, какие старые стратегии из мира RedCode '88 / '94 могли бы быть полезным в этом варианте.

Мои первые мысли были:

  • Там нет SPL, следовательно, нет репликаторов (и нет колец / спиралей имп). Это должно сделать бомбардировщиков сильными. (Кроме того, все эти причудливые стратегии бомбардировок, предназначенные для борьбы с репликаторами и спиралями бесов? Здесь совершенно бесполезно и бесполезно. Просто бомбите любыми DATс.)

  • С другой стороны, CMPсканирование все еще потенциально быстрее, чем бомбардировка, поэтому у быстрого сканера может быть шанс.

  • Отсутствие в / декрементов делает очистку ядра очень медленной. На самом деле, ядро ​​в этом варианте - просто бомбардировщик с (неоптимальным) размером шага ± 1. Опять же, это также вредит сканерам; Однако стратегия «один выстрел» → бомбардировщик может сработать.

  • Quickscanner / quickbombers (стратегия ранней игры, использующая развернутый цикл сканирования / бомбардировки, для тех, кто не очень знаком с жаргоном Core War), все еще потенциально полезны, но только против длинных программ (которыми они сами являются, так что есть своего рода обратная связь эффект здесь). Трудно сказать, действительно ли это стоит того.

  • Система начисления очков интересная. Связи набирают вдвое меньше очков, чем победа (а не 1/3, как в традиционной Core War), делая их более привлекательными. С другой стороны, единственная программа, которая может выиграть много связей по этим правилам, - бес. (Кроме того , отсутствие де / приросты делает имп ворота трудно, так что даже простые бесы на самом деле сделать есть шанс забил галстук , если они достигают своего противника в живых.)

  • Кроме того, поскольку итоговый рейтинг зависит только от того, какие программы вы избили, а не от того, насколько вы их побили, это способствует появлению универсальных записей. Лучше всего едва победить всех своих противников, чем полностью уничтожить половину из них и едва проиграть остальным.

  • Поскольку код является общедоступным, всегда можно найти программу, которая сможет побить любую заданную ранее отправку - возможно, даже несколько из них - независимо от того, насколько они хороши в целом. Такие трюки (например, настройка размера шага, чтобы поразить противника непосредственно перед тем, как он поразит вас) могут легко показаться дешевыми. И, конечно же, целевой игрок всегда может просто представить новую версию с разными константами.

Как бы то ни было, результатом всего этого стало то, что я решил, что мне следует написать либо быстрый бомбардировщик, либо очень быстрый сканер, и, возможно, прикрепить к нему быстродействующий сканер / бомбардировщик. Из этих вариантов быстрый бомбардировщик казался самым простым и наиболее вероятным.

В тот момент я потратил слишком много времени на настройку и оптимизацию кода интерпретатора PhiNotPi, потому что я решил, что, вероятно, проведу множество испытаний методом грубой силы для оптимизации констант. Так получилось, что мне никогда не приходилось этого делать - приведенный выше код в значительной степени является первой версией, которая действительно сработала (после пары неудачных попыток, которые просто покончили с собой из-за глупых ошибок).


Уловка, которая делает мой бомбардировщик быстрым, заключается в использовании косвенной адресации, чтобы бросать по две бомбы за каждую ADD. Вот как это работает:

  1. На первом цикле мы выполняем MOV bomb @aim. Это копирует bombинструкцию, куда бы ни лежало ядро ​​B-поля aim(изначально, ровно 6326 инструкций до aimили 6328 инструкций до step; вы поймете, почему эти числа имеют значение позже).

  2. На следующем шаге мы выполняем aimсаму инструкцию! На первом проходе, он выглядит следующим образом : MOV bomb @-6326. Таким образом, он копирует bombв местоположение, на которое указывает B-поле инструкции в 6326 строк перед собой.

    Итак, что там на 6326 строк раньше aim? Да, это копия, которую bombмы только что поместили туда на один цикл раньше! И мы просто так устроили, что B-поле bombимеет ненулевое значение, чтобы новая бомба не копировалась поверх старой, а на некотором расстоянии (фактически, здесь расстояние 3164, что составляет половину нашего номинального размера шага 6328, но другие смещения могут работать, возможно, даже лучше).

  3. В следующем цикле мы корректируем нашу цель с помощью SUB step aim, которая вычитает значения stepинструкции (которая также является прыжком, который мы собираемся выполнить в следующем, хотя это могло бы быть DATгде-то просто ) aim.

    (Одна деталь отметить здесь, что мы вроде хотим A-значение stepравным нулю, так что мы по- прежнему бросать же бомбы на следующей итерации Даже то , что не является строго необходимым, хотя,. Только бомбы выброшен по первой инструкции нужно, чтобы их B-поле было равно 3164, остальные могут быть чем угодно.)

  4. Затем JMZпроверка того, что инструкция 6328 отходит от нее, по-прежнему равна нулю и, если так, возвращается к началу кода. Теперь 6328 - это размер шага нашего бомбардировщика, и он делится на 8 (но не на 16); таким образом, если бы мы просто продолжали бросать бомбы каждые 6328 шагов, мы в конечном итоге вернулись бы к тому, с чего начали, взорвав каждую восьмую инструкцию в ядре (и со смещением дополнительных бомб на 3163 = 6328/2 ≡ 4 (мод. 8) мы бы поразили каждую четвертую инструкцию).

    Но мы начали бомбардировку работать на 6328 инструкции передJMZ , и отступили от -6328 на каждую итерации, так что мы будем бомбить местоположение 6328 шагов после того, как в JMZтолько одной итерации , прежде чем мы попали в JMZсебя. Поэтому, когда JMZобнаруживается бомба в 6328 инструкциях после нее, это признак того, что мы покрыли как можно большую часть ядра, не поразив себя, и должны перейти к стратегии резервного копирования, прежде чем убить себя.

  5. Что касается стратегии резервного копирования, то это просто старый MOV 0 1чертенок, потому что сейчас я не мог придумать ничего лучшего. На мой взгляд, если мы бомбили каждую четвертую локацию ядра и все еще не победили, мы, вероятно, сражаемся с чем-то очень маленьким или очень оборонительным, и могли бы просто попытаться выжить и согласиться на ничью. Это нормально, потому что такие маленькие или оборонительные программы, как правило, не очень хороши в убийстве чего-либо еще, и поэтому, даже если мы выиграем всего несколько боев случайно, мы, вероятно, все еще будем впереди.


Ps. На случай, если кто-то еще захочет, вот мой слегка улучшенный форк код турнира PhiNotPi . Это примерно в два раза быстрее, сохраняет старые результаты битвы, так что вам не нужно их повторно запускать, и исправляет, как я считаю, небольшую ошибку в подсчете результатов битвы. Изменения были объединены в основную версию PhiNotPi. Благодарность!


1
Как вы знаете, оценка оценивает КАЖДУЮ возможную комбинацию стартовых местоположений программы, и программа набирает наибольшее количество баллов. Это делает связи невозможными или совершенно неблагоприятными, поскольку, пока программа никогда не убивает себя и не бомбит хотя бы один адрес один раз, она победит беса, получив один выигрыш, а остальные связи.
mbomb007

9

Графическое представление

Это можно использовать как инструмент отладки. Он отображает ядро ​​и показывает местоположение игрока. Чтобы использовать его, вы должны вызвать его из кода. Я также предоставил модификацию, Game.javaкоторая автоматически отображает GraphView.

PhiNotPi и Ilmari Karonen недавно сменили контроллер. Илмари Каронен любезно предоставил обновленный GameView в этом месте .

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:

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.");
        }
    }*/
}

Похоже, вы также внесли изменения в Player. Я получаю./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

@ AShelly Извините за это. Я должен закомментировать printCore()метод.
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

Моя вторая попытка CoreWar. Предназначен для победы над Гномами. Сканирует данные на 3, а затем ставит бомбу каждые 2. Каждый этап выполняется только по 3 инструкциям, в надежде, что бомбы Гнома пропустят это.

NEW Turbo ++ : теперь улучшено. Он сканирует назад, пока не находит данные, затем перемещается туда, а затем бомбит назад. Надежда состоит в том, что ход либо забивает противника, либо находится в месте, уже подвергнутом бомбардировке и, следовательно, в безопасности (иш).

... И редактирование, чтобы сделать его более скудным, заставляет всех биться!


Кажется, бьет намного больше, чем просто гном. Поздравляем! Я думаю, что вы могли бы достичь третьего места, если бы вы только могли победить Imp.
Ильмари Каронен

Я обновил этот, но на самом деле это довольно большая эволюция из предыдущего. Должен ли я сделать новую запись вместо этого?
AShelly

Я не берусь говорить за PhiNotPi, но думаю, что это зависит от вас. Обновление на месте в основном означает удаление вашей старой записи. В любом случае, еще больше поздравлений с успешным уклонением от бомб на пути к третьему месту! Я думаю, что ваша единственная запись, которая пока побеждает DwarvenEngineer в паре.
Ильмари Каронен

Отлично сработано ;). Вы тот, кто победит сейчас!
Хит

8

карликовый

Обычная и простая программа, которая представляет собой бросание камней гномами. Он размещает DATинструкцию каждые четыре адреса.

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

РЕДАКТИРОВАТЬ: Исправляет адресацию. Видимо, режимы адресации отличаются от спецификации, с которой связан OP.


Я думаю, что это «добавить # 3 3» для первой строки, не так ли?
Хит

@ Нет, нет. Я хочу попасть на каждый 4-й адрес. Я мог бы использовать add 3 3, но тогда он бы удваивал каждый цикл вместо добавления, и это было бы бесполезно. #4является непосредственным, поэтому он добавляет число 4ко 2-му значению в адресе, который идет 3после текущего адреса.
mbomb007

Я думаю, что вы неправильно интерпретируете #режим адресации в вызове. Как указано в спецификации, я внес изменение в #режим адресации.
PhiNotPi

Вы должны пойти так: «добавить 2 3 mov 2 @ 2 jmp -2 4 dat 0 4»
Hit

При правильном поведении даже поражения развились
Хит

7

Evolved

Честно говоря, я не понимаю, как это работает. Кажется, он создает свой исходный код, прежде чем что-то делать. Я был бы рад, если бы кто-нибудь объяснил мне, как это работает.

Изучив его, я обнаружил, что это просто модифицированный гном с бесом. Вместо того, чтобы бомбить врагов DATинструкциями, он тасует код врагов. Он также бомбит каждые два регистра вместо каждых четырех регистров. Если бы было достаточно времени, это, несомненно, разрушило бы себя.

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

1
Тогда где ты это взял?
PyRulez

4
@PyRulez Это компьютер, созданный с помощью генетического алгоритма.
TheNumberOne

1
Похоже, что выполнение на самом деле не продвигается дальше, чем строка # 6, потому что там оно возвращается в программу. Я считаю, что причина этого в том, что ходов / петель больше, чем у конкурентов.
PhiNotPi

6

FirstTimer

Если это работает, он должен попытаться занять позицию в начале ядра и создать защиту

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

Это не совсем работает так, как я думаю, вы предполагали, что это так: #0относится к началу вашей программы (то есть так же, как #main), а не к началу ядра (что в любом случае не является осмысленным понятием - ядро циркуляр, ваш код не может сказать, где он начинается или заканчивается). Что происходит, так это то, что ваша первая инструкция ( main) перезаписывает себя с помощью MOV #data #100, после чего ваш код фактически превращается в ядро ​​с прямым переносом 0,25c (= одна инструкция на четыре цикла).
Ильмари Каронен

@IlmariKaronen О, спасибо за объяснение. Я сделал ошибку #0для запуска ядра. Первые 5 инструкций совершенно бесполезны.
Thrax

6

CopyPasta

Никогда не участвовавший в CoreWar, эта простая программа просто пытается копировать-вставить себя и затем выполнить копирование. Это может иметь неправильное поведение, пожалуйста, скажите мне, если это так.

Это слишком пацифистский и не может победить на самом деле.

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

Это текущее изменение, вероятно, не будет в следующем обновлении списка лидеров (сейчас я участвую в турнире). Старая версия, однако, выиграла (небольшой размер ядра) предварительные результаты.
PhiNotPi

Хорошо :) Старая версия не выходит из цикла 1, это не совсем желаемое поведение, я пытаюсь это исправить.
Хит

Текущая версия кажется сломанной. Пока не знаю почему.
PhiNotPi

1
Я обновил средства отладки, так что теперь я могу диагностировать проблему. Происходит то, что программа копирует только вторую половину себя (начиная с JMP loop 0). Затем, когда он переходит туда, где должно быть начало копии, это просто пустое место, и он теряет.
PhiNotPi

2
Пожалуйста, игнорируйте мой ранее (сейчас удаленный) комментарий; Я протестировал неверную версию вашего кода (по иронии судьбы, из-за ошибки копирования-вставки), поэтому он так плохо работал для меня.
Илмари Каронен

6

дворник

Он должен проверить, являются ли следующие адреса пустыми, а если нет, то очистить их (таким образом, мы надеемся, стереть бот противника).

Изменить: эта новая версия должна быть быстрее (теперь, когда я правильно понял JMZкоманду и @ссылку).

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

Разве уборщик не совершает самоубийство с первым JMZ? Должно быть как минимум JMZ 2. 8. Кстати, используя @, вы можете уменьшить два добавления только до одного. Что-то вроде: "JMZ 2 @ 5 MOV 5 @ 4 ADD 2 3 JMP -3 0 DAT 0 1 DAT 0 2 DAT 0 0" (непроверено)
Хит

@ Hit Это не прыгает, потому что адрес 2 оттуда есть ADD 3 -2, но вы правы, что он должен изменить его, я думаю.
mbomb007

Да, я неправильно прочитал инструкцию, JMZи думал, что JMZ A Bпроверял Aи перепрыгивал, Bесли 0, по-видимому, наоборот. Спасибо за замечание, потому что я не сделал :)
plannapus

5

ScanBomber

Удалите мои комментарии перед компиляцией. Сканирует некоторое время, а затем бомбит, когда находит программу. Это, вероятно, все еще проиграет моему Гному.

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

ОП определен #совершенно иначе, чем спецификация (прочитайте ссылку, на которую он ссылался), мне еще предстоит исправить эту программу для нее.
mbomb007

@TheBestOne Думаю, я это исправил. Похоже, это имеет смысл сейчас? Или мне нужно ставить #перед каждой ссылкой на zero? Да, я думаю, что мне нужно ...
mbomb007

Теперь это хорошо работает. Он бьет всех ботов, кроме Dwarf и Imp.
TheNumberOne

@TheBestOne Dwarf слишком мал и будет обнаружен только в 50% возможного размещения программы. Скорее всего, он проигрывает только Импу, потому что бомбит себя, обойдя всю память.
mbomb007

5

Хан выстрелил первым (v2)

Я подумал, что соревнования могут использовать больше разнообразия, так что вот моя вторая запись: один выстрел CMP сканер.

Это версия 2 , с улучшенной защитой против Импа - теперь она может побить Беса, хотя бы только на одно очко. Он по-прежнему проигрывает Гномовому Инженеру, но пока превосходит все остальное, ставя его в настоящее время на первое место.

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

Он работает путем сравнения соседних основных местоположений на 5 шагов друг от друга, с 10-шаговыми интервалами, пока не обнаружит разницу. Когда это происходит, он начинает сбрасывать бомбы с двухступенчатым интервалом, пока не убьет своего противника или не облетит вокруг ядра, чтобы достичь самого себя.

Если сканирование не находит ничего другого, оно в конечном итоге будет зацикливаться, находить свой собственный код и атаковать его. Это было бы самоубийством, но по счастливому совпадению первая бомба приземлилась прямо наaim линию, в результате чего следующая бомба была выброшена на 12 позиций (а не обычные 2) вниз по ядру, что удобно пропускало код. (Это также происходит с вероятностью 50%, если при сканировании что-то обнаруживается, но не удается убить противника.) Поскольку размер ядра кратен двум, это также будет происходить, если запуск бомбардировки будет повторяться, устраняя необходимость дальнейшая стратегия резервного копирования.

(Этот трюк самобомбировки изначально был чистым совпадением - я планировал совершенно другой способ перехода от режима сканирования к режиму бомбардировки, если ничего не было найдено, но когда я впервые протестировал код, константы просто оказались правильными, чтобы сделать его работать таким образом, и я решил придерживаться его.)



4

слизень

     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

Ползет по памяти назад. Изредка выбрасывает бомбу подальше.


3

Пасхальный заяц

Он любит прыгать назад :)

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

3

параноик

Вид копировально-макаронных изделий, но он проверит, был ли код изменен бомбардировкой. Если так, то скопируйте его за карлика и выполните. Если мне удастся снова создать GameView, я попытаюсь изменить некоторые из констант.

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

Хорошо, на самом деле это работает, и я просто возился, спасибо за новый запуск;)
Хит
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.