Sneekie

Як думає бот

Сторінка Бот не відтворює запис. Вона запускає справжню гру, Rust/WebAssembly-планувальник через bot-engine.js, невеликий пул workers, коли браузер повідомляє про вільні ядра, і тонкий драйвер у bot.js в одній сторінці. Превʼю на головних сторінках завантажують той самий Rust/WebAssembly-планувальник і, як і сторінка Bot, чекають на WebAssembly перед керуванням грою. Планувальник повертає DOS-код стрілки, а драйвер натискає ту саму клавішу, що й гравець.

Лабіринт Sneekie зі світними маршрутами, небезпеками і шляхами рішень бота.
Важлива межа. Справжня гра 1988 року на сторінці Грати залишається вірною BASIC-джерелу. Бот — лише сучасне доповнення в bot.html; він читає живі змінні порту і натискає клавіші так, як це робив би гравець. Окрім одного службового запису — переходу до вибраного LEVEL — він не чіпає правила і рушій гри.

1. Де працює бот

Сторінка бота напряму розміщує справжню гру. Вона завантажує game.js, потім bot-engine.js (WebAssembly-планувальник і координатор workers), а потім bot.js. Драйвер чекає, доки bot-engine.wasm буде готовий, і лише тоді натискає хід. Драйвер і далі керує вкладками, перезапусками, швидкістю і натисканнями клавіш:

const stalled = idle > 120 || (idle > 24 && repeats >= 3);
const forceRisk = stalled || movesSincePickup >= 250;
let sc = await planner.decide({ idle, looping, headTrail, budgetMs: routeBudget(forceRisk), forceRisk });
if(sc === null) sc = randomLegalScancode();   // планувальник здався або спрацював запобіжник
if(sc !== null) pushKey(keyOf[sc]);           // інакше: жодного легального ходу -> спалах застрягання

Таке розміщення навмисне. Гра використовує глобальні const і let, зокрема T, BTEL, ETEL, LEVEL, HART, KLAVER і pushKey(). Ці імена видимі скриптам на тій самій сторінці, але не є звичайними властивостями window. Так бот може викликати peek(), читати T[BTEL] і натискати клавіші через pushKey(), ніби він є частиною сторінки.

Перехід рівня

Він закриває рівень 1, встановлює LEVEL = TARGET - 1, один раз натискає F10 і дозволяє власному циклу гри увійти у вибраний рівень.

Snapshot-планувальник

Кожне рішення копіює CP437-поле гри в компактний масив, а потім пошук працює з цим snapshot замість повторного читання відрендереного стану гри.

Справжнє введення

Він надсилає розширені рядки клавіш у стилі DOS: вгору ' H', вниз ' P', ліворуч ' K', праворуч ' M'.

Логіка перезапуску

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

2. Дані, які він бачить

Бот використовує ту саму модель екранної памʼяті, що й гра. У нього немає акуратного списку "обʼєктів"; він читає символи з VRAM через peek(offset). Тіло змії відновлюється з ігрового масиву T, від індексу хвоста ETEL до індексу голови BTEL.

Значення гри Що це означає для бота Вплив на планування
32Порожня клітинкаМожна входити.
3 СерцеЇжа +10, змія росте.
5 ТрефаЇжа +25, змія росте.
1 СмайликКоштує 50 очок, усе одно подовжує змію та породжує заміну; бот витрачає його свідомо, коли саме це тримає шлях назад до хвоста відкритим.
10 КаміньМожна штовхнути, якщо клітинка позаду порожня.
24, 26, 27 ↑→←Рухомі стрілиВважаються смертельними, включно з клітинками, в які вони ось-ось увійдуть.
219Голова зміїГра використовує її для зіткнень; бот також відстежує голову через T[BTEL].

На початку кожного рішення bot-engine.js копіює CP437-символи, зміщення змії з T, таблицю стріл D і недавній слід позицій голови у сталі WebAssembly-буфери. Коли workers доступні, кожен має власний WebAssembly-instance і отримує той самий snapshot, тому може думати без спільного змінного стану гри. Для уявних маршрутів Rust-планувальник записує лише відмінності, наприклад проштовхнутий камінь або клітинку хвоста, що звільнилася, замість копіювати весь VRAM-масив для кожної гілки.

