Нано-основна війна


21

Це адаптація Core War , програмування KOTH, починаючи з 20 століття. Щоб бути більш конкретним, воно використовує неймовірно спрощений набір інструкцій, в основному виходячи з оригінальної пропозиції .

Фон

У Core War є дві програми, що борються за контроль над комп'ютером. Мета кожної програми - виграти шляхом пошуку та припинення протилежної програми.

Бій відбувається в основній пам'яті комп'ютера. Ця пам'ять називається Core, і вона містить 8192 адреси. Коли бій починається, код кожного учасника (званий воїном) поміщається у випадковий шматок пам'яті. Виконання програми чергується між воїнами, виконуючи по одній інструкції кожного. Кожна інструкція здатна змінювати частину ядра, що призводить до можливості самовиправлення програм.

Мета - припинити протилежну програму. Програма припиняється, коли вона намагається виконати недійсну інструкцію, яка є будь-якою DATінструкцією.

Набір інструкцій

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

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

Опкоди

Кожна інструкція повинна мати один із семи різних опкодів.

  • 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- (add) - Це додає вміст місця 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 бал.

Вам дозволяється подати більше одного воїна. Застосовуються типові правила для декількох подань, такі як відсутність об'єднань тегів, відсутність співпраці, не створення короля тощо. Насправді, в Основній війні дійсно немає місця для цього, тому це не повинно бути великою справою.

Контролер

Код контролера разом з двома простими прикладними роботами розміщений тут . Оскільки цей конкурс (при запуску з використанням офіційних налаштувань) є повністю детермінованим, то створений вами таблиця лідерів буде точно таким же, як і офіційний таблиця лідерів.

Приклад Бот

Ось приклад бота, який демонструє деякі особливості мови.

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

Перший рядок копіює бомбу на лінію безпосередньо над програмою. Наступний рядок додає значення bom ( 0 -1) до команди move, а також демонструє використання @режиму адресації. Це доповнення призводить до того, що команда переміщення вказує на нову ціль. Наступна команда беззастережно відскакує назад до початку програми.


Поточна таблиця лідерів

24 - Turbo
22 - DwarvenEngineer
20 - HanShotFirst
18 - Dwarf
14 - ScanBomber
10 - Paranoid
10 - FirstTimer
10 - Janitor
10 - Evolved
6 - EasterBunny
6 - CopyPasta
4 - Imp
2 - Slug

Підрядні результати:

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 хвилин на старий ноутбук. Хочу подякувати Ілмарі Каронен за його вдосконалення щодо контролера . Якщо у вас є локальна копія контролера, слід оновити свої файли.


Що станеться, якщо два конкуруючих бота намагаються використовувати одну і ту ж мітку?
mbomb007

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

1
@ mbomb007 Щоб програми не перетиналися. Крім того, я не планую додавати більше функцій до цієї версії, окрім цих для Micro Core War.
PhiNotPi

1
@ mbomb007 Непряма адресація посилань на те саме поле, що робить посилання (1-е чи 2-е). Не існує модифікаторів інструкцій. Я не грунтуюся на цьому виклику зі стандарту '94.
PhiNotPi

