Telegram Group Search
Storage system trivia time!

Воронка достаёт элементы из блока над собой и перекладывает в блок под собой или сбоку от себя.

Что такое "блок"? Если в этой позиции буквально есть блок с инвентарём — всё просто, берётся инвентарь конкретно этого блока. Если же блока там нет, или это обычный блок без инвентаря, рассматриваются все entity с инвентарём, хитбоксы которых пересекают границы блока, и из них берётся случайная. Если нет и таких, то в качестве инпута берётся первый подходящий item entity.

Выводы. Очевидные: если над воронкой неполный блок, например компостница, то айтемы, физически положенные внутрь компостницы, забираться не будут. Неочевидные: если над вашей воронкой две вагонетки или лодки, где айтемы есть только в одной, забираться они будут не с шагом в 8 gt, а чуть больше, потому что с вероятностью 1/2 выберется пустая вагонетка, и придётся ждать следующего геймтика (9 gt в матожидании, если я умею считать).

Вагонетки с воронками ведут себя примерно так же, но с нюансами. "Блок над собой" для вагонетки не определён, потому что вагонетка может стоять на нецелых координатах; в таком случае выбирается блок, содержащий центральную точку вагонетки (иными словами — блок, с которым вагонетка пересекается по максимальной площади). Вагонетка, стоящая под четырьмя блоками, будет всё равно забирать айтемы только из одного из них. Ещё вагонетка умеет собирать айтемы, не пересекающиеся с её видимым хитбоксом — границы расширены на четверть блока во все горизонтальные направления. В частности, если вагонетка стоит в центре блока, она может забрать айтемы, выровненные по границе соседнего блока.

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

У вагонеток с воронками можно считывать наполненность компаратором, если этот компаратор считывает сигнал с детекторного рельса, на котором эта вагонетка стоит. Воронка считается стоящей на рельсе, если она пересекается с ним как минимум на 0.2 блока (чуть больше 3 пикселей) по каждой из горизонатальных осей. Если таких вагонеток несколько — консистентно, но недетерминированно берётся одна из них. Вагонетка может "сойти с путей", если рельс убрать, подвинуть вагонетку, а потом вернуть рельс обратно. Заблокировать воронку можно подачей сигнала на активирующий рельс, на котором стоит вагонетка. Под "стоит" понимается, что центр вагонетки содержится в этом блоке; соответственно, такой рельс может быть только один. Если вагонеток на нём стоит несколько, активируются они все.

Вагонеткам не нравится находиться пересекаться хитбоксами, и заставить вагонетки ими пересечься у вас не получится. Но если они уже пересекаются сразу, то после этого они будут спокойно двигаться независимо, оставаясь пересечёнными. Такого можно добиться, если тыкнуть "use" на рельс несколько раз. Энтити в рамках тика симулируются по порядку и пересчитываются исходя из текущей позиции остальных энтити, а не позиции на предыдущем тике, поэтому даже если две вагонетки изначально имели одинаковые параметры, их можно разделить, толкая их в случайных направлениях.
Это всё означает, что если у вас есть большой поток айтемов, которые нужно сортировать, и на каждый конкретный тип вполне хватило бы скорости одной воронки, но суммарно скорости импульс-сортера не хватает — вы можете распараллелить сортировку вот такой схемой.

Инициализируете верхние вагонетки, как в импульс-сортере, потом верхние воронки тоже инициализируете теми же айтемами со сдвигом на 45 градусов. Когда в вагонетках много айтемов — разблокируете верхнюю воронку; когда в верхней воронке много айтемов — разблокируете нижнюю. Фильтры нужно ставить и в вагонетки, и верхние воронки, потому что каждая воронка "видит" две вагонетки, из которых нужная только одна. Две воронки вместо одной нужны из-за неконсистентной скорости, чтобы скомпенсировать то, что воронки пушат айтемы раз в 8 gt, а пуллить будут только раз в ~9 gt, чтобы воронка с фильтром не опустошилась.

Вайринг редстоуна остаётся в качестве упражнения читателю.

Для сравнения сложности: типичный способ достичь того же эффекта требует дропать айтемы в воду, выравнивать их и отправлять на независимые сортеры слайм-блоками; в незере слайм-лаунчеров нужно ещё больше. Если инпутом выступает ферма с неравномерной частотой айтемов, начинаются проблемы с тем, что воронки в кулдауне пропускают айтемы, поэтому их нужно ещё и зацикливать. Делается, но далеко не так компактно, и если инпута настолько много, что нужно несколько дропперов, начинаются сложности с выравниванием при отсутствии воды.
Основная проблема всех подобных параллельных систем — неопределённое поведение в ситуации, когда на вход приходит неизвестный айтем. В данном дизайне такие айтемы останутся в сундуке и в итоге рискуют забить его целиком. У этой проблемы есть два решение: одно очевидное, но не работающее, второе работающее, но не очевидное.