3. Один цикл рішення

Кожен хід бота проходить одну й ту саму драбину. У single-thread режимі перемагає перша стратегія, що повертає напрямок. У worker-режимі координатор запускає базовий планувальник поруч із глибшими профілями пошуку: глибший повний планувальник, глибший терміновий/фінішний планувальник і, коли вистачає ядер, агресивний вихід із застою. Результати позначаються стратегічним tier, тому глибший worker може замінити базовий хід лише коли доведе такий самий або кращий тип ходу. Після цього бот натискає клавішу, чекає відповідно до повзунка швидкості й рахує з нового живого стану. Над драбиною стоять два зобов'язання. В ендшпілі з'являється нульова сходинка: коли лишилося кілька предметів, планувальник спершу пробує сертифікувати один повний симульований тур, що з'їдає все, і бере його цілком. А коли перемагає пошук їжі, планувальник повертає весь свій сертифікований маршрут — драйвер відтворює його крок за кроком, з повторною перевіркою кожного ходу, замість того щоб переплановувати з нуля щотакту.

1. Скинути кеш небезпеки

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

2. Близька чиста їжа

Спершу пробується неглибокий пошук близьких сердець або треф, без поїдання смайликів.

3. Простір для дихання

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

4. Ширші маршрути

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

5. Тиск петлі

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

6. Локально вижити

Як останній безпечний запасний варіант він обирає одноходовий рух, який ще проходить локальні перевірки.

7. Найменш поганий хід

Якщо жоден маршрут не доведено, планувальник усе одно повертає найкращий негайно легальний хід. Бот ніколи сам не натискає Escape.

const decide = options => {
  model = capture();
  resetDanger();
  const urgent = options.idle >= 18 || options.looping;
  const forceRisk = options.forceRisk === true;
  routeDeadline = now() + options.budgetMs;
  let proved = null;
  try {
    proved = forceRisk
      ? pressureFood(true, true) ??
        nearFood(true) ??
        routeFood(true)
      : nearFood(false) ??
        routeFood(false) ??
        pressureFood(false, urgent);
  } finally {
    routeDeadline = 0;
  }
  return forceRisk
    ? proved ?? pressureStep([false, true]) ?? riskyMove() ??
      survivalMove([false, true]) ?? lastChanceMove()
    : proved ?? (urgent ? pressureStep() : null) ??
      survivalMove() ?? lastChanceMove();
};

Прапорець looping береться з короткого сліду позицій голови. WebAssembly-планувальник також отримує цей слід напряму, тому симульовані маршрути, що знову йдуть гарячими недавніми клітинками, платять стратегічний борг, якщо вони не наближають їжу або не зберігають доступ до хвоста. Якщо поточне зміщення голови повторювалося кілька разів, а рахунок не змінювався, бот вважає просте ходіння за хвостом підозрілим і переходить до тиску на їжу. Гілка forceRisk вище — це власна драбина втечі планувальника — найагресивніший захват їжі, крок тиску з прокопуванням крізь камені, ходіння за хвостом для довгої змії, а потім локальні ходи виживання, вчетверо збільшуючи борг за недавні клітинки, щоб кожен форсований хід заходив у свіжу клітинку. Живий драйвер вмикає цю гілку, коли змія застрягає в циклі або надто довго не їсть справжній pickup, і дає планувальнику більше часу перед випадковим відкотом. breathingMove() працює інакше: він запускається перед далекими маршрутами до їжі, коли поточна зона тісна, і цінує доступ до хвоста, виходи та глибину виживання вище за негайні очки. У такому тісному режимі смайлик може перемогти далеку чисту їжу, якщо маршрут через нього лишає шлях до рухомого хвоста. Якщо планувальники не встигають або не можуть сертифікувати маршрут, pressureStep() обирає безпечний негайний хід, що скорочує відстань до доступної їжі. Якщо навіть це не спрацьовує, lastChanceMove() повертає найменш поганий негайно легальний хід; сторінки бота продовжують ходи, доки змія справді не застрягне (жодного легального ходу) або рівень не очиститься.

4. Модель небезпеки стріл