2
@Thrax Я хочу сказати «ні», що ви не обмежені одним поданням. Застосовуються типові правила для багатокористувацького подання (відсутність об'єднань з тегами тощо), хоча місця для співпраці в основних війнах все одно мало.
PhiNotPi

Відповіді:


9

Гніздовий інженер

Новий і вдосконалений Гном. Виграє проти всього іншого, поданого поки що. Тут, напевно, оптимізований розмір кроку, оптимізований, є надмірним.

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

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


Я грав у «Основну війну» ще в 90-х (дехто з вас, можливо, бачив основний путівник, який я написав ще у 97-му), тому я подумав, що буде цікаво подивитися, які старі стратегії зі світу RedCode '88 / '94 можуть бути корисним у цьому варіанті.

Мої перші думки були:

  • Немає SPL, отже, немає реплікаторів (і жодних імпульсних кілець / спіралей). Це повинно зробити бомбардувальники сильними. (Крім того, всі ці фантазійні стратегії бомбардування, розроблені для боротьби з реплікаторами і імпульсними спіралями? Тут абсолютно непотрібні і марні. Просто бомбардуйте будь-які DATс.)

  • Потім знову CMPсканування все ще потенційно швидше, ніж бомбардування, тому швидкий сканер може мати шанс.

  • Відсутність в / зменшення ядра очищає дуже повільно. Насправді ядро ​​в цьому варіанті - це просто бомбардувальник з розміром кроку (неоптимальний) ± 1. Знову ж, це також шкодить сканерам; однакова стратегія з одним знімком → бомбардувальна стратегія може працювати.

  • Quickscanners / quickbombers (стратегія ранньої гри з використанням непрокрученого циклу сканування / бомбардування для тих, хто не такий знайомий з жаргоном Core War) все ще потенційно корисна, але лише проти довгих програм (якими вони є самі, тому є своєрідний зворотний зв'язок ефект тут). Важко сказати, чи справді це варте клопоту.

  • Система підрахунків балів цікава. Краватки набирають удвічі більше очок, ніж виграш (а не 1/3, як у традиційній Основній війні), що робить їх більш привабливими. Знову ж таки, про єдину програму, яка може зафіксувати велику кількість зв'язків за цими правилами, є ідеєю. (Крім того , відсутність де / прирости робить імп ворота важко, так що навіть прості біси на насправді зробити є шанс забив краватку , якщо вони досягають свого супротивника в живих.)

  • Крім того, оскільки остаточний рейтинг залежить лише від того, за якими програмами ви б'єте, а не від того, наскільки ви їх обіграли, це, як правило, надає перевагу загальним публікаціям. Краще просто ледь бити всіх своїх опонентів, ніж повністю знищити половину з них і ледь не програти решті.

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

У будь-якому випадку, підсумок всього цього полягає в тому, що я вирішив, що мені слід спробувати написати або швидкий бомбардувальник, або дуже швидкий сканер, і, можливо, надіти на нього квискард / бомбер. З цих варіантів швидкий бомбардувальник здавався найпростішим і, швидше за все, спрацював.

У цей момент я витратив занадто багато часу на налаштування і оптимізацію коду інтерпретатора 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ідеал, тому що я нічого не міг придумати на даний момент. Як я це бачу, якщо ми бомбили кожне четверте місце ядра і все ще не перемогли, ми, мабуть, боремося з чимось дуже малим або дуже оборонним, і можемо просто спробувати вижити і домогтися виходу. Це нормально, тому що такі невеликі або оборонні програми, як правило, не дуже добре вбивати щось інше, і тому навіть якщо ми випадково виграємо лише кілька боїв, ми, мабуть, все-таки вийдемо попереду.


Пс. У випадку, якщо хтось хоче цього, ось мій трохи вдосконалений вил турнірного коду 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

Турбо

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 інструкції, сподіваючись, що бомби гнома пропустять його.

НОВИЙ Turbo ++ : Тепер удосконалено. Він сканує назад, поки не знайде даних, потім переміститься туди, потім бомбить назад. Надія полягає в тому, що цей крок або приборкає противника, або до місця, яке вже бомбардується, і тому є безпечним (іш).

... І редагування, щоб зробити його сканування більш рідким, змушує його бити всіх!


Здається, бити набагато більше, ніж просто Гном. Вітаємо! Я думаю, ти міг би дійти до третього місця, якби ти також міг бити Імп.
Ільмарі Каронен

Я оновив цю, але насправді це досить велика еволюція від попередньої. Чи повинен я зробити новий запис замість цього?
AShelly

Я не припускаю виступати за PhiNotPi, але я думаю, що це залежить від вас. Оновлення на місці просто означає, що вилучите старий запис. У будь-якому разі, ще більше поздоровлень з успішним ухиленням бомби на третьому місці! Я думаю, що ваш - це єдиний запис на даний момент, щоб перемогти DwarvenEngineer попарно.
Ільмарі Каронен

Молодці;). ти зараз бив!
Хіт

8

Гном

Поширена і проста програма, яка представляє карлика, який кидає каміння. Він розміщує DATінструкцію кожні чотири адреси.

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

EDIT: Виправлення адреси. Мабуть, режими адресації відрізняються від специфікацій, з якими пов'язана ОП.


Я думаю, що це "додати №3 3" для першого рядка, чи не так?
Хіт

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

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

Ви повинні піти так: "додати 2 3 mov 2 @ 2 jmp -2 4 dat 0 4"
Hit

При правильній поведінці воно навіть перемогло розвинулося
Хіт

7

Еволюціонував

Я, чесно кажучи, не розумію, як це працює. Здається, побудуйте свій вихідний код, перш ніж робити що-небудь. Мені б сподобалося, якби хтось дав мені пояснення, як це працює.

Вивчивши це, я виявив, що це просто модифікований карлик із захисниками. Замість того, щоб бомбардувати ворогів 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 перших інструкцій тоді абсолютно марні.
Фракс

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

@Hit Це не стрибає, тому що адреса 2 звідти є ADD 3 -2, але ти маєш рацію, що він повинен її змінити.
mbomb007

Так, я неправильно прочитав інструкцію JMZі думав, що JMZ A Bперевіряє Aі переходить до B0, коли, мабуть, це навпаки. Дякую за те, що помітили, що я цього не зробив :)
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

Він зараз добре працює. Він б'є кожного бота, крім гнома та Imp.
TheNumberOne

@TheBestOne Гном занадто малий і його було б виявлено лише у 50% можливого розміщення програми. Він, швидше за все, програє Imp, оскільки він бомбить після того, як обійде цілу пам'ять.
mbomb007

5

Перший постріл Хань (v2)

Я вважав, що конкуренція може використовувати ще більше різноманітності, тож ось мій другий запис: односкатерний CMPсканер.

Це версія 2 з поліпшеними захисними засобами проти Imp - тепер вона може перемогти Imp, хоча б лише одним очком. Він все ще програє Інженеру Гномів, але перемагає все інше дотепер, ставлячи його в даний час на прив'язане перше місце.

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 кроків, поки не знайде різниці. Коли це станеться, він починає кидати бомби з 2-х ступінчастими інтервалами, поки не вб'є свого опонента або обертається навколо ядра, щоб досягти себе.

Якщо сканування не знайде нічого іншого, воно з часом обернеться і знайде власний код і атакує його. Це було б суїцидально, але за щасливим збігом обставин, що перша бомба приземляється прямо на 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.