Очевидное решение — поставить где-нибудь воронку, достающую произвольный айтем и сортирующую его обычным последовательным сортером. Идея в том, что хорошие айтемы будут сортироваться по большей части через fast path, но иногда ещё через slow path, а плохие айтемы (если их достаточно мало, конечно) будут всегда через slow path уходить в overflow последовательного сортера.

Нюанс следующий: поскольку воронки забирают айтемы из слотов слева направо, а добавляются айтемы все тоже слева направо, при достаточно высокой скорости входного потока воронка *всегда* будет забирать из первого слота, а в остальных будет скапливаться мусор. Когда мусором забьются все слоты, кроме первого, система потеряет возможность сортировать несколько типов айтемов параллельно, и сравняется по скорости с последовательным сортером.

Фикс очень простой: заменить сундук на активирующийся по клоку дроппер. Дроппер при активации выбрасывает элемент из случайного слота, а не первого, поэтому мусор сможет выбрасываться из всех слотов. Особенно интересно, что эта система умеет автоматически калиброваться под нагрузку в моменте. Если вам приходит мало мусора, в дроппере, вероятно, 4 слота будут забиты чем-то полезным, а 1 — мусором, и мусор будет выкидываться только с вероятностью 1/5. Но если доля мусора начнёт увеличиваться и, например, заполнит все оставшиеся 5 слотов, то мусор будет выбрасываться уже с вероятностью 5/9. Благодаря этому эффекту частота активации дроппера может быть сильно ниже пропускной способности системы, а это значит, что если вы умеете эффективно проверять поток айтемов на полезность, можно не сортировать их последовательно, а отправлять хорошие айтемы обратно в дроппер, откуда их когда-нибудь потом заберут вагонетки.

У меня на основе этого эффекта стала работать ферма золота, когда меня начала бесить дороговизна и некомпактность типичных дизайнов. На картинке весь механизм сортировки. Дропы мобов приходят сверху, тем или иным путём оказываются в дропперах. Из дроппера ~половину дропов забирает один 2x сортер на вагонетке (на gold nugget), сам дроппер со скоростью 2x перемещает оставшиеся айтемы (по большей части rotten flesh) в воронку. Из воронки rotten flesh забирает другой 2x сортер на вагонетке. Остаются мечи, gold ingot'ы, и overflow; первые два вида отфильтровываются последовательным сортером, третий возвращается обратно в дроппер. Вайринг оставляет желать лучшего, но оно почти влезает в 8x8, так что в целом ок.
Да, это канал про оптимизаторский подход в целом, а не только про классическое программирование, как вы догадались?
Алиса копается
https://lexi-lambda.github.io/blog/2025/05/29/a-break-from-programming-languages/
Недавно прочитала ещё The Copilot Delusion, занятная вещица. У меня отношение к LLMкам чуть другого рода, но в целом смежно.

Я периодически использую их как поисковик, чтобы вспомнить забытое слово, когда словари не помогают. Я прогоняю через них свои статьи, чтобы пофиксить опечатки, убрать лишние повторы и проверить, корректно ли считывается со стороны, на чём я пытаюсь сделать фокус (люди такую задачу выполнить не могут, потому что мои знакомые обычно и так понимают, о чём я пишу, и контекст у них в голове оказывается сразу правильным).

Но когда речь заходит о программировании, LLM мне не может помочь вообще никак. Я очень хочу понять, откуда такой хайп, и поэтому, когда у меня возникает задача, которую я не могу решить сходу, я запихиваю её в LLMку и пытаюсь решить с её помощью. Когда это сделать не получается, я трачу время и решаю её сама, я опять пытаюсь навести LLMку на нужную мысль.

Короче говоря, не работает это никогда, у меня ноль успешного опыта в подобном плане. Вроде как должно быть очевидно, что они не умеют думать, но насколько не умеют — для меня было шоком. Всё взаимодействие сводится к "вот решение" — "оно не работает, контрпример X" — "хорошо, вот исправленное решение" — "Y" — "вот проверенное и работающее решение" — "нет, всё ещё Z" — "вот решение с эвристиками" — "я не хочу эвристики" — "вот решение без эвристик, опирающееся на NP-трудную задачу" — "я хочу эффективный код" — "вот хорошее решение" — "нет, оно всё ещё не работает", после чего я забиваю и иду думать своей головой.