Бот інакше ставиться до рівнів зі стрілами, бо клітинка може бути порожньою зараз і смертельною через кілька тактів. Функція danger(offset) позначає ціль небезпечною, якщо стріла вже там, якщо прогнозована майбутня стріла зайде туди або якщо стріла з обгортанням знову зʼявиться там.

Поточна клітинкаpeek(o) дорівнює 24, 26 або 27.
Стріли вгору під клітинкою піде вгору в неї.
Стріли праворуч ліворуч від клітинки піде праворуч у неї.
Стріли ліворуч праворуч від клітинки піде ліворуч у неї.
Краї з обгортаннямОкремі перевірки покривають стріли, що стрибають із далекого краю назад на початок.

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

5. Симуляція ходу

Головний примітив планувальника — move(state, scancode, allowSmile). Він повертає новий уявний стан, якщо хід легальний, або null, якщо хід зіткнеться, розвернеться назад, помре або штовхне неможливий камінь.

Правило Як бот його застосовує
Без миттєвого розвороту Якщо запитаний код клавіші протилежний поточному напрямку, відхилити його.
Без небезпеки Якщо danger(next) істинний, відхилити хід.
Без удару в себе Якщо наступна клітинка є в симульованому bodySet, відхилити її.
Смайлики опційні Відкидати смайлики під час чистого пошуку; пізніше цінувати їх лише тоді, коли ріст компенсується шляхом назад, доступом до кімнати або кількома доступними підбираннями.
Камені штовхаються Камінь може зрушити на одну клітинку вперед лише тоді, коли ця клітинка порожня і не зайнята симульованим тілом.
Ріст Серця, трефи і смайлики ростять тіло. Порожні ходи спершу прибирають хвіст.
Перший хід збережено Кожна симульована гілка памʼятає тільки першу реальну клавішу, яку треба натиснути, якщо ця гілка переможе.

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

6. Пошук їжі

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

Планувальник Коли запускається Обмеження Головний ухил
nearFood() Перед кожним ширшим пошуком; у тісних позиціях прохід зі смайликом може йти перед далекою чистою дорогою. Глибина 10 зазвичай, 14 коли лишається 6 або менше предметів. Дуже сильний штраф за відстань, тому безпечна близька їжа перемагає.
routeFood() Нормальний обережний пошук маршруту. Глибина 98 зазвичай, 145 коли лишається 6 або менше предметів; до 3500-7000 станів. Виживання спершу: доведений вихід, доступ до хвоста, майбутні виходи і відкритий простір важливіші за короткість.
pressureFood() Коли бот простоює, ходить петлею або коли звичайні безпечні пошуки їжі не допомогли. Глибина 85-155 залежно від кінцівки рівня і терміновості; до 5000-9000 станів. Мʼякші перевірки виживання і менший штраф за відстань, щоб ламати кола без хапання очевидних пасток.

У фіналі рівня пошук стає глибшим, бо останні кілька сердець і треф часто там, де пастки і довгі обхідні шляхи найболючіші. Раніше на рівні зазвичай вистачає неглибшого пошуку, і він тримає бота швидким. Смайлики все одно лишаються другорядними цілями: бот хоче чисту їжу, але може зʼїсти смайлик, якщо це тримає шлях назад відкритим або відкриває корисну групу наступної їжі.

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

7. Перевірки пасток

Дістатися серця недостатньо. Більшість поганих ботів для змійки гине тому, що йде найкоротшим шляхом до їжі і занадто пізно помічає, що їжа лежала в глухому куті. Тому бот Sneekie перевіряє маршрут-кандидат кількома сигналами виживання, зокрема вільними дверима кімнат і раннім самозамиканням у малих кімнатах або вузьких проходах.

Кількість виходів

legalCount() питає, скільки ходів буде доступно відразу після їжі. Нуль виходів відхиляє маршрут; коридори з одним виходом сильно штрафуються.

Досяжний простір

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

Доступ до хвоста

Та сама заливка простору записує, чи може симульована голова дістатися хвоста. Якщо хвіст досяжний, змія, ймовірно, має рухомий шлях втечі.

Глибина виживання

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

Шлях виходу

escapeProof() симулює додаткові ходи після підбирання їжі і вимагає шлях у відкритий простір або назад до рухомого хвоста.

Двері кімнат

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

Ворота повернення

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

Недавній слід

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

Локальне прибирання

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

Регіональний план

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

Мінімальна вимога до простору росте разом із довжиною змії: чим довша змія, тим більше місця кандидат має залишити позаду. Нормальні маршрути до їжі мають після підбирання довести шлях назад до рухомого хвоста; терміновий тиск може взяти дуже велику відрізану зону лише як останній засіб, і лише у своєму проході зі смайликами — чистий маршрут ніколи не віддає шлях назад, бо міст за −50, який зберігає хвіст, завжди вигідніший. Малі зони, одноклітинні шийки, заблоковані двері кімнат, погіршені дверні смуги, крихкі ворота повернення і раннє відрізання хвоста фільтруються до того, як короткочасна винагорода може перемогти. Останнє слово за вартою самозамикання: на кожному рівні, включно з ендшпілем, хід, що заганяє голову в кишеню, замалу для тіла, переспрямовується в найпросторіший безпечний напрямок — а коли єдиний вихід лежить через смайлик, бот радше платить −50, ніж замуровує себе.

8. Запасні ходи

Навколо нормальних безпечних планувальників їжі бот має чотири запасні поведінки. Вони не дають йому завмерти, а остання — навмисна дисципліна ходіння за власним хвостом для довгої змії.

Запасний варіант Мета Поведінка
breathingMove() Уникати кишень Запускається перед далекими маршрутами до їжі, коли поле тісне або змія довга. Віддає перевагу доступу до хвоста, майбутнім виходам, вільним дверям кімнат і відкритому простору.
pressureFood() Ламати петлі Запускається, коли idle >= 18 або недавній слід голови показує повторені позиції. Шукає їжу з легшими перевірками виживання, спершу без смайликів, потім зі смайликами.
tailChaseMove() Фінал довгої змії Щойно тіло сягає близько 80 клітинок на відкритих полях або близько 24 на тісних лабіринтах, обирає безпечний хід з найглибшим гарантованим виживанням, тримаючи хвіст досяжним — на практиці він іде за власним хвостом і заповнює простір, замість замурувати себе. Короткі змії це пропускають.
survivalMove() Короткий міст Легший останній запасний варіант. Оцінює кожний негайно легальний хід за досяжним простором, виходами, загальною тягою до найближчої досяжної їжі, місцем для повернення, ціною або бонусом смайлика, ціною каменя і перевагою руху прямо.

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

9. Оцінювання маршрутів

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

score = tailReach ? +145000 : 0
score += survivalDepth * 5600
score += exits * 2400
score += reachableSpace * 16
score += escapeSpace * 8
score += escapeTailReach ? +44000 : 0
score += itemPoints * 150
score -= routeDistance * 260
score -= cellsBeyondNearestFood * 900 (з межею)
score -= leavesARoomStillHoldingFood ? regionDebt : 0
score += returnPreservingSmileys * 8800
score += smileyEscapeBonus
score += preservedRoomDoorLanes
score -= smileysEaten * 10500
score -= blockedRoomDoorLanes
score -= stonesPushed * 55
score -= oneExitAfterEating ? 18000 : 0

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

Два новіші планувальники їжі навмисно використовують інші ваги:

Планувальник Важлива різниця в оцінюванні
nearFood() Застосовує дуже великий член -distance * 6200. Саме це не дає боту ігнорувати безпечну їжу, що лише за кілька ходів.
pressureFood() Коли ситуація термінова, використовує менші штрафи за пастки і смайлики, а також менший штраф за відстань. Це дозволяє виходити з петель за хвостом через реальний прогрес.
survivalMove() Не шукає повний маршрут до їжі. Він оцінює негайні легальні ходи за відкритим простором, тягою до найближчої досяжної їжі, виходами, ціною смайлика, ціною каменя і перевагою руху прямо.

Оцінку формують ще дві сили. Смайлик коштує −50, а на цих рівнях кожне підбирання породжує новий, тож поле заповнюється смайликами; планувальник знижує штраф лише коли смайлик справді мостить шлях до досяжної їжі, інакше тримає повний штраф, щоб бот не гриз смайлики намарно. Як остаточна перевірка після вибору ходу, бот узагалі відмовляється ступати на смайлик, коли серце досяжне без перетину смайлика, тож він ніколи не платить −50 за підбирання, до якого міг дійти, обійшовши на одну клітинку — хіба що саме цей смайлик тримає шлях назад до хвоста відкритим. Свідомий смайлик-втеча ніколи не скасовується, а варта самозамикання, що завжди йде останньою, може й сама обрати його, коли кожен чистий напрямок замурував би тіло. А оскільки бонус рівня спливає з кожним ходом і зараховується в рахунок при очищенні рівня, невеликий додатковий штраф за відстань — масштабований за залишком бонусу — вмикається, коли лишилося кілька предметів, підштовхуючи бота до найкоротшого завершального маршруту замість зволікання на останніх підбираннях. Обидві — лише тайбрейкери: вони ніколи не переважають фільтри виживання.

10. Обмеження швидкості

Бот мусить думати між видимими ходами гри. Кілька обмежень тримають цю роботу під контролем:

Повзунок привʼязаний до варіантів швидкості сторінки: 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100. Низькі значення змушують бота довше чекати між ходами; високі натискають клавіші набагато швидше, аж до приблизно 45 мс на хід. Звичайний час планування віднімається від цієї затримки; якщо пошук перевищує цільовий темп, наступна клавіша надсилається відразу.

delay = round(45 + 375 * ((100 - speedValue) / 100) ** 1.6)

11. Перемога, смерть, перезапуск

Сторінка бота має сім вибірних вкладок: рівні 2-8. Перемога блимає зеленим, невдача блимає червоним, і обидва результати переходять до наступної вкладки. Після рівня 8 вона повертається до рівня 2. Спалах проходження використовує такий самий короткий пульс, як спалах застрягання, перед завантаженням наступного рівня.

Умова Результат
LEVEL === TARGET + 1 && LIVE > 0 Чисте проходження. Спалах зеленим і перехід до наступного рівня зі списку.
LEVEL !== TARGET Гра перескочила або завершилася. Вважати невдачею і спалахнути червоним.
BTEL перестає просуватися Змія заблокована або не рухається. Продовжувати ходи; спроба завершується смертю лише коли не лишається жодного легального ходу, інакше — очищенням рівня.
Планувальник не повертає ходу Відкотитися до випадкового легального ходу. Послідовність застрягання, включно з червоним спалахом, використовується лише коли взагалі немає легального ходу — змія справді застрягла.
Немає приросту рахунку 120 рішень, або 24 із повтореними позиціями голови Продовжувати питати WebAssembly-планувальник, але ввімкнути forceRisk. Тоді борг за недавні цикли важить набагато сильніше, планувальник отримує більше часу, а свіжі клітинки, тиск на їжу, прокопування каменів і ходи зі збереженням зворотного шляху мають перевагу. Випадковий легальний відкат використовується лише якщо планувальник не повертає ходу.
250 ходів без поїдання серця чи трефи (смайлики не рахуються) Той самий шлях планувальника з forceRisk, щоб довга суха серія стала сильнішим Wasm-пошуком виходу, а не перезапуском. Це більше не тригер смерті чи перезапуску.

Рахунок використовується для сигналу простою замість кількості предметів, бо пізні серця можуть породжувати трефи. Наприкінці це важливо: остання трефа може вимагати довгого маршруту без зміни рахунку, тому бот продовжує шукати, а не сприймає тихий рахунок як невдачу. Окремий ліміт у 250 ходів без підбирання рахує лише справжню їжу — смайлик за −50 не є прогресом, тож гризіння смайликів більше не може завадити спрацюванню ліміту — а коли він спрацьовує, то лише переводить бота у форсоване Wasm-планування втечі, ніколи на смерть чи перезапуск.

12. Межі і компроміси

Бот навмисно практичний, а не ідеальний. Він не розвʼязує весь рівень як один велетенський план, бо поле змінюється після кожного підбирання, проштовхнутого каменя, нової трефи і такту ворога. Замість цього він постійно переплановує з живого стану. Це робить його стійким і достатньо швидким для спостереження.

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

Лабіринт Sneekie вибухає світними виборами маршрутів, зонами небезпеки і шляхами рішень бота.