ЗОРЯ

Головна/Завдання/Частина 2/Наказ

Наказ: грамота, словник, лінтер, зразки

Наказ - обов'язкова грамота бота: файл nakaz.yaml у каталозі бота. Він веде ваш слот, коли бот мовчить 6 тактів поспіль або не підключився до партії, і віддає керування першим непорожнім кадром команд, який розібрав кодек. Коли і як наказ бере і віддає слот, що він уміє у світі - правила, 01-rules.md, розділ 23. Тут - формат, словник, лінтер і зразки.

опубліковано 28.09

Як наказ здається в залікові партії, визначає частина 4 завдання. У частині 2 наказ грає в локальному пакеті (06-local-pack.md): вузол садить ваш nakaz.yaml на ваше місце.

1Формат

YAML, упорядкований список правил "коли - що":

YAML
nakaz: 1
name: "експансія"
rules:
  - do: hold_net
  - when: "my_drones < 4"
    do: { build_drone: { cls: tkach, upto: 4 } }
  - do: { extend_to: { target: nearest_cluster } }
  - when: "every 60"
    do: { scan_at: { target: densest_contacts, every: 60 } }
    priority: drop-on-budget
  • Верхні ключі: nakaz (версія, ціле 1; запис 1.0 не проходить), name (непорожній рядок), rules (від 1 до 24 правил). Інших ключів немає.
  • Правило: when (необов'язково; без нього правило спрацьовує завжди), do (рівно один крок), priority (keep типово або drop-on-budget).
  • Файл - не більше 16 КіБ.

Умова when - рядок-вираз ("my_drones < 4"), список all: [...] або any: [...] на один рівень вкладеності, вираз ключем із порожнім значенням ({ my_drones < 4: }) або селектор ключем зі значенням ({ every: 40 }). Порівняння: <, >, <=, >=; записи =<, =>, ==, = відкидаються.

Крок do - ім'я рядком, { ім'я } або { ім'я: { параметри } }. Сота пишеться {q, r} або [q, r].

2Як грамота грає такт

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

Команди грамоти - звичайні команди слота: той самий канал, ті самі тарифи, шторм, стеля кадру і правила світу. Грамота не подає команд понад залишок каналу і вух. Типово priority: keep: правило, якому забракло байтів, замикає всі правила нижче до наступного такту. На старті канал - 141 байт (підлога 96 і дві стартові голки), тобто в такт уміщаються чотири команди по 32 байти, і п'яте правило списку при чотирьох, що спрацювали вище, мовчить. priority: drop-on-budget поступається байтами нижнім правилам без черги - ставте його на те, що можна пропустити (спалах, погляд), а не на будівництво. Крок idle обриває список.

every N рахує такти світу: перший кадр приходить із тактом 1, і every 40 уперше справджується на 40-му такті.

Пам'ять грамоти - тільки курсори правил: такт останньої дії, ціль, машина і коротка черга сот, куди воротар щойно не пустив: три останні відмови, кожна на 60 тактів, після чого сота знову допустима. Це єдине, що грамота бере з вердиктів; коду причини вона не читає. Черга потрібна проти чужої голки, якої немає в кадрі: без неї правило слало б туди одну й ту саму команду до кінця партії. Пам'ять живе одну партію.

3Словник

Словник закритий: ім'я поза словником - помилка лінтера з підказкою найближчого. Точний список із границями параметрів - vocab.ts пакета @zoria/nakaz (node_modules/@zoria/nakaz/dist/vocab.js).

Кроки

КрокПараметриЩо робить
hold_net-лагодить розрізи своєї мережі
extend_totarget: nearest_cluster, richest_visible, hex, ring; hex при target: hexтягне нитку до цілі: нічию руїну - прогрівом, порожню соту - будівництвом
build_dronecls: tkach, klinok; upto: 1-8замовляє машину класу, поки їх менше за upto; тримає вагу рою і заряд
build_relay_athexставить голку в названій соті
scan_attarget: hex, densest_contacts; hex; every: 1-1200спалах по цілі
keep_distancefrom: keeper_line, storm; margin: 1-30забороняє правилам нижче цілі, ближчі ніж на margin сот до межі або до плями шторму
feed_networktarget: richest_onlineживить найдохіднішу голку з тих, що горять і не живляться
take_contractprefer: nearestведе Ткача до відкритого замовлення і тримає його
watch_attarget: contract, densest_contacts, hex; hex; keep: 1-4096ставить фокус; повторює команду тільки при зсуві цілі
jam_nearesttarget: nearest_visible, richest_visible; mode: hold, passглушить видиму чужу нестартову голку, не партнера і не Ключника
ram_threattarget: jammer, nearest_visibleтаранить видиму чужу машину, не партнера
return_homecls: any, tkach, klinokповертає вільну машину до вогнища
idle-нічого; обриває список

repeat_in_storm - резерв: лінтер його відкидає.

Селектори

СелекторЗначенняЗміст
tick0-100000номер такту
every N-такт кратний N
my_relays_online0-200своїх голок горить
my_relays_embers0-200своїх голок догоряє (вага менша за 1)
my_relays_crowned0-200своїх голок у вінці (вага більша за 1)
my_energy0-100000свій заряд
my_drones0-32своїх машин
net_cutпрапорецьє відрізана голка
in_mercyпрапорецьслот під милістю Ключника
dawnпрапорецьзоря настала
contract_open(any)прапорецьрозкрито замовлення; види hold і reach зарезервовано
storm_incoming1-100через скільки тактів пляма накриє вузол живої мережі; 0 - уже накрила, більше 100 - не накриє

Як читати деякі імена

  • richest_visible у extend_to - найдохідніша з нічиїх досяжних руїн. Дохід руїни рахується за маніфестом; "видима" тут про володіння: руїна, яку видно чужою, з вибору випадає. Читати "видима" як "та, що прийшла в кадр", не можна.
  • ram_threat: { target: jammer } - найближчий видимий Клинок: чи глушить він зараз, кадр не несе.
  • take_contract веде замовлення "Утримати лінію" (01-rules.md, розділ 18): число тактів утримання береться з pub.order.holdTicks, свій лічильник і причина скидання - з mine.order. Після захоплення всіх трьох голок крок лагодить розрив ланкою мережі або спрямовує вільного Клинка на видиму загрозу поруч із цілями; свого Клинка крок не будує.
  • extend_to: { target: ring } тягне нитку до голок кільця. До зорі кроки наказу не ведуть машин усередину межі, якщо слот не під милістю; правило "на зорі - у кільце" пишеться when: dawn і ставиться вище звичайного зростання, інакше вільного Ткача забере правило вище.
  • keep_distance from: storm замикає для правил нижче соти, ближчі за margin до маски плями зараз і на десять тактів уперед. from: keeper_line після зорі заборони не ставить.
  • watch_at з keep: машини-очі запалюються по черзі від фокуса, поки у вухах залишається не менше keep байтів; типово - scanCost маніфесту, рівно один спалах. Вогнище і голки під keep не гасяться.

Чого грамота не вміє

Торги, угоди, кличі і halt грамоті недоступні. Назвати гравця або приховану машину не можна: цілі вибираються з кадру спостереження. Чинну угоду грамота поважає: голок партнера не глушить, машин партнера не таранить. Умов про знахідки немає: розкладка знахідок - рельєф маніфесту, і гиблу соту будівництво оминає саме.

4Лінтер: три ступені

shell
npm run lint:nakaz

Команда шаблону (zaria-kit lint nakaz.yaml) проганяє всі три ступені і друкує помилки й попередження з кодом у квадратних дужках, мовою каталогу бота (zaria.lang, прапорець --lang); коди від мови не залежать. Рядок виводу:

схема
увага [duplicate] (правило 3, рядок 6): правило повторює правило 2: та сама умова, той самий крок - за вільної машини це друга така сама команда за такт

Ті самі ступені доступні з коду:

JavaScript
import { readFileSync } from 'node:fs';
import { createEngine } from '@zoria/engine';
import { lintNakaz, reportIssues } from '@zoria/nakaz'; // 1-2: схема і словник
import { lintNakazRun } from '@zoria/sdk/nakaz';        // 3: прогін

const bytes = readFileSync('nakaz.yaml');
const { issues } = lintNakaz(bytes, { lang: 'uk' });    // тексти: 'uk' (за замовчуванням) або 'en'
console.log(reportIssues(issues, 'uk'));                // ті самі рядки, що друкує zaria-kit lint
const report = lintNakazRun(createEngine, bytes, { lang: 'uk' });
  1. Схема: YAML розібрався, форма грамоти правильна, правил не більше 24, файл не більше 16 КіБ.
  2. Словник: кожен крок і селектор існує, параметри в границях, резерви не зайняті. Попередження: no-hold-net (немає лагодження мережі), dead (правило не спрацює ніколи), cond-trivial, scan-once, fence-all, unreachable, duplicate, param-hex-idle.
  3. Прогін: грамота грає повний бій (жереб 2026, 1200 тактів, місце A проти двох порожніх місць). Відмова - якщо інтерпретатор кинув виняток або жодну команду не прийнято. Попередження refused - якщо воротар відкинув понад 30 % команд; прогін називає найчастішу соту відмови: грамота цілиться туди, де світ відмовляє.

Грамота з помилкою ступеня 1 або 2 не грає зовсім. Проганяйте всі три ступені перед кожним комітом грамоти.

5Зразки

Шість грамот лежать у пакеті SDK: node_modules/@zoria/sdk/samples/. Зразок у шаблоні бота - копія minutes.yaml.

ФайлІм'яПрофіль
cherepaha.yamlчерепахаоборона: лагодження мережі, 3 Ткачі, дистанція 3 від межі, мережа до 12 голок, спалах раз на 120 тактів
expansion.yamlекспансіязростання: 4 Ткачі, нитка до найближчого скупчення руїн, спалах раз на 60 тактів
ekonomka.yamlекономкаспершу заряд: машина тільки при заряді понад 120, зростання до найдохіднішої руїни
minutes.yamlхвилинидохід: 4 Ткачі до 600-го такту, замовлення після, мережа до 24 голок, живлення до зорі, на зорі - кільце
contract.yamlзамовленнязамовлення: take_contract і watch_at при відкритому замовленні, повернення машин перед зорею, на зорі - кільце
pressure.yamlтисктиск: Клинки при заряді понад 120, завада до 1080-го такту, потім таран Клинків суперника

Усі шість проходять три ступені лінтера; коментарі всередині файлів пояснюють ціну кожної ролі.

6Як побачити наказ у дії

  • Зіграйте локальний пакет, де ваше місце веде тільки грамота: npm run play -- --fast --bot nakaz (06-local-pack.md). Грамота присягає як бот, і частки рахуються звичайно.
  • Зупиніть свого бота посеред партії локального пакета: через 6 тактів мовчання слот візьме наказ, у журналі партії з'явиться епізод [слот, від, до]. Епізоди відтворюються реплеєм біт у біт.
  • Порівняйте грамоти на кількох жеребах: npm run play -- --fast --bot nakaz --packs 8 друкує частки за кожним пакетом і суму. Суперниками можуть бути й грамоти: --vs черепаха,тиск або шляхи до файлів .yaml.