-
Вайбкодинг (2)
Говорит один разработчик: иногда меня просят сделать ревью, а я в нем ничего не понимаю. Тогда я копирую ветку и прошу модель сделать ревью. Она хвалит хорошие места и критикует плохие. Я копирую выхлоп и вставляю в оригинал.
На меня это произвело впечатление: каким же дном нужно быть, чтобы так поступать. Во-первых, если не разбираешься в теме, то и не берись, верно? Проходи мимо, пусть посмотрят те, кто разбираются. Я на работе смотрю в лучшем случае каждый десятый реквест. Везде своя специфика, своя история, свой контекст.
Далее: кто несет отвественность за комментарии? Вот сказала нейросеть заменить один класс на другой. Зато автор считает, что менять ничего не нужно. Как это разрулить? Автор пишет ответочку, ревьюер отправляет ее модели и копирует ответ? Бред же.
При таком раскладе зачем нам вообще ревьюер с моделью? Сделайте кнопку “AI-review” или встройте модель в CI — по крайней мере будет ясно, откуда дровишки.
Помнится, в Южном Парке была забавная серия. Учитель дает детям сочинение; дети никогда его не пишут и получают двойки. Зато теперь у всех — текст А4 от чата джи-пи-ти. Учитель садиться проверять и не может: на 30 сочинений нет никаких сил. Тогда он достает телефон и сканирует сочинения. ГПТ пишет рецензию и ставит оценку. Круг замкнулся, все довольны.
Так и здесь. С появлением нейросетей появилось много людей, которые считают, что могут перехитрить систему. Срезать угол здесь, срезать там. Проблема в том, что таких людей сегодня — девять из десяти. На таких числах это не просто не работает, но и смотрится жалко.
Ребята, которые тащатся с ИИ, любят рассказывать: тех, кто не умеет с ним работать, заменят. И почему-то исключают обратное: если за тебя все делает агент, на кой ты нам сдался? Такие открытия очень неприятны и приходят в последний момент, подобно тому как человека, который кого-то устранил, тоже устраняют. Как в фильмах: внезапное понимание, расширенный глаз и выстрел. Только вместо выстрела — увольнение.
Пугая кого-то заменой или устранением, подумайте — не придется ли вам пройти раньше своих жертв.
-
Вайбкодинг (1)
Вирус под названием “вайбкодинг” дошел и до нас. Начиналось все буднично: файлы AGENTS.md в каждом репозитории; бесконечные обсуждения моделей в чате. У иных разработчиков резко изменился стиль: годами они не писали ни одного комментария, а сегодня у каждой функции докстринг; файлы разбиты на секции вида:
;;;;;;;;;;;;;;;; ;; public API ;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;; ;; dev section ;;;;;;;;;;;;;;;;и так далее.
Некоторые люди занимаются нейросетями всерьез: знают все эти Клоды, запускают модели локально и в облаке. У нас даже проводятся внутренние вебинары на тему моделей.
При этом я не вижу, чтобы разработка качественно улучшилась. Нет такого, чтобы задача, которая раньше занимала месяц, сегодня закрывалась за три дня. Разумеется, я не могу залезть в чужую голову. Но я вижу висящие пул-реквесты и в целом понимаю, на какой они стадии. На мой взгляд, генерация кода особо не помогает.
Конечно, пользователи нейросетей доказывают обратное: мол, раньше я писал этот код день, а теперь сгенерил за полчаса. Это классический обман. Упоминается только первая, черновая генерация. Сколько времени уходит на то, чтобы прочитать и оценить код, причесать его или сгенерить повторно – об этом обычно молчат.
В одной книге хорошо сказано: у человека есть определенный когнитивный запас на день, так называемое “мыслетопливо”. Я уверен, что генерация кода с последующей доводкой поглощает не меньше топлива, чем обычная работа. Может быть и больше. Поэтому даже справившись с задачей быстрее, человек устает и смотрит Ютуб.
Качество вайб-кодинга по-прежнему низкое. Мне приходилось править сгенерированный код, и чаще всего это неприятно. Например, в одном случае разработчик смешал ленивые коллекции, виртуальные треды и семафоры. Разумеется, в промышленном запуске произошла блокировка, и понять ее причину было невозможно. Код пришлось переписать.
Другой коллега навайбокоил несколько плейбуков для Ансибл. Идея в том, что по текстовому файлу генерится плейбук, который создает в Амазоне определенные сущности. Идея простая, а реализация выглядит так, словно ее писали три джуниора. Стоило мне добавить новую строчку в файл, плейбук УДАЛИЛ все созданные до этого сущности. Автор проекта смотрит в сгенерированный код и ничего не понимает – даже где примерно искать баг.
Смотрю код другого коллеги: это Питон-скрипт для перекладывания данных между таблицами. Задаю простой вопрос – скрипт работает? Отвечает, что да. В ту же минуту я вижу ошибку в SQL, из-за которой ничего работать не будет. Ради интереса запускаю скрипт на тестовой базе. Происходит вот что:
- выскакивает неизвестная ошибка (до SQL даже не дошло)
- из-за try/catch управление переходит в блок catch
- там используется неинициализированная переменная
- скрипт вылетает
То есть в скрипте как минимум три ошибки! Работает он, ага.
При этом низкое качество кода коррелирует с дороговизной оборудования. Всем известно, как выросла цена на память: в интернете все шутки только об этом. Однако теперь коллеги обсуждают покупку Mac Studio со встроенной памятью 128 гигабайт. Стоит он от 5 до 7 тысяч долларов. Купить такую дорогую вещь, чтобы генерить на ней неработающие скрипты?
На этом месте к горлу подкатывает тошнота. В моем понимании это то же самое, что купить животное и покалечить его. Или купить книгу и распотрошить её. Что-то настолько мерзкое и низкое, что даже не хочется думать. Одновременное ухудшение качества и рост стоимости окружения. Делать хуже, используя топовую технику. Еще можно понять, когда по идеологическим причинам разработчик сидит на ноуте из прошлого века в vim-е. Но когда человек пишет плохой код и отдает Эплу несколько зарплат в надежде, что теперь попрет… это какое-то безумие.
Пока что вайбкодинг напоминает вейпы. Не смотря на громкие обещания, все еще нет данных за долгие периоды, и что особенно важно – последствий. А подозрения есть, и очень нехорошие.
-
О домах
А какие могут быть выводы? И так все понятно.
Люди не хотят жить в домах, которые служат экспериментом кому-то. Самый высокий, самый тонкий, решение от ветра, от того-сего – это интересно строителям, а не человеку. Мы приходим домой, чтобы отдохнуть от дневных потрясений. Если и дома будет стресс, однажды мы выйдем в окно.
Дома не должны подпирать стратосферу. Лучшая высота здания – 5-10 этажей и не больше. Такой дом не давит размером, не выделяется на фоне городского пейзажа, не отбрасывает тень. Последнее, кстати, проблема городов вроде Чикаго: на улицах между высоких зданий почти не бывает света. Это сырость, лужи и банальная проступность.
Дом на 100 этажей не станет могильником, если отключат электричество и воду. Попробуйте подняться на 45 этаж с сумкой продуктов, а еще втащить двоих детей.
Несмотря на очевидные вещи, в мире продолжают экспериментировать с жильем. Особенно в этом плане отличаются арабы: то они строят город-куб, то город-линию. При этом обязательно в пустыне – других мест нет. Конец одинаков: деньги зарыты в песок в буквальном смысле. В недостроенных городах живут строители с семьями, подтягиваются беженцы, отшельники и всякий сброд. Так продолжается, пока есть какое-то снабжение. Затем люди уходят, а пустыня пожирает город.
Мне как человеку искренне непонятно желание жить как можно выше. Зачем? Какая цель? Чтобы ехать в лифте десять минут? Переживать за детей и домашних животных, чтобы не выпали в окно? Если экстраполировать, то будет ли жилье на 450-м этаже лучше, чем на 200-м? А на тысячном?
Люди, которые живут высоко, выключены из жизни. Если живешь на десятом этаже и нужно сходить в магазин, это даже приятно. Спустился по лестнице, размял булки. А какой интерес ездить в лифте?
Помнится, я работал в фирме, которая снимала этаж в бизнес-центре. Выглядел он как стеклянный ларец: все прозрачное, лифты словно из хрусталя. Однако до 17 этажа ехать на нем было чуть ли не 10 минут. Сначала ждешь пять минут, затем едешь с остановкой на каждом этаже. Лифтов было четыре, но это не спасало от ожидания.
Однажды я поехал с работы вниз и обнаружил, что забыл ключи. Пришлось подняться-спуститься, и это заняло лишние полчаса.
Когда был в Москве, наблюдал жилые небоскребы по образу того, что в прошлой заметке. Циклопической высоты, этажей этак 70. При этом сквозь открытые окна видны черновые потолки. Квартиры выше 20 этажа не отделаны. Бетонные коробки напоминают пчелиные соты. Вид у домов зловещий.
Одинаково я не люблю противоположность – частные дома и коттеджные поселки. Это другая крайность: каждый житель – маленький божок на клочке земли. Хорошо знаю об этом по Краснодару: там считается, что в городе живут неудачники, а самый жир – свой дом. И вот в одном коттедже свадьба: салюты, гости, стрельба в воздух. У другого соседа родился сын – салюты, гости, стрельба в воздух. В третьем доме держат животных: собаки лают до часу ночи, петухи кричат с четырех утра.
Попросишь потише — бро, это частная собственность.
В коттеджных районах негде гулять. Куда ты пойдешь? Во все стороны – чужие дома. Разве что идти по дороге без тротуара и глотать пыль машин.
В детстве у меня был приятель. Родители его были богаты и жили в коттедже. Много раз я ночевал у него и видел жизнь этой семьи. В таком доме без машины (а то и двух) просто нереально. Но даже две машины постоянно заняты, так что либо кого-то жди, либо иди на остановку (напоминаю: вдоль дороги, где все мчатся как угорелые).
Выйти из этого дома некуда – кругом частная собственность. Либо туси на своем участке, либо езжай в город. Жители этих участков, наверное, даже не знают, что такое обычная прогулка.
Наконец, есть еще кое-что. Когда люди живут вместе в многоквартирном доме, нет бесконтрольных прав. В таком жилье больше общественных мест: подъездов, придомовой территории, детских площадок. Когда люди живут вместе, они больше за собой следят, уважают друг друга. Становятся цивилизованней, что ли.
Небоскребы в облаках хороши на картинках, а на деле жить в них никто не хочет. Может быть, работать, да и то под вопросом. Выйти из этой клетки — уже проблема!
Здания должны быть небольшими, дворы – закрытыми от машин и с зеленью. Застройка города должна быть как можно более равномерна. Группа домов, школа, сад, больница – а дальше этот кластер клонируют нужное число раз.
Вообще, важный критерий счастья – это когда в любое место ты попадаешь пешком. В школу, в поликлинику, на работу, не важно… возможность добраться до цели пешком за 15 минут – бесценна.
Хорошо, когда эта возможность распространяется и на собственную квартиру. Не всегда это возможно, но если к вам это относится, возможно, вы самый счастливый человек! Хоть и не знаете этого.
-
Нью-Йоркский небоскреб
UPD: видео заблокировали, посмотреть уже нельзя.
Посмотрел одно видео, захотелось пересказать.
В Нью-Йорке построили очень высокий и тонкий небоскреб. Выглядит как карандаш или шприц: высота 435 метров (1428 футов), а ширина сечения сравнима с теннисным кортом. Один этаж – одна квартира. Небоскреб предназначен для жилья, а не офисов. Это апартаменты для богачей, и слова “лакшери”, “worldclass” так и сыплются с экрана.