И ладно ещё неэффективный код, он всё равно может сойти для прототипа; основная проблема в коде, который абсолютно очевидно не работает и работать не может, но LLM об этом даже не задумываются, и в итоге приходится по сотне раз повторять одно и то же, и это всё занимает настолько много времени и даёт в итоге настолько мелкий результат, что возникает вопрос, почему бы это просто не сделать сразу самой.

Здесь обычно говорят, что LLMки пока не умеют работать с алгоритмическим кодом. (Ну, как "говорят"; то, что Claude Sonnet это умеет, тоже говорят, но вот я проверила, и не умеет.) А для кода без алгоритмов его использовать, получается, можно. Это не так: в коде, не требующем знать никакие известные алгоритмы, они тоже спокойно ошибаются, если над этим кодом надо думать, чтобы написать его корректно. Хорошо у них не с неалгоритмическим кодом, а исключительно с бойлерплейтом.

Но ведь с бойлерплейтом мы, программисты, всегда справлялись иным образом. Если какой-то простой код повторяется — мы его выносим в функции, классы, библиотеки. Если работать с базой данных неудобно — мы пишем для этого фреймворк, ORM. Такое выделение позволяет начертить зоны ответственности, тестировать каждый компонент в изоляции, использовать проверенные реализации. Если LLMки уберут эту болевую точку, у нас просто исчезнет DRY как понятие, а код превратится в багованное и нечитаемое спагетти и разростётся до таких объемов, где никакой человек и никакая LLMка уже не сможет впиливать фичи.

Так что как-то странно и непонятно это всё. Максимум, что LLM может сделать — написать мне match; но даже сделать это я ей не доверю, потому что и в matchах надо думать, а тыкать LLMку, когда и так на 100% понимаешь, что хочешь написать, бессмысленно.
Это всё был ответ на The Copilot Delusion, если что — ссылка первое время была не та, простите.
Раз в несколько месяцев я возвращаюсь к какому-нибудь старому проекту и перезапускаю на нём CI последнего коммита. И почему-то постоянно результаты не радуют. Lithium упал на трёх платформах, как такое может быть — загадка. Потом когда-нибудь надо будет сесть разобраться...
Ещё боль на тему LLMок в программировании, на которую как будто бы не обращают внимание вообще — это цена. Понятно, что все взрослые люди, очередная подписка — это не шок, но в контексте IT, как мне кажется, это рискует стать огромной проблемой.

Когда мне нужна IDEшка, я не иду её покупать. Есть VS Code, есть плагины для Sublime, есть Zed, у IDE от JetBrains и у Visual Studio есть Community-версии. Компиляторы — gcc, clang, javac, rustc, tsc — все бесплатные и открытые. Рантаймы тоже. В сети есть полно качественных тьюториалов и курсов. Да Linux со всем набором GNU-шных утилит свободный и бесплатный, в конце концов.

Так было далеко не всегда. Первые операционки, IDE, компиляторы, были платными, зачастую их могли себе позволить только корпорации и универы. GCC создавался в противовес, на открытый JDK перешли только недавно, и так далее. Заниматься разработкой не на компанию, а для себя или в open-source было возможно, но либо результаты выходили менее профессиональными, либо нужно было вкладывать кучу времени и других ресурсов. LLMки как друзья программиста — это по сути инструмент той же категории, только появляющийся, а не исчезающий.

Чего я боюсь — что такими темпами в программирование нельзя будет войти вообще, не прогибаясь под какую-то компанию. Нельзя будет, как я, найти в шкафу книжку по PHP, скачать Apache с php-cgi, написать "Hello, world" и почувствовать себя всемогущей. Я боюсь, что тулинг станет настолько неподъёмным, что родители подумают дважды, перед тем как вложить такие ресурсы в обучение детей. Я переживаю, как бы open-source не заглох, если доступ к сильным моделям смогут себе позволить только корпораты.
Алиса копается
Раз в несколько месяцев я возвращаюсь к какому-нибудь старому проекту и перезапускаю на нём CI последнего коммита. И почему-то постоянно результаты не радуют. Lithium упал на трёх платформах, как такое может быть — загадка. Потом когда-нибудь надо будет сесть…
Пофиксили с Юлей, всё как всегда — ноль проблем в коде и десяток в тулинге вокруг него. Куча капельку сломанных таргетов, в половине ситуаций непонятно кого винить.

