СПРАВКА ПО REDCODE
CoreDuel реализует стандарт ICWS'94 с расширениями pMARS (p-space, FOR/ROF, выражения в EQU). Если вы знакомы с pMARS, здесь всё будет привычно.
0. Что это за игра
Вы пишете небольшие программы — ботов — и отправляете их на Арену сражаться с другими ботами. Бот написан на Redcode, ассемблероподобном языке из 19 инструкций. Все боты матча загружаются в одну общую память — ядро: кольцо из 8000 ячеек (конец переходит в начало) внутри простого эмулируемого компьютера MARS. Больше в этом компьютере ничего нет: ни экрана, ни файлов, только инструкции в ячейках и несколько процессов, которые их выполняют.
Каждый бот начинает с одним процессом в случайном месте, не ближе 100 ячеек от остальных. Эмулятор даёт ботам ходы по кругу; в свой ход бот выполняет одну инструкцию одного из своих процессов. Процесс погибает, выполнив инструкцию DAT (или разделив на ноль); бот погибает, когда не остаётся ни одного его процесса. Память общая, поэтому побеждают так: затирают код противника бомбами DAT, ищут его сканированием или заливают ядро своими копиями — и при этом сохраняют собственный код живым.
Раунд заканчивается, когда остаётся один бот, или через 80000 циклов. Матч — один или несколько раундов; выжившие в раунде получают очки, побеждает лучшая сумма. Вот и вся игра: напишите программу лучше, чем у соперника.
С чего начать:
- Боты: сделайте копию классики из библиотеки (Imp, Dwarf) или напишите нового; редактор проверяет код по мере ввода.
- Арена: заполните слоты ботов (один бот — тестовый прогон), задайте раунды и скорость, СТАРТ, смотрите на ядро.
- Обучение: восемь коротких уроков, в которых рабочий бот собирается шаг за шагом. Остальная часть справки — описание языка.
1. Ядро
Ядро — это кольцо из 8000 ячеек. В каждой ячейке лежит одна инструкция. Адреса зациклены: ячейка 7999 + 1 = ячейка 0. Абсолютных адресов в Redcode нет: любой адрес отсчитывается от инструкции, которая его использует.
Перед раундом каждая ячейка содержит DAT.F $0, $0. Воины размещаются в случайных позициях на расстоянии не менее MINDISTANCE (100) ячеек друг от друга. Каждый воин начинает с одного процесса, стоящего на его ORG.
Процессы ходят по очереди: воин A выполняет одну инструкцию, затем воин B, и так далее. Если у воина несколько процессов, они выполняются по кругу, по одному за ход.
Процесс погибает, когда выполняет DAT или делит на ноль. Воин погибает, когда погибает его последний процесс. Раунд заканчивается, когда остаётся один воин, или через MAXCYCLES (80000) циклов.
2. Структура программы
;redcode-94
;name Dwarf
;author A. K. Dewdney
;strategy Bombs every 4th cell.
;assert CORESIZE == 8000
ORG start
target DAT.F #0, #0
start ADD.AB #4, target
MOV.AB #0, @target
JMP start
END
- Всё после
;— комментарий. Строки;name,;authorи;strategyпоказываются в списке ботов и на арене. ;assert EXPRдолжно быть истинным, иначе воин отклоняется. Так удобно указать, под какой размер ядра вы настраивали воина.- Текст до
;redcode(почтовые заголовки, пояснения) игнорируется. - Метка стоит в начале строки. Двоеточие после неё допускается (
start: ADD ...). Метки чувствительны к регистру. - Опкоды, модификаторы и режимы к регистру не чувствительны.
- В программе может быть не более
MAXLENGTH(100) инструкций.
3. Синтаксис инструкции
label OPCODE.MOD A-mode A-value, B-mode B-value ; comment
У каждой инструкции есть опкод, модификатор, операнд A и операнд B. Каждый операнд — это режим адресации плюс значение. Значения — это выражения (см. 8), которые вычисляются относительно адреса самой инструкции.
Если указать только один операнд, ассемблер дополнит второй:
DAT xпревращается вDAT #0, x.- Для всех остальных опкодов единственный операнд становится A, а B становится
$0.
4. Режимы адресации
До выполнения опкода каждый операнд разрешается в указатель (адрес ячейки). Оба операнда вычисляются всегда, сначала A, даже если опкод один из них игнорирует. Поэтому побочные эффекты пре-/постинкремента срабатывают даже в JMP.
#непосредственный. Указатель — сама текущая инструкция; значение используется как число.$прямой (по умолчанию). Указатель = здесь + значение.@косвенный через B. Указатель = (здесь + значение) + поле B этой ячейки.<косвенный через B с предекрементом. Сначала уменьшить поле B ячейки (здесь + значение), затем как@.>косвенный через B с постинкрементом. Как@, затем увеличить поле B этой ячейки.*косвенный через A. Как@, но через поле A.{косвенный через A с предекрементом. Как<, через поле A.}косвенный через A с постинкрементом. Как>, через поле A.
Примечание: в ICWS'94 у непосредственного операнда тоже есть указатель — сама инструкция. MOV.I #0, 1 копирует всю инструкцию на одну ячейку вперёд. Это классический Imp.
5. Модификаторы
Модификатор определяет, какие поля опкод читает и записывает. Для инструкции с A-указателем a и B-указателем b:
.Aполе A из a в поле A в b..Bполе B из a в поле B в b..ABполе A из a в поле B в b..BAполе B из a в поле A в b..Fоба поля: A в A и B в B..Xоба поля накрест: A в B и B в A..Iвся инструкция, включая опкод и режимы. Только дляMOV; в арифметике.Iработает как.F.
Модификатор по умолчанию, если он не указан:
DAT,NOP:.FMOV,CMP,SEQ,SNE:.AB, если A непосредственный,.B, если B непосредственный, иначе.IADD,SUB,MUL,DIV,MOD:.AB, если A непосредственный,.B, если B непосредственный, иначе.FSLT,LDP,STP:.AB, если A непосредственный, иначе.BJMP,JMZ,JMN,DJN,SPL:.B
6. Опкоды
DATУбивает выполнивший его процесс. Также обычная ячейка данных и бомба.MOVКопирует из A в B.ADDB = B + A (согласно модификатору).SUBB = B - A.MULB = B * A.DIVB = B / A. Деление на ноль убивает процесс; с.F/.X/.Iвторое поле всё равно записывается, если его делитель не ноль.MODB = B mod A. То же правило про ноль, что и уDIV.JMPПереход по A-указателю.JMZПереход на A, если проверяемые поля B равны нулю. С.F/.X/.Iнулю должны быть равны оба поля.JMNПереход на A, если проверяемые поля не равны нулю. С.F/.X/.I: если хотя бы одно не ноль.DJNУменьшить проверяемые поля B, затем перейти на A, если результат не ноль. Классический счётчик цикла.SPLДобавить новый процесс по A-указателю, в очередь после текущих процессов. Текущий процесс продолжает со следующей инструкции. Не болееMAXPROCESSES(8000) на воина; сверх этогоSPLничего не делает.SEQПропустить следующую инструкцию, если A равно B (согласно модификатору).CMP— синоним.SNEПропустить следующую инструкцию, если A отличается от B.SLTПропустить следующую инструкцию, если A меньше B (без знака, 0..7999).LDPЗагрузка из p-space: значение p-space[A-значение] записывается в цель B.STPЗапись в p-space: значение A записывается в p-space[B-значение].NOPНичего не делает (но оба операнда всё равно вычисляются).
Вся арифметика ведётся по модулю CORESIZE. Числа в ядре всегда лежат в диапазоне 0..7999; -1 хранится как 7999.
7. Директивы
ORG labelОткуда начинается выполнение. По умолчанию: с первой инструкции.END [label]Конец исходника; необязательная метка работает какORG.name EQU exprТекстовая константа: каждое последующее вхождение name заменяется на expr. Может содержать операнд с режимом (EQU #7) или даже опкод.FOR n ... ROFПовторяет блок n раз. Метка на строкеFORили на строке прямо перед ней служит счётчиком (с 1).&counterвнутри блока раскрывается в двузначный номер, так чтоlabel&iдаётlabel01,label02, ... БлокиFOR 0пропускаются — удобно для длинных комментариев.PIN nИдентификатор p-space. Два воина с одинаковымPINделят общий p-space (кроме ячейки 0).
ORG loop
loop FOR 3
MOV.I #0, &loop
ROF
8. Выражения
Операторы, от высшего приоритета к низшему: унарные - + !, затем * / % (целочисленные, с отбрасыванием дробной части), затем + -, затем сравнения == != < > <= >=, затем && и ||. Скобки допускаются. Сравнения дают 1 или 0.
Предопределённые константы: CORESIZE, MAXCYCLES, MAXPROCESSES, MAXLENGTH, MINDISTANCE, PSPACESIZE, ROUNDS, WARRIORS (2), VERSION (92), CURLINE (индекс текущей инструкции).
Метка в выражении означает расстояние от текущей инструкции до неё, поэтому label+1 — это ячейка после label, а start-target — просто число ячеек.
9. P-space
У каждого воина есть личный массив из PSPACESIZE (500) ячеек, который сохраняется между раундами матча. Индексы зациклены. Ячейка 0 особая и всегда личная: в начале каждого раунда в ней лежит предыдущий результат — 0, если воин погиб, иначе число выживших воинов (1 = победа, 2 = ничья). В первом раунде в ней CORESIZE-1.
LDP.AB #0, x загружает этот результат в поле B ячейки x. STP.B x, #1 сохраняет поле B ячейки x в ячейку 1. Так воин может сменить стратегию после поражения.
10. Правила матча
- Размер ядра 8000, макс. циклов 80000, макс. процессов 8000, макс. длина 100, мин. расстояние 100, p-space 500.
- Матч состоит из N раундов. Стартовые позиции зависят от сида матча и номера раунда; тот же сид воспроизводит тот же матч.
- Очки: каждый выживший в раунде получает (W*W-1)/S очков, где W — число воинов, S — выжившие; остальные 0. Для двух воинов это 3 за победу, по 1 за ничью, 0 за поражение.
- В ядре могут сражаться до 36 воинов (лимит слотов задаётся в настройках); они стартуют не ближе MINDISTANCE друг от друга, один бот может занимать несколько слотов.
- Воин, ходящий первым, меняется каждый раунд.
- Тестовый режим (один бот) сообщает ВЫЖИЛ или ПОГИБ по достижении
MAXCYCLES.
11. Типовые приёмы
- Имп:
MOV.I #0, 1. Бомбами не убить, но в одиночку никогда не побеждает. - Имп-гейт:
JMP 0, <gate. Каждый цикл уменьшает ячейку перед собой, превращая прибывающих импов вMOV.I 0, 0. - Бомбер (Dwarf): цикл
ADD,MOV,JMP, который сбрасываетDATс шагом, покрывающим всё ядро. - Сканер: сравнивает ячейки с нулём и бомбит то, что нашёл.
- Репликатор (paper): копирует себя в другое место циклом
MOV, затем делает тудаSPL. - SPL-бомба: ковёр из
SPL 0; воин, попавший на него, забивает свою очередь процессов бесполезными циклами. - Загрузка (boot): скопировать воина подальше и запустить копию, а исходный код оставить приманкой.