Как мы хакатон в 30ке провели и зачем я форкнул интерпретатор Lua
- вторник, 25 ноября 2025 г. в 00:00:07
18го ноября, в ГФМЛ №30 (Губернаторский Физико-Математический Лицей №30, он же 30ка, он же тридцатка) на дне наук, я, Саша, CGSG, при поддержки Yandex Cloud провели игровой хакатон для школьников.
Задачей ребят было за 2 часа написать бота для змейки на Lua. Они писали и отправляли стратегии, а в фоне постоянно шли матчи, в которых их стратегии и принимали участие.
Через 2 часа рейтинг сбросился, для каждой команды был взят последний отправленный алгоритм, и в финальной пачке матчей определился победитель.

И тут я хочу поделиться внутренней кухней. Как все это было приготовлено, с какими проблемами столкнулись, какие ошибки допустили, а что вышло хорошо. Будет много картинок, фоток и даже пару видео, надеюсь не переборщил)
Идея провести хакатон в школе появилась около года назад. Тогда мы потратили уйму времени на придумывание форматов разной степени извращенности. Варианты были от CTF до написания майнкрафт ботов. В итоге эти рассуждения ни к чему особо не привели.
В этом году у меня появилось навязчивое желание реализовать какой нибудь пет проект, который ну точно завершится (а пет проекты таким могут похвастаться не часто). Хакатон в школе - идеальный кандидат. С Сашей мы отправились к нашему школьному учителю информатики - Виталию Александровичу Галинскому, идею он принял с радостью и предложил провести ивент на дне наук.
Это круто избавило нас от задачи поиска участников - мы даже заранее представляли их количество - ~20 команд ~40 человек.
Откуда же появилась идея провести хакатон по написанию скриптов для змейки? На самом деле такая идея всплывала и год назад. Я вообще фанат примитивных клеточных игр (моя онлайн змейка) и совсем не против в 10й раз реализовать очередную game of life или змейку. Но именно этой осенью произошло ещё кое что интересное. В универе я хожу на курс по Rust от ШАДа. На одной из домашек мы писали ботов для paperio. И когда я с заданием справился, я понял, что затея то реально веселая и рабочая, и хакатона вполне заслуживает. А с тем, что змейка - лучший кандидат на баланс фана и сложности - думаю многие согласятся (для школьников паперио было бы все таки чересчур).

Я пошёл в Yandex Cloud (я тут работаю) с предложением нас проспонсировать. Пройдя не очень большую цепочку "кинь меня в твоего знакомого, близкого к данной теме", я дошёл до нашего деврела - Насти Масловой, и облако в её лице поддержало нас с удовольствием) Никакой бюрократии, мы просто обсудили формат, количество человек, количество и способ определения победителей, а затем выбрали призы, которые должны были зайти школьникам. От меня оставалось только принять посылку в офисе. Огромное спасибо Насте, без неё этого бы не случилось!


Съеденные яблочки дают длину - всё по классике.
На поле сразу несколько змеек, в противников можно врезаться.
Игра идёт до момента гибели всех, но ограниченна 1000 тиками.
Внутри матча +1 очко за каждое яблоко, +5 за убийство.
Глобальный рейтинг - среднее количество очков по всем матчам.
Дальше много технических деталей, весьма интересных, но можете скипнуть, пролистав до "Ещё немного про подготовку".
Скелет таков:
Сервер на Go (~85% кода написанного человеко). Для себя открыл huma, достойный аналог fastapi для go.

Фронт на react+vite (~5% кода написанного человеком) - Да, за это слегка стыдно, но фронт писать мы не умельцы, да и времени немного. Под конец фичи добавлялись через боль (даже что-то приходилось писать руками!). Дальше эта штука не особо расширяема. За начальный шаблон и укрощение агента спасибо Сане - без него эта штука бы развалилась не доехав до финиша.

Lua в качестве языка для ботов я выбрал без раздумий, давно хотел с ним поиграться. Язык очень красивый своей простотою. Считается супер встраиваемым, и инструментов под него написано немало. Есть gopher-lua на 6.8к звёзд, go-lua на 3.4к - это всё интерпретаторы Lua на Go.
То есть прямо внутри go кода можно выполнить код lua из условной строчки, ничего не компилируя и не плодя лишних процессов. Но не всё так гладко, как выяснилось позже...
За полторы недели до дня X мы решили провести бета тест среди своих знакомых. Набралось аж 15 человек. Был вечер субботы и все согласились, что думать лень и код будет писать ии. Главное было - протестировать нагрузку.
Во первых, бета тест выявил проблему - 1.5 часа невероятно мало. К счастью, после моей эмоциональной аргументации, нам увеличили время до 2.5 часов.
А вот про "во вторых" так кратко и не рассказать...
Немного про ограничения: скрипт должен выполняться не более 100 секунд суммарно за все тики (игра длилась максимум 1000 тиков). Получается, чтобы каждая команда участвовала в каком то матче раз в 2 минуты, ядер на сервере нужно столько же, сколько команд!
Время я ограничил без особого труда, но забыл про кое что ещё.
И один товарищ (спасибо ему большое) на бета тесте написал бесконечно растущий массив. Вся оперативная память машинки (а на тот момент это было 48гигов) - забилась.

Немало времени я потратил на решение задачи ограничения памяти... В канонической реализации Lua (на C) - такая возможность есть. Но ни в gopher-lua, ни в go-lua её не было. Теоретически можно было написать запуск скриптов на C, а потом использовать в Go через CGO, но к счастью до этого не дошло...
Нашёлся новый покемон - golua, киллер фичёй которого и была возможность ограничить и время и память. Звёзд на гитхабе было уже не так много, всего 100 штук, но интерпретатор проходил официальные тесты lua, и казалось бы - бояться нечего...
После переноса логики на новую библиотеку, сервер и правда ожил - злополучный скрипт убивался при превышении памяти. Правда и старые стратегии у этого товарища сломались, но они уже не вписывались во время. Я списал это на то, что стратегии реально были прожорливыми, а старый код был к ним слишком лояльным.

За дня три до хака я делал песочницу. В ней участники могли просимулировать матч прямо на сайте. Для этого go компилировался в wasm - бинарный формат, который может исполняться прямо в браузере. И тут всплыла проблема - новый интерпретатор lua отказывался компилироваться в wasm, так как имел системные зависимости. Интерпретатор пришлось форкнуть, чтобы это пофиксить. Но issue я завёл, и автор исправил ошибку.
А вот так выглядит песочница.
А за два дня до хака я решил пописать примеров кода на lua, чтобы добавить их в документацию. Тестировать в песочнице было просто и кайфово. И тут я наткнулся на реальную проблему - при определённых ключах в объекте lua итерация по этим ключам длилась бесконечно! Не без труда проблема была устранена, форк у меня уже был :) А после хака я разумеется сделал PR. Оказывается даже официальные тесты lua не гарантируют полную работоспособность...
И скрипты того товарища начали работать, спасибо Егору за лучшее тестирование!
Отдельно внимание стоит уделить GGSG и его бойцам. CGSG - Computer Graphics Support Group, кружок в 30ке, который существует уже больше 30 лет. Под руководством Виталия Александровича ребята делают невероятные проекты, связанные с компьютерной графикой. От собственного рейтрейсинга до софта для 3д очков, и всё это на C++, на собственных движках и собственной графике. Они сделали нам шикарную 3д визуализацию, и во время соревнования на большом экране (даже на двух!) транслировались последние матчи.


А за неделю до хакатона мы развесили вот такие плакаты. Язык был засекречен, нам это показалось интересной задумкой.

Итак, 18 ноября мы провели хакатон. И что сказать? Это было не идеально, но это было отлично! Где то посередине сервер упал на 5 минут (nil dereference, который магическим образом не всплыл за часы тестов), но проблема быстро решилась.

Начнем с того, что стоило сделать по другому:
Большинство согласились с тем, что времени мало. На самом деле это радует, потому что доказывает, что ребята готовы кодить на протяжении длительного времени, а это позволяет давать более интересные задачи.

Засекречивать язык было ошибкой. Да, Lua простой, и похож на Python, который знало большинство (в документации были сравнения именно с ним). Но минут 40 на вникание ушло точно. А при двух часах на код - это критически много.

Награждение, мне кажется, можно было сделать лучше. Мы просто забыли его порепетировать, и всё было спонтанно.
А что же прошло хорошо?
Да в общем самое главное: Все остались довольны! Большинство смогли написать стратегию победившую всех ботов. Отмечали красоту 3д визуализации и удобство сайта. Да и впринципе сказали, что учавствовать им понравилось!
Победители победили заслуженно, им действительно удалось написать достойную стратегию за отведённое время.
Немного из формы обратной связи:





Отдельное спасибо хочется сказать Паше, репортёру этого мероприятия - за фоточки и шикарный ролик. Вставил ролик в самый конец статьи, лучше посмотреть со звуком.

Надеюсь в будущем мне ещё удастся поорганизовывать подобные мероприятия :)
Саше за укрощение ИИ и за то, что фронт не развалился впринципе. Ну и за совместную организацию разумеется.
Виталию Александровичу и бойцам CGSG - Саше и Тимофею за 3д визуал.
Вике за дизайн плакатов.
Yandex Cloud: команде HR-бренда, а также Оле за спонсирование мерчом и призами. Насте за то что помогла со всем разобраться. И облаку за машинку, на которой бежал сервер.
Паше Хохлюку за съёмку и монтаж ролика.
Мите за прототип редактора и идею использовать retroui
Всем 15 тестировщикам!
Да и мне, чего уж там :)
Обещанный клип