COREDUEL

ОБУЧЕНИЕ

Добро пожаловать в Core War

Это игра для программистов. Вы пишете бота на Redcode — крошечном ассемблере — и помещаете его в ядро: кольцо из 8000 ячеек памяти, общее с ботами противника. Каждый бот выполняет по одной инструкции за ход. Стен нет и правил боя нет: кто затрёт чужой код или просто проживёт дольше, тот выиграл раунд. Арена показывает всё ядро в виде пикселей, так что видно, как ваша программа ползёт, бомбит и сканирует, в реальном времени.

Уроки начинаются с бота из одной строки и заканчиваются воином, который меняет стратегию между раундами. Через список шагов можно перескочить вперёд или вернуться в любой момент. В каждом уроке есть готовый бот: откройте его в редакторе, запустите, потом измените одно число и запустите снова.

Урок 0: Как играть

Урок 1: Имп

Ядро — это кольцо из 8000 ячеек. Процесс выполняет одну ячейку за ход и переходит к следующей. MOV копирует ячейку. С модификатором .I копируется вся инструкция, а #0 означает «эта самая инструкция».

;redcode-94
;name Imp
;author Tutorial
;strategy Copies itself one cell forward, forever.
        MOV.I   #0, 1
        END

Каждый ход Imp копирует себя в следующую ячейку и затем выполняет эту копию. Он ползёт по всему ядру, затирая всё на своём пути. Одним DAT его не убить (он просто копирует себя поверх), но и сам он никого не убивает: обычно всё кончается ничьей.

Запустите его против Dwarf из библиотеки и посмотрите на зелёный след.

Урок 2: Dwarf, бомбер

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

;redcode-94
;name Dwarf
;author Tutorial
;strategy Drops a DAT every 4 cells.
        ORG     start
target  DAT.F   #0, #0
start   ADD.AB  #4, target
        MOV.AB  #0, @target
        JMP     start
        END

Почему 4? 8000 делится на 4, поэтому указатель проходит 2000 ячеек и возвращается ровно в target, где и так DAT. Свои три строки кода Dwarf не бомбит никогда.

Урок 3: Модификаторы

Модификатор выбирает, какие поля копируются. Сравните:

Dwarf выше использует ADD.AB, чтобы непосредственное 4 (поле A) попало в поле B ячейки target. А вот Dwarf, который кидает целые инструкции DAT через MOV.I. У его бомбы операнды с предекрементом: когда вражеский процесс её выполняет, оба операнда всё равно вычисляются до гибели процесса, так что страдают ещё две далёкие ячейки.

;redcode-94
;name Dwarf Plus
;author Tutorial
;strategy Full-instruction DAT bombs with decrementing fields.
        ORG     start
ptr     DAT     #0, #0
bomb    DAT     <2667, <5334
start   ADD.AB  #4, ptr
        MOV.I   bomb, @ptr
        JMP     start
        END

Сравните Dwarf и Dwarf Plus несколько раз с одним и тем же сидом.

Урок 4: Режимы адресации и имп-гейт

Оба операнда вычисляются в каждой инструкции, даже те, что JMP игнорирует. < уменьшает поле B адресуемой ячейки перед использованием. JMP, который раз за разом уменьшает одну ячейку, — это имп-гейт: когда туда попадает копия импа (MOV.I 0, 1), её поле B становится 0, она копирует саму себя на место, процесс уходит в пустое ядро и погибает.

;redcode-94
;name Gated Dwarf
;author Tutorial
;strategy Dwarf whose loop instruction also gates the cell before it.
        ORG     start
gate    DAT     #0, #0
target  DAT     #0, #0
start   ADD.AB  #4, target
        MOV.AB  #0, @target
        JMP     start, <gate
        END

Запустите Gated Dwarf против Imp: теперь Imp обычно погибает, а не сводит к ничьей (гейт срабатывает раз за проход цикла, так что нужно несколько кругов). Поскольку target и start стоят на 1 и 2 ячейки после gate, а бомбы падают с шагом, кратным 4, от target, сам гейт никогда не попадает под бомбу.

Урок 5: SPL и имп-кольцо