Интересно, сколько проблем решили инженеры, пока строили эту иглу. Дело в том, что здание испытывает нешуточное давление от ветра. Чтобы оно не упало, сделали множество тросов толщиной с футбольный мяч, которые уходят глубоко под землю. Таким образом у здания корневая система в точности как у дерева.
Далее: вещи под названием турбулентность и резонанс. Когда воздушный поток встречает препятствие, он порождает хаотичные волны по его бокам. Хаотичность эта, однако, со временем входит в резонанс и раскачивает препятствие вправо-влево (а не вперед-назад, как можно подумать). В результате раскачивания “корни” рано или поздно будут вырваны.


Чтобы это устранить, здание обшито рельефными плитами и напоминает сверло. За счет специальных канавок воздушный поток разделяют в разные стороны, и его давление падает. Кроме того, каждый 30-й этаж не застроен, а вместо него сквозное отверстие для ветра.

Все это венчает вишенка на торте: циклопический кусок стали размером с грузовик на последнем этаже. Весит он какое-то страшное число тонн. Кусок придавливает конструкцию, чтобы она не играла, а специальные механизмы сдвигают его, чтобы корректировать центр тяжести.

Когда здание почти достроили, случился шторм. Башня крана – на высоте 400 метров – сорвалась с креплений и стала неуправляемо вращаться. Выглядело это жутко. Никто не погиб, но в итоге кран заехал стрелой по готовому фасаду. Были убытки и репутационные потери.

Был и другой инцидент со льдом. Известно, что чем выше поднимаешься, тем холоднее. На высоте 400 метров даже в хорошую (на поверхности земли) погоду перепад температуры может быть значительными. На вершине здания стал скапливаться лед, и позже он обвалился на тротуары. На видео показано, как глыба льда размером с бревно падает рядом с такси. Популярности зданию это не добавило.

И вот небоскреб достроили и стали заселять. Центр мира – Америка, Нью-Йорк, Манхэттен. Вид – потрясающий, в ясную погоду простирается на километры. Ты властелин, а мелкие твари копошатся у твоих подошв.

Однако выяснились интересные детали. На высоких этажах здание все-таки шатается, и это можно почувствовать! Люди описывают это как крайне криповое ощущение. Также покачивания порождают шум: металл и бетон, как бы ни были крепки, деформируются и скрипят.
Шума добавляет система насосов. Чтобы поднять воду на 400 метров, нужные мощные насосные станции, которые шумят. Да, изоляция звука, все дела. Но полностью скрыть звук от них невозможно.
Лифты. Во-первых, ехать с условного 150 этажа на первый – долго. Во-вторых, смена давления. Со школы мы знаем, что каждые 12 метров давление падает на один миллиметр ртутного столба. 400 метров – 33 миллиметра долой. Немало людей чувствительны к резкой смене высоты из-за давления.
И вот здание стоит пустое. Кто-то, конечно, купил там квартиру, однако в целом оно – призрак. Огромный гвоздь стоимостью словно из золота, который воткнули неизвестно зачем. Очередной урок, что так делать не надо.
Выводы я оставлю на следующую заметку.
-
О написании заглавных
Не люблю, когда ставят заглавные буквы там, где этого не требуется. Примеры:
- квитанция за Март;
- следующее собрание в Понедельник;
- низкие оценки по Алгебре;
- повышение тарифов за Водоотведение;
- лучший подарок Любимой;
- в ответ на Ваше обращение…;
- с Новым Годом!
Теперь по пунктам. Названия дней недели и месяцев в русском языке пишутся со строчной буквы: понедельник, январь. Это в английском Monday и January, а в русском – со строчной. Названия языка тоже, кстати, не нуждается в заглавной. Поэтому языки — английский и немецкий, а не Английский и Немецкий.
Та же история с названиями школьных предметов. Это в английском Math, History, Art. В русском – математика, история, рисование.
Вода, тепло, газ и прочие услуги – со строчной. Просто они берутся из справочной таблицы по айдишке и подставляются как есть:
| id | title | |----|---------------| | 1 | Водоотведение | | 2 | Газоснабжение | | 3 | Отопление |Проблема решается вызовом
.toLowerCase.Любимая, Он, Она, Правда, Сила, Знание, Честь – все это лишний пафос. Напоминает творчество Ника Перумова, где в каждом предложении — Священный Меч, Древняя Сила, Темный Страж, Воин Пути и так далее.
“Вы” с большой пишется только если вы обращаетесь к конкретному человеку, при этом он старше вас в какой-то иерархии. Писать “Вы” в статьях – безграмотно, потому обращение обезличенно.
Вселенная пишется с большой.
Если праздник состоит из нескольких слов, с заглавной буквы пишется только первое. Новый год, а не Новый Год. Капитализировать все слова – разводить лишний пафос.
Если праздник начинается с даты (цифры, числа), то написание варьируется. Когда цифру пишут прописью, она принимает на себя заглавную букву:
- Восьмое марта
- Двадцать третье февраля
- Девятое мая
Обратите внимание: “Двадцать третье”, а не “Двадцать Третье”. Если число пишется цифрой, то заглавная буква уходит на следующее слово:
- 8 Марта
- 23 Февраля
- 9 Мая
Последнее особенно важно дизайнерам открыток и поздравительной мишуры.
-
Числа №16. Знаковость
Почему в Джаве байт имеет знаковый тип? Скажем, если прочитать из файла байт 249, то при печати увидим -7. Откуда минус?
Дело в том, что в Джаве тип byte и его старший брат Byte – знаковые.
Знаковые числа – те, что тратят первый бит на признак того, есть ли впереди минус. Например, число 3 в битовом виде записано так:
00000011А с минусом – так:
10000011Первый бит означает, что впереди минус. Если прочитать этот набор битов как беззнаковое число, то первый бит дает 128 (два в степени 8). Итоговое число становится 128 + 2 + 1 = 131.
Это как раз случай Джавы. Она смотрит на биты
10000011как на знаковое число, поэтому первый бит нарисует минус, а оставшиеся0000011– тройку.В интернете есть цитата Гослинга:
For me as a language designer, which I don’t really count myself as these days, what “simple” really ended up meaning was could I expect J. Random Developer to hold the spec in his head. That definition says that, for instance, Java isn’t – and in fact a lot of these languages end up with a lot of corner cases, things that nobody really understands. Quiz any C developer about unsigned, and pretty soon you discover that almost no C developers actually understand what goes on with unsigned, what unsigned arithmetic is. Things like that made C complex. The language part of Java is, I think, pretty simple. The libraries you have to look up.
Кому лень переводить: причина в том, чтобы сделать проще. В языках С и С++ есть знаковые и беззнаковые типы, и когда они выступают вместе, никто не знает, что произойдет. Да, в стандартах Си описаны правила, но легко ошибиться. Поэтому проще всего исключить беззнаковые числа.
Мое мнение таково: да, беззнаковые short, int, long лучше исключить. Пусть будут только знаковые. А вот с байтом вышла ошибка.
Беда в том, что в Джаве байт считается числом: тип Byte унаследован от Number. Я не согласен. Байт – это не число. Он никогда не интересует нас в качестве числа. Когда в последний раз вы прибавляли число к байту? Или умножали байт на байт? Вычитали их?
Байт – это группа битов, элемент алфавита из 256 символов. Да, ему можно сопоставить число для удобства, но не более того. Байт – это сырые данные, на которые можно смотреть по-разному в зависимости от контекста. Это может быть знаковое число или беззнаковое; это может быть несколько битовых групп, например, первые три – тип, а остальные пять – значение.
В байте может умещаться крошечное число с плавающей запятой! Бит на знак, три на экспоненту и четыре на мантису:
1 101 1010Такие числа называются tiny float 8 и поддерживаются видеокартами.
Факт, что отрицательные байты никем не используются, очевиден. Скажем, IP-адрес – это четыре байта через точку, но никто не записывает
192.168.0.1как-64.-88.0.1. Ни один hex-редактор не показывает отрицательные байты, не важно в hex- или decimal режимах.Тот же Турбо Паскаль разделял байт и знаковое 8-битное число: типы Byte и ShortInt. Первый фигурирует, когда читаешь сеть или файл. Второе – это преобразование группы бит к конкретному типу.
На примере Джавы можно вспомнить цитату Эйнштейна: вещи должны быть простыми, но не проще этого. Идея избавиться от беззнаковых типов в целом хороша, но повлияла на работу с байтами. Байты не должны трактоваться как числа, а если это допустимо, то знака не должно быть. Мы ведь не считаем буквы от -16? Так что с байтами упрощение пошло во вред.
Это была последняя заметка о числах – других заметок на эту тему в черновиках у меня нет. Надеюсь, вам понравилось, и кто-то что-то для себя извлек. Напомню, что все заметки про числа легко найти по тегу #numbers.
-
Числа №15. Об играх
Пока бы закончить с числами; осталось две заметки.
В прошлый раз я рассказывал о числах с фиксированной запятой и о том, как они использовались в играх. В том числе упомянул первый Старкрафт (BroodWar), о котором и хочу поговорить.
Старкрафт – это стратегия реального времени фирмы Blizzard; вышла в 1998 году. Она стала прорывом во многих областях и надолго задала планку. Старкрафт отличался запредельным числом игровых механик и их комбинаций. Расы были кардинально разными: зерги делали ставку на массу, протоссы – на мощь, терраны – на технологии. Юниты были наземные и летающие, транспортные, невидимые, с обычным уроном и массовым. Были псионики – юниты, которые атаковали только магией.
Игра, хотя и была двумерной, поддерживала многоуровневый ландшафт, и это влияло на исход боя: юниты ниже мазали по тем, что выше.
Не все тонкости игры были очевидны. Скажем, юниты делились на классы (легкий, тяжелый и другие), атака тоже была разных типов. Позже игроки составили таблицы, кто по кому наносит какой урон.
Добавьте к этому отличную кампанию, полную пафоса; крутые CGI-ролики; сетевую игру в компьютерном классе; Battle.net для счастливчиков с интернетом в 98 году – и станет ясно, что игра была прорывом. До сих пор она остается одной из главных киберспортивных дисциплин, а в Южной Корее – национальным видом спорта.
Чтобы обеспечить все это добро, движок считал много чисел. В 98 году не все процессоры поддерживали плавающие числа, поэтому в Старкрафте использовались числа с фиксированной запятой. Код игры никогда не открывали, однако ее устройство ясно из публикаций разработчиков и специального SDK для ботов. Последнее особенно интересно: Старкрафт был первой игрой, под которую писали ИИ-ботов. Были даже шоу-матчи между известными игроками и ботами по аналогии с шашками Go и AlphaStar. Ежегодно проходят турниры для разрабочиков ботов.
Интересен следующий факт: числа, что мы видим в игре, на самом деле являются дробными. Скажем, у морпеха (марика) 40 единиц здоровья, атака 6, броня 0. У зилота (zealot, воин протоссов) здоровье 100 единиц, защитное поле на 60 пунктов, атака 16. Все числа выглядят как целые, удобно складывать и вычитать их.
На уровне кода каждое число – это контейнер с фиксированной запятой. Тип, который его описывает, называется
fp8, что означает fixed point 8. Восьмерка подразумевает число битов, отведенных под дробную часть. Таким образом, fp8 хранит числа с точностью до1/256или0.00390625в десятичной системе.Я не видел полного описания этого типа. Однако логично предположить: если он занимал 16 бит, то на целую часть оставалось 8 бит. Таким образом целая часть вмещала диапазон от -128 до 127. Это мало. Поэтому скорее всего тип занимал 32 бита (обычный int), и на целую часть отводилось 24 бита вместе со знаком. Диапазон таком образом был от
-(2^23)до(2^23)-1или от-8,388,608до8,388,607.Восемь миллионов – достаточно, чтобы покрыть все случаи игры, включая особенные. Например, юнитов-боссов в кампании с колоссальным запасом здоровья.
И все-таки: зачем игре дробные числа? Мы ведь выяснили, что у зилота 160 здоровья, а у марика 6. Это значит, потребуется 27 атак, чтобы марик победил зилота. Все удобно, но нет – за этим удобством кроется фиксированная запятая.
Причина в следующем: если все числа целые, то в какой-то момент они портят игровой баланс. Такая сложная игра как Старкрафт обязана учитывать фракции, чтобы не вышла досадная вещь, которую мы рассмотрим ниже.
Про морпеха вы уже знаете; а у зергов есть юнит под названием ультралиск. Это слонопотам, который хорош против морпехов. Изначально у него 1 единица брони. Всем юнитам зергов можно сделать три апгрейда на броню +1. Наконец, ультралиску доступен его личный апгрейд +2 к броне. В сумме они дают 6 брони.
Единица брони снижает урон на единицу. А теперь смотрите: ультралиск со всеми апгрейдами имеет 6 брони, а морпех без апгрейдов на атаку наносит 6 урона. 6 - 6 = 0. Если задача решается в целых числах, то морпех наносит нулевой урон, и ультралиск полностью для него неуязвим!
Проблему можно решить тем, чтобы обязать игрока, который играет за терранов, сделать апгрейд +1 для мариков. Тогда морпех будет наносить единичку урона: 6 - 7 = -1. Но это слабое решение. Общая беда в том, что если у юнита высокая броня, он неуязвим для всех юнитов с атакой ниже брони.
Возможна абсурдная ситуация: у юнита слабая атака, но высокая броня, скажем атака 10 и броня 20. Представим теперь, что игроки выбрали одинакову расу. Оба настроили этих юнитов и пошли воевать. В результате ни одна армия не победит другую: каждая атака на 10 единиц нивелируется броней в 20 единиц. Такой себе геймдизайн.
На самом деле броня в 6 единиц – это не показатель -6 ко вражеской атаке. Это коэффициент ее снижения. В Старкрафте и других играх Blizzard броня устроена логарифмически. Вспомним как устроен логарифмический рост: на малых значениях он почти линейный. Значения брони 1 или 2 дают снижение урона на 1 и 2 соответственно. Однако далее рост замедляется: показатель 3 снижает урон не на 3, а на 2.9, 4 – на 3.5 и так далее.
Числа я беру из головы, потому что не готов запускать Старкрафт и все это проверять. Однако такая же механика справедлива для Warcraft, Diablo и других игр Blizzard. Логарифмическая броня – это залог того, юнит никогда не станет неуязвим. Даже если выдать юниту 100 брони, урон по нему будет снижен до условных 0.01 здоровья. Да, это мало, и здоровье будет тикать по единичке в минуту. Но урон все-таки будет, и сотня-другая обычных юнитов смогут победить тяжеловеса.
Как следствие, даже ультралиска с броней +6 можно победить морпехами без апгрейдов. Каждый марик будет наносить не 0, а 0.25 урона (условно). Разумеется, игроку за терранов в такой ситуации ничего не светит, но дизайн игры в этом не виноват. Не получится создать юнита, который полностью неуязвим, и это правильно.
Должно быть, вы замечали в других играх: долбишь врага, а здоровье убывает на единичку каждый сотый удар. На самом деле его здоровье — дробное число и урон наносится тоже дробный. В интерфейсе все показатели выглядят целыми, чтобы не смущать игрока длинными хвостами. Кому понравится
2.513523734353450003урона?Поскольку логарифмы ведут к дробным числам, вот откуда нужда в числах с фиксированной запятой.
По той же причине в раннем Старкрафте был баг: “мертвые” юниты с нулевым здоровьем. Такой юнит мог воевать и погибал только от следующего удара. Как вы поняли, он не был мертвым: целая часть здоровья была нулевой, но оставались единичные биты в дробной части. Это случалось из-за показателей брони, сплеш-урона с затуханием и многих других факторов. Старкрафт не округлял дробные числа: чтобы показать целую часть, он просто сдвигал fp8 на восемь битов вправо. Результат был 0, но юнит считался живым. Позже это исправили.
Да, многое я бы мог рассказать про Старкрафт – сколько времени в него прощелкал! В качестве бонуса предлагаю цикл статей разработчика, который пишет ботов и понимает устройство игры как бог.
-
Текущий case
Один из худших атавизмов в программировании – это протекающий оператор
switch/case. Он ведет родословную со времен Си и в настоящее время есть во всех императивных языках.switch (expression) { case constant1: // Code to execute if expression == constant1 break; case constant2: // Code to execute if expression == constant2 break; default: // Code to execute if no case matches }В чем проблема? В том, что без оператора
breakуправление передается в ниже. Если expression подходит под разные условия, сработают две ветки одна за другой. Счастливой отладки.Сколько людей погорело на протечках в
switch/case– я даже боюсь предположить.В Джаве
switch/caseустроен аналогично, но к счастью там это поправили. Есть другойswitch/caseсо стрелками, который гарантирует, что будет выполнена только одна ветка. Вдобавок он возвращает значение:int result = switch (input) { case "A" -> { System.out.println("Processing A"); yield 1; } case "B" -> { System.out.println("Processing B"); yield 2; } default -> 0; };В последних Джавах много внимания уделяют паттерн-матчингу. Можно сказать, что любой switch лучше свести к паттерн-матчингу, потому что иначе получается минное поле из-за протечек.
В Кложе, например, макрос
caseгарантирует, что сработает только одна ветка. При этом выражения должны быть литералами, потому что компилятор вычисляет от них хеши для таблицы переходов:(case (:status item) "active" (process-active item) "pending" (process-pending item) ;; default (throw-error "wrong status"))Есть макрос
condдля условий, которые вычисляются в рантайме. Будет вычеслена первая ветка, для которой условие вернет истину:(cond (-> item :status (= "active")) (process-active item) (or (-> item :status (= "pending")) (queue-is-blocked ...) (process-pending item) :else (default-case "..."))Наконец, есть скользящий, “протекающий”
cond->со стрелкой. Он принимает пары (условие->выражение) и пропускает сквозь них исходное выражение.(cond-> [] :always (conj (get-some-item ..)) (item-is-active? item) (conj the-item) :finally (into (get-additional-items)))Пишу я это потому, что погорел со
switch/caseв Питоне. Хотя с версии 3.10 в него завезли операторmatch, многие до сих пор пишут каскадыif...elif...elif...elif...else. И вот я добавил новую ветку в середину, а вместоelifнаписалif:if action == "foobar": do_this() elif action == "kek": do_that() if action == "some_action": do_lol() else: throw Exception("...")Ветка выполняется, но первый оператор
ifобрывается и начинается второй. Для него условие не находится и срабатываетelse, где бросается исключение. Получилось так, что действие выполняется, но из-за того, что было исключение, задача запускается еще раз. В итоге мой код вызвал действие три раза. Линтер ничего не заметил.На мой взгляд, старого оператора
switch/caseв коде быть не должно – только паттерн-матчинг, который гарантирует, что сработает только одна ветка. А второе – не знаю, как можно жить с языком без макросов. Это словно красть у себя самого. -
Закончили Гарри Поттера
Вчера закончили с дочкой Гарри Поттера – все семь книг. Это заняло год и пять месяцев. Я вычислил срок с помощью блога: оказалось, в январе 2025 года я публиковал маленькую заметку о денежной системе в мире ГП.
О каждой книге можно сказать разное: где-то лучше, где-то хуже. Кто-что затянуто; какие-то сцены никуда не ведут; от некоторых персонажей можно было отказаться. Однако в целом ГП – замечательный, монументальный труд. Это новая вселенная по образу Звездный Войн, и по ней еще долго будут делать приквелы-сиквелы, фанатский контент и прочее.
Гарри Поттер – это современная детская литература. Ключевое слово “современная”, то есть описывает персонажа нашего времени. Несмотря на все нелепости вселенной ГП, ключевые темы вроде отношений, любви, долга, преданности совпадают с ориентирами нормального современного человека. При этом книга взрослеет вместе с читателем.
Я жалею лишь об одном: мы читали ГП в неудачном переводе. Нам подарили первые несколько книг, и на тот момент мне были безразличны все эти Снейпы-Злеи. Когда я разобрался, то уже был полный комплект, и покупать новый не хотелось. Считаю, что переводчиков Махаона, которые исковеркали все имена, нужно отпинать и сбросить в канализацию. Может, так они поймут, что нельзя портить чужой труд.
Прикупил пару книжек о ГП на английском. Развивает: много прилагательных, разговорных оборотов, художественные вещи, с которыми не имеешь дела на созвонах.
ГП — важная веха современности. Читайте детям Гарри Поттера. И детям хорошо, и вам понравится.
-
Keeping things apart
Один кудрявый разработчик сказал: design is about keeping things apart. Дизайн – это удежание вещей по отдельности. Полная цитата звучала так:
design is fundamentally about taking things apart (decomplecting) so they can be managed, understood, and put back together (composed) in a flexible wayСмысл в том, что пока элементы более-менее свободны, свободен и дизайн. Следовательно, свободны и мы в принятии решений.
Сегодня я осознал, насколько это важно.
Где-то полгода назад я запрограммировал сложный процесс. Много шагов и состояния, работает почти час, поднимает тысячу лямбд. Ширина картинки с диаграммой – 10 тысяч пикселей. Я этой сложностью не горжусь: была бы моя воля, я бы сделал проще и вообще по-другому. Но меня заставили сделать так.
Этот процесс производит много данных, которые нужны всем. Со временем я заметил, что разработчики “подсасываются” к процессу: добавляют новые шаги и побочные эффекты. Я говорю: ребята, давайте вы запустите свой процесс, который считает ваши штучки параллельно моему, а не во время. Я не хочу, чтобы мой процесс падал из-за ваших вычислений, да и вообще – растет сложность. Давайте по отдельности.
А мне говорят: уж если есть убер-комбайн, давай педалить его. Я свое мнение защитить не смог и проиграл. В итоге на пайплайн навесили кучу других операций.
И вот вчера случилась классика. Мне понадобилось вызвать процесс еще раз, но с особыми параметрами. Ну знаете, такой же, но с перламутровыми пуговицами (с). Новое бизнес-требование. Я запустил, и все эти допики, которые навесили другие разработчики, посыпались. Где-то создались дубли, где-то не те поля и так далее. Пришлось чистить и откатывать.
И вот теперь я должен пройтись по всей цепочке шагов и добавить if-else. Если режим запуска такой-то, то не вызывать эту примочку, не обращаться в этот сервис, не выполнять этот запрос и так далее. Фактически – вынести все обвесы в условие.
В итоге у нас things нифига не apart, и design тоже не задался. А если бы вынесли в отдельные пайплайны вместо того, чтобы бесконтрольно усложнять исходный, дело бы обошлось.
У докладов Рича Хикки двойное дно. Ты их смотришь и не понимаешь: кажется, что дед несет пургу. А через много лет понимаешь, но уже через боль. Жаль, что понимание приходит именно так.