Хакатон DATSART — обзор и опыт участия
Сегодня, буквально спустя пол часа после финала, пока еще по горячим следам, хочу поделиться с вами историей о моем участии в команде DreamTeam вместе со своей женой, в хакатоне от кампании DatsTeam. Постараюсь изложить события в хронологическом порядке и уже самому взглянуть на свою работу со стороны.
Итак в чем краткая суть задачи? Существует виртуальный холст на который можно наносить краску путем броска краски через абстрактную катапульту, в катапульту можно положить разную краску, чтобы получить желаемый оттенок, саму же краску необходимо добывать на складе, а управлять стреляя катапультой через примитивные ‘баллистические’ расчеты. Хакатон проходит в два этапа, в 17:00 старт и до 16:00 следующего дня у вас есть время научиться всем этим управлять и показать какой-то результат. После в 17:00 второго дня, начинается финальная часть на 3 часа. Поехали
Первый этап
Старт — 17:00 — 20:00
Хакатон начинается с публикации документации на сервис API в котором кратко изложены доступные функции разработчикам.
И первый же шквал критики на создателей и разработчика хакатона (автор разработки хакатона Вадим Коваленко — мой бывший коллега yii php разработчик) обрушивается за непонятную документацию. Большинство команд, не могли попасть в холст и не понимали правил игры. Документация хоть и была написана +- доступным языком, но организаторы не стеснялись дополнять ее примеры и фиксить ее на ходу, что конечно же вызывает волну негодования у участников, а организаторы пытаются отшутится.
Сами участники представляют собой набор команд в большинстве своем со стеком — C, Python, JavaScript, PHP
Я выбрал путь работы через SPA приложение на vue3+ts
и сразу же влип в историю, когда тестовые запросы в PostMan у меня работают отлично, а вот в браузере у меня уже всем фронтендерам известная чихарда с CORS
, да еще и все запросы даже те, где не нужен request.body на сервер нужно отправлять через POST запрос (после таких нестыковок технических, RESTом называть апишку не хочется, а еще там все не в json а в multipart form data
что накладывает еще свои ограничения и неудобства т.к. обычно FormData используется для отправки файлов, а не обмена json объектами ) В общем мой совет разработчикам хакатона — подружиться с OpenApi спецификацией, научиться прикручивать Swagger и на основе нормальной спецификации публиковать (генерировать) UI — ну или взять готовый swagger UI — и избежать критики за отсутствие подствеки синтаксиса и убогую (примитивную) верстку в своей документации. Но такие же претензии лично к Коваленко Вадиму у меня были и когда я увольнялся с позапрошлого места работы) в этом плане для меня ничего не изменилось. Я пытался помочь сначала с кокнфигурацией и правками для nginx
лично Вадиму, но потом, ответил ему — не чини это же хакатон
и пошел написал свой server.js
аж на 96 строк кода) нативно, без библиотек и лишнего, костыли ради обхода заголовков Authorization
с клиента, + все передается в textplain
чтобы при CORS
режиме браузер и не думал обрезать мне ответ от этого космического сервиса.
Пример рализации server.js для хакатона, слабоневрным не смотреть
const http = require('http'); const headers = { 'Access-Control-Allow-Origin': 'http://127.0.0.1:5173', 'Access-Control-Allow-Methods': 'OPTIONS, POST, GET', 'Access-Control-Allow-Headers': 'Origin, Content-Type, Authorization', 'Access-Control-Max-Age': 2592000, 'Access-Control-Allow-Credentials': true, 'Content-Type': 'application/json', }; //Call dats api function fetch(url, options = {}) { options = Object.assign(options, { headers: { 'Authorization': 'Bearer 644016da92f7f644016da92f80', 'Content-Type': 'multipart/form-data; boundary=xxxxxxxxxx' }, method: 'POST' }); return new Promise((resolve, reject) => { const request = http.request(`http://api.datsart.dats.team${url}`, options, (response) => { const body = []; response.on('data', (chunk) => body.push(chunk)); response.on('end', () => { const responseBody = Buffer.concat(body).toString(); resolve({ statusCode: response.statusCode, headers: response.headers, body: responseBody }); }); }); request.on('error', (err) => { reject(err); }); if (options && options.body) { request.write(options.body); } request.end(); }); } const handleApiRequest = (req, res) => { let body = ''; req.on('data', function (data) { body += data; if (body.length > 1e6) req.connection.destroy(); }); req.on('end', function () { const json = JSON.parse(body); var boundary = "xxxxxxxxxx"; var data = ""; for(var i in json) { if ({}.hasOwnProperty.call(json, i)) { data += "--" + boundary + "\r\n"; data += "Content-Disposition: form-data; name=\"" + i + "\"; \r\n\r\n" + json[i] + "\r\n"; } }; data += "--" + boundary + "\r\n"; data += "Content-Type:application/octet-stream\r\n\r\n"; var payload = Buffer.concat([ Buffer.from(data, "utf8"), Buffer.from("\r\n--" + boundary + "--\r\n", "utf8"), ]); // console.log('form_data', payload); fetch(req.url, { body: payload, }).then(response => { // console.log('DATS RESP:', response.body); res.writeHead(200, headers); res.end(JSON.stringify(response.body)); }); }); }; const server = http.createServer((req, res) => { console.log('Received request:', req.method, req.url); if (req.method === 'OPTIONS') { res.writeHead(204, headers); // console.log('skip options 204', req) res.end(); return; } handleApiRequest(req, res); }); const port = 3001; server.listen(port, () => { console.log(`HTTP server is listening on port ${port}`); });
Набираем оборооты 20:00 — 00:00
После потери времени на CORS, фиксов nginx и лирики про верстку документации, я приступил наконец-то к выполнению задачи. И это моя первая ошибка. Мне стоило провести декомпозицию задачи не в 20:00 а в 18:00, пропустив время на лирику и CORS, но кто же знал :) в общем суть и порядок задачи сводился к организации нескольких процессов, с которыми мне помогала жена. Я взял на себя доработки по nodejs серверной части, а жена пошла на склад генерировать цвета и добавлять их к нам в виртуальный профиль. Из первых подводных камней мы встретили баг с JSON.parse и bigint когда происходит округление больших чисел и часть значения отбрасывается. В общем multipart data аукнулась еще разок неприятным багом на полчаса времени. Второй проблемой было то, что опциональные параметры по документации по сути являлись обязательными и это тоже отняло время на понимание. Также ошибки API про то что у тебя уже есть похожее задание в очереди на выстрел составляли больше половины ответов на мои запросы. Удавалось это вылечить и стабильно стрелять только около раза в секунду, принудительно дергая апишку обновления остатка цветовв и дожидаясь конца ее ответа, иначе все сыпалось в 400.
Стреляем всю ночь 00:00 — 7:00
Вторая моя фатальная ошибка, это мыслить как принтер, а не как катапульта с ведром краски, я ударился в описание алгоритма попиксельной сверки пикселя между финальным и текущим холстом с выяснением каким цветом мне туда нужно выстрелить. Но даже в банальной ракете на первом уровне было 9тысяч пикселей, которые необходимо закрасить, а значит даже на 1 картинку со средней скоростью стрельбы 1 выстрел в 2сек, я бы потратил 2.5 часа на всю картинку, а впереди было бы еще 7 таких и в два раза больше по количеству закрашенных пикселей. Но ближе к четырем часам утра, понимания, что большинство команд даже попасть в холст не может, я решаю не сворачивать со своего пути и прорисовывать на холсте первую картинку насколько смогу, чтобы пройти в финал и там уже разбираться над своими ошибками и сделать маленькую паузу. Дотянув до 7 часов утра на морально волевых, по сути наблюдая как по пикселю красится канвас, я принимаю решение ложиться спать, в этот момент жена закончила свою часть с добыванием краски из сервиса и ждала каких-то новых указаний по движению далее, а я свою очередь уповал на идею попиксельно все четенько закрасить и показать всем красоту.
Сон и второе дыхание 12:00-17:00
После весьма непродолжительного и нездорового сна, голова все-таки просветлела, а груз и печаль по казалось уже вчерашним проблемам — испарились. За время моего отсутствия появились явные лидеры, я сделали около 10 тысяч выстрелов и попадал более чем в 90% случаев, но несовпадение цвета давало мне всего 2 балла в таблице лидеров, в то время, когда ТОП лидеры уже имели десятки тысяч баллов, на уровне алгоритмов, уже было понятно, что чуда не будет и у нас есть явные победители на хакатоне, но я пришел на хакатон не за макбуком, а за участием и тусовкой в знакомой мне IT среде, а также для обмена опыта и заведения знакомства с другими участниками хакатона. Решено было продолжить наши начинания и идти до самого финала. Посмотрев на результаты и скриншоты работы других игроков, я понял, что моя главная ошибка это попиксельная стрельба, в то время когда большая клякса с нужным цветом за 1 выстрел давала сразу сотни баллов участникам хакатона. Я решил развивать далее свои наработки попиксельной стрельбы и загружать краски уже не по 1 а по 2-5-10-20 постепенно наращивая и корректируя формулу подсчета которую я отложил на потом и пользовался константами и соотношениями сил и углов для вычисления нужного пикселя на холсте. Смена тактики не заставила себя долго ждать, и уже за пару часов я настрелял +40 баллов, оставалось решить вопрос с попаданием в цвет и заменить эмпирическую формулу на полубаллистический аналог тем более что в чате уже были скинуты наработки по формулам. Также утром организаторы обьявили, что скорость игры возросла до 250мс на 1 тик, что кратно ускоряло мой пиксельный принтер который уже рисовал по 2-4 пикселя на холсте за выстрел. Оставалось развивать идею и догонять финалистов, но время бессердечная сука и не ждет никого, хакатон и первый этап подходили к концу, лидеры были явно определены, оставалось посмотреть развязку в финале и пообщаться за кулисами
Финал
В финал прошли все, что приятно, но с другой стороны моя тактика — набрать хоть сколько-то в начале, чтобы пройти в финал и там все пофиксить — оказалась тупой идеей и делать нормально надо было сразу. Также в финале не обязательно было играть в финальный уровень картинки, которая была ну просто космос и не имела белых (незакрашеных областей) что делало бесполезным мое потраченное время на попиксельную сверку холстов — теперь красить надо было каждый пиксель. Просто набивать очки ради очков на больших кляксах, чтобы потешить себя местом повыше — гнилой подход и полная соревновательная импотенция. Наша команда приняла решение играть в супер игру) и пробовать отрисовать целый космос цвет в цвет с четким совпадением пикселей. По сути к этому моменту пятно от выстрела было 4-6px в зависимости от количества краски и оставалось только скорректировать точность и разобраться с соотношением количества кидаемой краски к величине пикселя (если бы не мысли в стиле принтера, то это стоило бы сделать в самом начале) я начал увеличивать количеств смешиваемой краски и заполнять холст своим катапулеметом и на этом в целом и закончился мой финал. До полноценной реализации мне не хватило две функции:
- Управление размером пятна от выстрела
- Корректировки точности с поправкой на псевдобаллистику
Уже после финала в чате за кулисами появилось больше информации от финалистов и больше информации от организаторов, стало понятно, что за выстрел нужно кидать на 10-20 красок а 100-200 и будет вполне хорошо и достаточно для победы. На этом стоит подвести итоги этого хакатона. Мои же изменения за последние 3 часа в финале дали рост с 40 до 182 баллов за совпадение и точность цвета, но уже слишком поздно было генерировать картинку выбранным мною подходом
Итоги
Итого я занял 46 из 340 — по количеству очков. 11 место по количеству попаданий в холст. 51 место по точности попаданий. 33 место по количеству попаданий краски и 68 место по процентам точности подбора краски. Приобрел 18 новых знакомств с разработчиками и собрали команду на следующий более серьезный хакатон.
Зачем вообще ходить на хакатоны?
- Опыт, бесценный опыт, только за ним стоит идти. Программирование на результат, а не на архитектуру, merge мыслей и функций с участниками команды в рамках одного файла без всяких MR\PR — на такое удовольствие просто нет времени
- Проверка на стрессоустойчивость и способность решать задачи при недостатке информации, инфрастрктуры, обеспечения, важен только конечный результат.
- Знакомства, хакатоны это шикарная возможность познакомиться с другими разработчиками и найти себе команду и просто единомышленников. Лично у меня появилось +19 новых контактов и я рад каждому) на следующий хакатон пойдем уже не просто командой а несколькими командами сразу! Чтобы занимать весь рейтинг.
Фидбек от участников
Если вам интересна тема хакатов, хочтся развиваться, пишите мне в телеграм или присоединяйтесь в чатик нашего сайта, думаю, что этим летом я посещу еще не один хакатон и есть мысли прикрутить к этому сайту календарик хакатонов.
Если не брать в расчет недочеты с документацией и API, то хакатон в целом удался на сто процентов и можно выразить респект Коваленко Вадиму, за организацию такого творческого и креативного хакатона и его техническую реализацию, выдержать DDOS от 340 команд в момент проведения хакатона — это достойный технический челендж.
Также для тех кто ищет комаду, или единомышленников, несколько участников после хакатона попросили оставить их контакты под информацией об этом событии, чтобы найти еще единомышленников
@dead_inside_but_still_hornyy — Лидер командды PyCat
Во время этого двухдневного хакатона испробовал множество вещей, и лучше познакомился с физикой(прежде ее почти не затрагивал, и был упор в прикладную математику), также многое узнал о таких расширениях, как RGB888, но главное по итогу, самый ценный из приобретенных навыков: научился выводить универсальные решения из минимума информации. сама атмосфера была классная, многие люди не спали целую ночь, жаль, что калькуляция целевой метрики была не правильная(ИМХО, слишком сильно перетягивались баллы из-за различия в пару оттенков), да и в целом не была явно задана
Павел Зубков
для себя: разберись уже в школьном матане, делай сначала то что приносит больший импакт, научись работать с мотивацией, суточный хакатон начинай на старте а не спустя 8 часов после старта.
как-то так)
Максат
По началу было не понятно но потом углубился в суть и стало понятно , но я всё ещё не понял как работать с светам. Но в целом не жалею что участвовал было круто спасибо вам
Farel
Задания интересные, есть много моментов к которым можно найти довольно неожиданный подход. Порог входа не слишком низкий, и это радует — задолбали ивенты, рассчитанные на новичков-новичков. Было интересно участвовать даже несмотря на то, что наша команда практически ничего не смогла выполнить
LostDesu
Круто постреляли))
Последняя редакция 25 апреля, 2023 в 06:04