SPL запускает второй процесс. Процессы одного воина ходят по очереди. Три импа на расстоянии 2667 ячеек друг от друга (3 x 2667 = 8001, на одну больше размера ядра) догоняют друг друга и продвигаются на одну ячейку каждые три копирования: это имп-кольцо. Его гораздо труднее остановить гейтом, чем одиночного импа.

;redcode-94
;name Imp Ring
;author Tutorial
;strategy 3-point imp ring launched with SPL.
step    EQU     2667
        ORG     start
start   MOV.I   imp, imp+step
        MOV.I   imp, imp+2*step
        SPL     imp+step
        SPL     imp+2*step
        JMP     imp
imp     MOV.I   #0, step
        END

EQU задаёт константу. imp+2*step — выражение, его вычисляет ассемблер. Попробуйте step = 4001 с двумя импами.

Урок 6: Сканеры

Сканер сначала ищет врага, а потом бомбит. Пустое ядро — это DAT 0, 0, поэтому ненулевое поле B означает «здесь что-то есть». JMZ делает переход, если проверяемое поле равно нулю.

;redcode-94
;name Scanner
;author Tutorial
;strategy Scan every 3039th cell for a non-zero B-field, bomb it.
step    EQU     3039
        ORG     scan
ptr     DAT     #0, #0
bomb    DAT     #0, #0
scan    ADD.AB  #step, ptr
        JMZ.B   scan, @ptr
        MOV.I   bomb, @ptr
        JMP     scan
        END

Против Dwarf сканер быстро находит код бомбера. Против Imp он тратит бомбы на след. Настоящий сканер использует нечётный шаг, который никогда не проходит одну ячейку дважды, и перепрыгивает свой собственный код.

Урок 7: Загрузка (boot)

Загруженный код — неподвижная мишень. Скопируйте настоящего воина подальше, перейдите туда, и пусть враг тратит бомбы на загрузчик. Цикл MOV с указателями с предекрементом копирует задом наперёд; DJN с непосредственным операндом считает итерации в собственном поле B.

;redcode-94
;name Boot Dwarf
;author Tutorial
;strategy Copies Dwarf 3000 cells away and runs the copy.
dist    EQU     3000
len     EQU     4
        ORG     boot
boot    MOV.I   <src, <dst
        DJN     boot, #len
        JMP     dwarf+1+dist
src     DAT     #0, dwarf+len
dst     DAT     #0, dwarf+len+dist
dwarf   DAT.F   #0, #0
        ADD.AB  #4, dwarf
        MOV.AB  #0, @dwarf
        JMP     dwarf+1
        END

Урок 8: P-space, обучение между раундами

Каждый воин хранит 500 личных ячеек между раундами матча. Ячейка 0 сообщает, чем кончился прошлый раунд: 0 = мы погибли, 1 = мы победили, 2 = ничья. LDP загружает ячейку, STP сохраняет. Этот воин запоминает в ячейке 1, какую стратегию он использовал, и меняет её после каждого поражения.

;redcode-94
;name Switcher
;author Tutorial
;strategy Runs Dwarf or an imp ring; switches after a lost round.
step    EQU     2667
        ORG     start
res     DAT     #0, #0
cur     DAT     #0, #0
start   LDP.AB  #0, res         ; res.B = last result
        LDP.AB  #1, cur         ; cur.B = strategy 0 or 1
        JMN.B   keep, res       ; did not lose: keep it
        MUL.AB  #-1, cur        ; toggle 0 <-> 1
        ADD.AB  #1, cur
keep    STP.B   cur, #1         ; remember it
        JMZ.B   dwarf, cur
        MOV.I   imp, imp+step
        MOV.I   imp, imp+2*step
        SPL     imp+step
        SPL     imp+2*step
        JMP     imp
imp     MOV.I   #0, step
target  DAT.F   #0, #0
dwarf   ADD.AB  #4, target
        MOV.AB  #0, @target
        JMP     dwarf
        END

Сыграйте матч из 10 раундов против Scanner и посмотрите, как стратегия переключается после поражений. Настоящие p-space-воины («pswitchers») выбирают из нескольких компонентов и составляют заметную часть современной турнирной игры.

Что дальше