У меня только один вопрос, может кто знает ответ: внезапно сломалось очень много тулинга, связанного с доктестами. В куски кода, которые раньше ставили только RUSTFLAGS, пришлось добавить RUSTDOCFLAGS. Что за последние месяцы такого могло случиться, что на это могло повлиять? Логов за тот период, чтобы сравнить, к сожалению, нет, хотя при желании можно попробовать просто со старым cargo сравнить.



Ах, понятно, конечно, чёт я сходу не догадалась: доктесты наконец-то начали запускаться для нехостовых таргетов: https://github.com/rust-lang/cargo/pull/15462
Please open Telegram to view this post
VIEW IN TELEGRAM
Я долгое время думала, что в десктопной версии Telegram нельзя посмотреть статистику канала. Потому что в мобильной версии это заметный пункт в меню, а в десктопной его как будто бы нет (см. фотокарточку 1). В менюшке, которая "Channel Info" в сайдбаре, тоже нет ничего похожего, как и в окне, которое открывается по нажатию "Manage Channel".

Так вот: если вы нажмёте на заголовок канала, откроется окно "Channel Info", которое полностью совпадает с сайдбаром, но ещё содержит отдельную кнопку меню. И вот это меню сильно похоже на меню, которое доступно сразу в заголовке, но содержит ещё парочку опций — в частности статистику.

Дизайнеры, блин...
Генераторы — очень удобная штука.

Вот, например, есть у вас AST, и вы хотите пройтись по всем его нодам. В норме вы делаете на AST-ноде рекурсивный метод, которому подаётся callback, и этот callback он потом вызывает на разных уровнях вложенности. Можно ещё встретить visitor-класс, но это суть то же самое. Проблем с таким подходом много: break не сделаешь, ошибку без костылей не выбросишь, комбинаторы типа .map/.fold не используешь. Часть можно исправить, но только ценой усложнения собственно кода метода.

Генераторы же упрощают и API, и его реализацию. С точки зрения пользователя — вы просто пишете обычный for по итератору и можете при желании использовать комбинаторы, с точки зрения реализации — написать yield/yield from в паре мест.

Но есть нюанс. Пример с AST не просто так: AST может быть достаточно сильно вложенный, и должен возникнуть вопрос: а как же во время yield управление передаётся из глубины стека куда-то наружу, а потом обратно внутрь? Какая такая магия позволяет эффективно делать эти операции?

В большинстве языков ответ — "никакая". Генератор — это суть штука, которой можно сказать "покрутись и дай следующий элемент". При первом запуске генератора, как только управление доходит до yield, значение возвращается по всему стеку вызовов наверх, прям как с return. При этом сохраняются значения переменных на стеке и для каждой функции — строка кода, которая исполнялась на тот момент. При следующем запросе элемента стек вызовов из этих данных восстанавливается и управление передаётся обратно. Никакого явного сохранения локальных переменных обычно, конечно, нет, но рекурсивные вызовы и возвраты никуда не деваются.

А это значит, что если у вас есть AST из N вложенных нод, итерация по этому AST генераторами будет работать за квадрат — в отличие от подхода с коллбеками, который даст здесь линию, потому что ему прыгать наверх не надо, он коллбек откуда угодно запустить может.

Ровно то же самое, кстати, происходит и с асинхронными функциями. Когда вы делаете await на какой-то task, он поднимается по стеку вызовов наверх до рантайма. Рантайм ждёт, когда задача исполнится, и после этого пропихивает её результат обратно вниз.

Это всё максимально забавно ещё потому, что JavaScript, который всегда в асинках опирался на неудобные callback'и и промисы, таки умудряется свой async/await сделать линейным — как раз благодаря коллбекам. Потому в джаваскрипте рантайм сидит не сверху стека вызовов, а отдельно, и рекурсивные вызовы по сути образуют просто море маленьких корутин, которые друг на друга повесили event handler'ы. И когда промис резолвится, его обработчики вызывается напрямую — а их родители вызовутся только если зарезолвятся дети. На генераторы это, впрочем, не распространяется. У C# история примерно такая же; больше языков с таким подходом я не знаю.

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

Короче говоря, из-за всего этого я не использую рекурсивные генераторы, а жру коллбеки. И вам того же советую.
PSA: На YouTube можно навигироваться покадрово клавишами , и .
https://math.stackexchange.com/questions/5074503/can-pa-prove-each-goodstein-sequence-can-be-proven-in-pa-to-reach-zero

Прикольный тред на тему того, что арифметика Пеано умеет доказывать существование доказательств всяких высокоуровневых фактов, но не доказывать собственно сами эти факты (немного кликбейтная формулировка, но тут такая специфика, что языком нюансы не передашь)
2025/06/16 13:40:09
Back to Top
HTML Embed Code: