программирование
Поправил ошибку в bc
Вчера часа проснулся около часа ночи, сна ни в одном глазу.
Бродил по коду
for (i = 0; i < f->autos.len; ++i)
{
// Get the auto.
BcAuto* aptr = bc_vec_item(&f->autos, i);
// If they match, barf.
if (BC_ERR(idx == aptr->idx && type == aptr->type))
{
const char* array = type == BC_TYPE_ARRAY ? "[]" : "";
bc_error(BC_ERR_PARSE_DUP_LOCAL, line, name, array);
}
}Посмотрел что тут происходит и заметил, что в перечислении, описывающем виды параметров,
define a(*t[], t[]) {}Подумал, что уже достаточно утомился, чтобы лечь спать,
Уже утром я прочитал сообщение по моему коммиту — автор bc попросил отформатировать код и написать небольшой тест. Я всё исправил и теперь ещё немного моего кода есть в этой прекрасной утилите!
Атрибут LANGUAGE
Просматривал тут публичные репозитории некого соискателя одной из наших вакансий, обратил внимание, что у него там, помимо всего прочего, лежат решённые задания со «Стэпика». Заглянул из любопытства, никаких особенных выводов не сделал, но удивился, что в тегах одного задания у него script содержит атрибут language. Это что же, его до сих пор учат писать?
Мне стало любопытно — он вообще сейчас
Проверил. В документации он считается устаревшим, но если подставить туда неизвестное браузеру значение, скрипт не выполняется. В принципе, это логично. В интернете вполне могут встречаться ещё и древние сайты, использующие другие языки, зачем выполнять то, что браузер выполнить не может?
А что же считается верным значением для ДжаваСкрипта? В угадайку играть не хотелось, скачал исходники «Хромиума», посмотрел что там используется. Как оказалось, подставить можно следующие значения (регистр значения не имеет):
- javascript или x-javascript — это понятно, единственный сюрприз, что можно поставить «экс» перед словом;
- ecmascript или x-ecmascript — тоже понятно, ДжаваСкрипт — расширение этого языка;
- jscript — диалект языка в «Эксплорере», считается, что другие браузеры сейчас его тоже «знают», хотя на деле есть нюансы, но всем уже всё равно;
- от javascript1.0 до javascript1.5 —
когда-то указанием версии можно было разделять скрипты по уровню поддержки языка в браузере; - livescript — старое название языка «ДжаваСкрипта», я ещё помню времена, когда это значение использовалось, хотя и редко.
Сейчас, конечно, это скорее археология, чем
Сжатие и картинки
Я тут размышлял о том, что в вебе, когда человечество изобретает новый алгоритм сжатия, для текстовых файлов, содержащих программный код или разметку, никто не придумывает новый формат, в котором это всё будет передаваться.
Поступают иначе — в браузер и сервер вносят реализацию сжатия и распаковки и те же самые текстовые данные начинают передаваться чуточку быстрее.
С картинками так не происходит. Понятно, что многие форматы имеют специальные алгоритмы для удаления части информации, которая не так уж и важна глазу, зато позволяет эффективнее сжимать,
Например, в PNG это DEFLATE, в GIF — LZW.
В принципе, никто не мешает наложить встроенный в браузер алгоритм сжатия на формат графики, — протокол передачи это позволяет. Я уже экспериментировал подобным образом с форматом GIF, так как оказалось, что этот формат позволяет выключить встроенное сжатие. То есть я как бы выкидывал используемый в GIF древний LZW и заменял его на более современный DEFLATE, реализованный в браузере.
Тогда этот эксперимент ничего не дал, но недавно я его повторил. Только вместо DEFLATE использовал brotli, ещё более свежий алгоритм. GIF, в этом случае, стал меньше PNG. Это, кстати, не всегда так происходит, но в этом случае повезло. Я попробовал ещё и zstd, он ещё новее, но он жмёт хуже brotli, его достоинство в скорости.
Любопытно ещё, что когда я сжал через brotli картинку, сконвертированную в формат AVIF, она стала ещё меньше. Я такого не ожидал, мне казалось, что современные бинарные форматы делаются максимально экономно.
В общем, схема со сжатием графики в браузере, как будто бы, имеет право на существование.
Приняли коммит в bc
Ненормальное программирование ★
Введение
Недавно разговорился с одним приятелем, тема разговора
Я его удивил, рассказав, что тема «непрактичного» или, как я его ещё называю, «ненормального программирования» привлекает меня давно, по меньшей мере с конца
Более нормальным программированием я тоже занимаюсь, но чаще всего это работа
«Junix» (1999—2003)
Это самый старый из сохранившихся подобных проектов. И, наверное, самый монументальный. Дело было в конце
Мне же было очевидно, что ДжаваСкрипт уже можно использовать для большего. Я так был уверен в своей идее, что был готов продемонстрировать её всему миру. Проект занял несколько лет — в начальной фазе я занимался им каждый вечер на протяжении нескольких месяцев, а потом изредка реализовывал
Так у меня получилось
«JS-FTPd » (2001)
В то время, когда я делал этот проект, ДжаваСкрипт ещё не был монополистом в мире браузерных языков. Существовало несколько альтернативных технологий, так, например, в браузерах была технология «апплетов», позволявших писать небольшие встраиваемые в браузер приложения на языке Джава.
В то же время в браузере «Нетскейп» была нестандартная технология «LiveConnect», которая позволяла вызывать Джаву из ДжаваСкрипта, а так же запрашивать из браузера различные разрешения, например, на сетевое взаимодействие. Впервые я познакомился с этой технологией, когда писал «Junix», там я через неё работал с диском компьютера, а в этом проекте мне захотелось изучить работу с сокетами.
Используя «LiveConnect» я написал FTP-сервер — на ДжаваСкрипте, вызывая Джаву там, где ДжаваСкрипт
«Крестики-нолики » на HTML и CSS (2011)
Тема игр для компьютера без программирования меня давно интересовала.Я написал их несколько. В основном, с использованием HTML и CSS. Ни тот ни другой языками программирования не считаются, тем не менее, развились настолько, что на них можно писать очень сложные интерактивные вещи.
«Арканоид» на bash (2011)
В рамках этой игры я хотел испытать две концепции.
«Шахматы» на языке утилиты sed (2013)
Когда я решил написать шахматы на sed, знакомые, с которыми я поделился своей идеей, подумали, что я сошёл с ума. Ну а как иначе? sed — в их глазах не язык программирования, а текстовый редактор, который умеет менять «кота» на «собаку» в конфигах.
Но мне хотелось доказать, что даже в таком примитиве можно выжать больше, чем кажется. В итоге я провёл две недели в диалоге с регулярными выражениями: заставлял их считать, сравнивать числа в придуманном мной формате 111::11 (так в нём записывается число 302) и имитировать логику шахматного ИИ. Каждый день я порывался бросить: когда код превращался в лабиринт из меток и переходов, а функция вычитания занимала 59 строк, но упрямство перевешивало.
Как я уже сказал, большая часть логики сделана на регулярных выражениях, поверх которых я написал
«Тетрис» на языке AppleScript (2017)
Встроенный в «МакОс» язык автоматизации «ЭплСкрипт», кажется, никто никогда для написания игр использовать и не пытался. По крайней мере, я таких попыток не обнаружил. И немудрено — сходу неясно чем бы он мог таким оперировать, чтобы взаимодействовать с пользователем.
Я написал «Тетрис» на нём, когда понял, что графику можно выводить, манипулируя свёрнутыми в точку окнами приложений и опрашивая клавиши регулирования громкости.
«Морской бой» на клавише «Капслок» (2020)
Игру «Морской бой» я написал по двум причинам.
Идея с клавишей пришла в голову, когда я в очередной раз размышлял о бессмысленной для меня клавише «Капслок» — я её совершенно не использую и всегда отключаю на уровне операционной системы.
В итоге я написал игру, где всё взаимодействие с компьютером делается через азбуку Морзе — пользуясь одной клавишей, как ключом, мы вводим координаты для игры в «Морской бой», а ответы компьютера считываем через мигание светодиода на «Капслоке». Картинка ниже — это отладочный режим, его можно включить,
«Вампус» для наручных часов в корпусе «Касио» (2024)
Я вообще очень люблю различные
До сих пор ношу эти часы почти каждый день, они у меня сейчас самые любимые.
Но если уж я добыл себе эти часы, было бы логично
«Вампус» на командах командной строки (2024)
Была у меня ещё одна идея — сделать
Путём разных манипуляций я ввёл целый набор всяких ограничений, чтобы шелл напоминал игру, а не просто командную строку, но если знать как всё устроено, это всё, конечно, можно обойти. Тем не менее, мне кажется, что с целью я справился.
«PowerShoot» игра, управляемая подключением зарядки (2024)
Игра, написанная под влиянием болезни. Я довольно тяжело болел и хотелось развлечь себя
На искомую идею меня натолкнул звук, который раздаётся, когда «Макбук» подключается к зарядке. Я задумался, что можно же программно узнать — заряжается ноутбук или нет, а потом уже, от скуки, развил эту мысль до небольшой игрушки. В ней игрок выпускает в сторону врагов бомбу, когда ноутбук подключается к зарядке.
«Дум» на языке калькулятора bc (2025)
Концепция вывода графики в текстовую консоль существует давно — в различных терминалах было последовательно придумано несколько способов как это сделать, что меня весьма интриговало. Хотя и понятно, что терминалы нынче никакие не текстовые, всё равно графика там поначалу
Во мне идея сделать
Хотя первые несколько часов хакатона я был в отчаянии от своей идеи и мне казалось, что я либо ничего не сделаю, либо не уложусь в срок, у меня всё получилось — за сутки я сделал порт игры «Wolf5k» на bc и придумал как на этом языке генерировать графику. А заодно доказал, что кодить сутки с раннего утра и до трёх ночи можно и в 47 лет.
«Throttling» игра, использующая для отображения мониторинг загрузки процессора (2025)
Как я недавно рассказывал, эта игра основывается на моей старой идее — выводить пульсацию проигрываемой мелодии через «Мониторинг ресурсов» процессора. В старых «Макбуках» каждому ядру соответствовал свой столбик и я даже написал код,
Недавно я этот код случайно нашёл и придумал небольшую игрушку. Её несложный сюжет, остающийся за рамками игрового процесса, таков: вы оператор блока реактора на АЭС, у вас отключается автоматика и вам нужно вручную удерживать температуру внутри блока, которая отображается как загрузка процессора.
Заключение
Я отобрал всего несколько проектов из того «ненормального», что я делал. Надеюсь, вполне очевидно, что «ненормальное» программирование я ценю и люблю очень давно. Для меня это не просто странное баловство, а способ изучить
Программирование, как например, и рисование, может быть процессом получения как можно более точной копией задачи, просто в другой форме, как, например, в реализме. В целом, нужный и почитаемый навык, но оно может быть и очень абстрактным, отражающим иную реальность, такой, какой она видится внутри, как идея.
Мучительная потребность поделиться своими внутренними идеями, думаю, знакома любому художнику. Я делаю свои странные программы по той же причине — потому что не могу, да и не хочу держать эти идеи только внутри себя.
Ускорение операций в bc
Как вы помните, я недавно устроил себе суточный хакатон, писал «Дум» на bc. Получился он тогда довольно медленным, особенно когда на экране было много объектов. Сейчас я его потихоньку оптимизирую, но так как профилирования в bc не существует, двигаюсь наугад.
По сути я до сих пор не знаю, на что там уходит больше всего времени, но иногда выбираю
В стандартной библиотеке bc есть много функций, написанных нативно. Их, как правило, удаётся так или иначе ускорить, поэтому я решил посмотреть что у меня из такого ещё используется. Взор пал на функцию band(), особенно в форме (band(a, b) && 1), то есть там, где всё, что меня интересует — установлен бит или нет. У меня в коде было несколько таких мест.
Написал свой вариант по мотивам того кода, который используется в стандартной библиотеке:
define check_bit(x, y) {
auto result, m[]
result = 0
scale = 0
while (x && y) {
x = divmod(x, 2, m[])
if (m[0]) {
y = divmod(y, 2, m[])
result += m[0]
} else {
y /= 2
}
}
return result
}Получилось быстрее почти в два с половиной раза. Саму операцию band() тоже удалось ускорить в 1,7 раза, в «чистом» виде она у меня в коде тоже есть. Думаю, может сделать коммит в код bc?
Добавлено: коммит сделал. Но беда — автор bc в примечании к репозиторию на «Гитхабе» написал, что с «Гитхаба» он ушёл. К сожалению, то место, куда он ушёл не работает. Так что коммит пришлось залить на «Гитхаб»,
Throttling game
В полку́ странных игр прибыло! Помните я писал игру, где надо управлять человечком, подключая и отключая зарядку «Макбука»? Вчера вечером написал небольшую игру из той же оперы.
Фабула игры такова: у одного из блоков атомной электростанции сработала система предупреждения — все три уровня защиты вырубились, теперь управлять реактором нужно вручную. График загрузки процессора символизирует температуру внутри блока — много плохо, мало — тоже плохо.
Чтобы поиграть в эту игру, надо открыть рядом «Монитор ресурсов» и вывести график загрузки процессоров. Частоту обновления («Вид» → «Частота обновления») надо поставить «Очень часто».
После этого можно запустить игру и, управляя загрузкой процессора через клавишу «Касплок» («капс» включен — процессор загружен), держать загрузку между 10 и 90%. После старта у вас будет четыре секунды, чтобы подготовиться, вывести все нужные окна на передний план и нащупать кнопку.
На идею меня навела бессмертная игра «Посадка на Луну» для калькулятора «Электроника
Код игрушки лежит в моём репозитории.
Улучшение в словаре на bc
За последние недели мой уровень в bc очень сильно вырос. Я стал видеть вещи в своих прежних проектах, которые можно сделать проще и изящнее. Но, кажется, мой рост остановился. Интересно, достиг ли я потолка? Или, может, стал ли я сеньором в этом? Не пора ли мне учить других? Открыть курсы, поднимать сообщество? Вопросы без ответов!
В общем, открыл сегодня, пока ехал в такси, исходник своего
Почему же я
Добавлено: грустно — на скриншоте обнаружил ошибки в словаре, который я использую.
Быстрый корень из суммы квадратов
Я недавно писал как ускорял свой «Дум» для bc. В частности я заменял тригонометрические функции их менее точными, но более быстрыми, аналогами.
Роман Парпалак посоветовал способ ускорения вычисления квадратного корня из суммы квадратов. Аппроксимация там через формулу 0,96 × x + 0,4 × y при x ≥ y. Погрешность должна быть не более 4%.
Выглядит здорово, я замерил — производительность вырастает в восемь раз, заманчиво, но если посмотреть абсолютную погрешность там она почти 5,5 единиц. Это как будто бы очень много. Я даже нарисовал график разницы между корнем и аппроксимацией в «Днунипере».
Заменять я не буду, но знать о такой возможности полезно, спасибо, Роман!
Что там нечётные числа?
Возможно вы уже забыли, но в конце прошлого года я написал небольшую программу, которая искала числа, генерируемые по определённому закону. Рассчитывая быстро упереться в ограничения целого типа в Си, я запускал её на своём ноутбуке, но конца вычислений не дождался.
Тогда я сократил размерность типа вдвое — стал вычислять
Сегодня мне надоело, я этот процесс остановил. Вывод — на одном ядре считать совершенно не вариант, надо дописывать многопоточную версию.
Я, кстати, её даже начинал писать — накидал
Единственное, в чём я признателен нейросеткам, — они решили мою проблему с производительность. Моя многопоточная версия упиралась в блокировку некого разделяемого ресурса и я решил посмотреть справятся ли нейросети с этой проблемой. Справились! В остальном время было потрачено зря.
Надо бы уже показать кованным мешкам кто тут папочка и дописать код самостоятельно.
Doom на bc
Давно хотел повысить градус безумия своих приложений и изобразить
Писать начал, разумеется, на одном из самых далёких от вывода графики на экран языков — bc, я ведь теперь всё на нём пишу
Как вообще устроен вывод графики в текстовый терминал? Надо сказать, что как правило, терминал у нас текстовый число номинально. Да, там выводятся
Обычно мы отсылаем в терминал
ESC ] 1337 ; File = [необязательные параметры] : изображение в формате base64 ^GХорошо, с этим разобрались. Но как нарисовать картинку именно из bc? Тут надо решить несколько очевидных проблем.
BMP — это, как водится во многих старых форматах, внутренне не один формат хранения графики, а несколько. В данном случае я взял самый простой и примитивный — BMP Core с индексированной палитрой из двух цветов.
Его плюс — у него очень простой заголовок и его относительно дёшево формировать. Тем более, что мне не хотелось возиться со сжатием, количество вычислений для этого могло превысить все разумные пределы.
Теперь надо было решить что же я буду писать. На самом деле к этому моменту я уже внутренне определился. В начале двухтысячных локальный фурор произвела игрушка «Wolf5k», написанная на ДжаваСкрипте. Чтобы понять, чем она крута, надо знать, что ДжаваСкрипт тогда графику выводить не умел — не существовало ни тега CANVAS, ни, тем более, WebGL или WebGPU.
Изображение формировалось в текстовом формате XBM, которое, через весьма остроумный хак, показывалось в браузере. На настоящий момент поддержки этого формата давно уже нет ни в одном современном браузере.
Это как будто могло быть очень символично — сделать порт игрушки, которая выводила свою графику на языке, для этого в те годы непригодном, на язык, который так же непригоден для этого сейчас.
К несчастью, исходный код игры обфусцирован и, когда я в самом начале сел за его чтение, был в отчаянии — мне казалось, что за отведённый самому себе суточный срок разобраться не получится. К счастью, раскопки
Не буду описывать все сложности, с которыми пришлось столкнуться, но примерно к полуночи у меня получился код, который умел рендерить картинку покадрово — к тому времени я ещё не решил проблему опроса клавиатуры и мой «игрок» просто крутился на одном месте. В рендере я увидел несколько проблем, но не критичных.
Самой большой проблемой была производительность. Но это я отложил на следующий день — решил, что работающая, но немного глючащая и сильно подтормаживающая игра, написанная за сутки — удовлетворительный результат. До окончания хакатона мне предстояло решить ещё одну проблему — проблему опроса клавиатуры.
«Wolf5k» — динамичная игра, а во всём языке bc единственная функция ввода — read(), которая требует нажатия на «Энтер» после каждого ввода. Такой себе интерактив.
В самом языке это никак не решить, так что, признаюсь, тут я немного смухлевал — написал на zsh запускающую часть, которая сама читает клавиатуру и пересылает нажатия в bc, «нажимая» на «Энтер»:
#!/bin/zsh
x=/*
while {}; do
v=
read -rs -k1 -t0.4 v
case "${v:l}" in
[wasdljkm\ ]) printf "%d\n" "'${v:l}";;
*) echo 0;;
esac
done | bc -g -Ll -f "$0"
exit; */1
/* тут программа на bc */Да, не очень честно, но какой выбор?
К трём ночи у меня была игрушка, в которой всё работало на три с плюсом, особенно, если на экране было не так много объектов. Правда, при поворотах враги иногда пропадали, а иногда взлетали и улетали за пределы экрана. Но в это уже можно было играть.
У меня был соблазн опубликовать её на следующий же день, но хорошо, что я дал коду «отлежаться».
Результат выложил в свой репозиторий. Несмотря на то, что это порт «Wolf5k», игра называется
Ускорение тригонометрических и прочих функций в bc
Интересно, что вся стандартная библиотека (подключаемая через ключ -l) для bc написана нативно. С одной стороны, это
Я тут потихоньку ускоряю одну из своих программ на bc, но все «низко висящие фрукты» я уже подобрал. Добрался до тригонометрических функций. Во внутренней библиотеке косинус выражается через синус, так что, если ускорить синус, то ускорится сразу и косинус.
Посмотрел разные реализации, в итоге портировал функцию, которую взял из одной из статей на «Хабре», получилось так (используется многочлен Чебышёва):
define mod(a, b) {
auto s, m
s = scale; scale = 0; m = a$ % b$; scale = s; return m
}
define s(x) {
auto sign, xf, per, xx, y
x *= 0.63661977236758134308
if (sign = (x < 0)) x = -x
xf = x$
x -= xf
if (mod(xf, 2)) x = 1 - x
per = mod(xf / 2, 2)
xx = x * x
y = x * (1.5707903005870776 + xx * (-0.6458858977085938 + \
xx*(0.07941798513358536 - 0.0043223880120647346 * xx)))
if (sign != per) return -y
return y
}Расхождение со встроенной в bc реализацией начинается в седьмом знаке, это более чем достаточно для меня, зато вычисление синуса ускорилось больше, чем в четыре раза.
Добавлено позднее: ещё я заменил функцию нахождения логарифма по основанию десять. Логарифмом я находил количество разрядов числа, чтобы потом вывести его на экран, так что его легко было заменить на цикл с подсчётом порядка делением. Разница в производительности — больше, чем в 60 раз. Код теперь выглядит так:
define cnt_digits(x) {
auto i, s
s = scale; scale = 0
for (i = 0; x /= 10; i++){}
scale = s; return i
}Ещё немного про random() в bc
Как я уже рассказывал, bc под «Линуксом» очень старый, — на всех машинах, куда у меня есть доступ, установлена версия 1.07.1
Ранее я обнаружил недокументированную функцию random(), но генератор случайных чисел в линуксовом bc не инициализируется и функция выдаёт на старте всегда одно и тоже число. Её можно покрутить снаружи случайное число раз и это исправляет ситуацию, но способ, который я тогда придумал для этого, так себе.
Сегодня, пока ехал в такси, придумывал ещё более так себе способы это сделать.
Мысль моя крутилась вокруг запуска через утилиту env. В мире командной строки так частенько делают, так что этот способ как будто бы чуть более честный, так шелл тут не используется:
#!/usr/bin/env -S -i seed_="0${XDG_SESSION_ID};while(seed_--).=random()#" bc /proc/self/environ
print random()
quit()Здорово, правда?
Вся чёрная магия сосредоточена в первой строке. Файл /proc/self/environ формируется в «Линуксе» для каждого запущенного процесса и содержит унаследованные переменные окружения. Так как там много ненужного нам мусора, очищаем его целиком при помощи ключа -i утилиты env.
Далее мы создаём переменную окружения seed_, а внутри её формируем небольшую программу, которая запустится до нашего кода. Для bc результат будет выглядеть так:
seed_=0123456
while(seed_--).=random()
#^@
print random()
quit()Я развернул односрочную программу в многострочную для удобства.
Вместо 123456, конечно, будет подставлено число из переменной окружения $XDG_SESSION_ID, которая содержит число. Ноль там впереди, чтобы не случилось ничего страшного, если эта переменная в вашем дистрибутиве «Линукс» отсутствует.
Мне кажется, это неплохой способ, жаль только что содержимое $XDG_SESSION_ID остаётся одинаковым на всём протяжении жизни сессии. То есть, если запустить программу несколько раз в одном и том же окне терминала, случайные числа будут генерироваться всегда в одной и той же последовательности.
В конце файла /proc/self/environ всегда есть символ с кодом ноль, который мог бы нам всё испортить, но мы спрятали его за символом комментария — #.
Как видно, функция random() вызывается seed_ раз, что позволяет остановиться
Несложно додуматься до похожего способа, который позволяет читать число
Англо-русский словарь на bc
Пока писал «Виселицу» на bc, в процессе немало с ним экспериментировал. Обратил внимание на то, какой он шустрый. Я не делал прямых сравнений, но сложилось такое впечатление. Подмывало
Позже, не справившись с исследовательским зудом, решил попробовать сделать на нём
Сделал так же, как в версии под «Линукс» — bc переводится на ввод
Правда есть проблема — под «Мак» слова надо вводить исключительно в верхнем регистре, иначе они трактуются как имена переменных. Можно было бы создать, конечно, кучу переменных, но это работает не очень — некоторые английские слова совпадают с ключевыми словами языка.
Так что я поступил иначе — проверяю версию bc и, если она достаточной новая (как под «Маком»), запускаю bc через tr, который заменяет буквы в нижнем регистре на буквы в верхнем. В этом случае, начало программы выглядит так:
#!/bin/sh
c=/*
tr -u a-z A-Z | bc -R "$0"
exit; */1Похожий пролог я уже использовал в прошлый раз, когда надо было «накрутить» рандомизирующую функцию.
Сам словарь нашёлся в интернете. Чтобы не заливать в свой репозиторий чужую интеллектуальную собственность, я сделал make-файл, генерирующий мне программу на bc — словарь скачивается, распаковывается и обрабатывается специально написанной программой на Пайтоне. В итоге получается программа на пятьдесят тысяч с гаком строк.
В результате всё работает шустро, несмотря на линейный поиск. Я вообще не замечаю проблем с производительностью у bc, любопытно. Умели же делать!
Чтобы минимизировать программу по объёму, я сделал два цикла, а останов поиска делаю через break — он позволяет выйти из внутреннего цикла к началу внешнего:
while(c) while (c) {
ibase=36;c=read();ibase=A
if(c==19976){"Несколько";break}
/* тут очень много строк */
if(c==2813515210532){"Сухарь";break}
if(c==2174201762){"Зигота";break}
"Неизвестное слово";}
quit()Ноль используется для выхода — у меня в Makefile есть небольшой тест, вот он и использует ноль, чтобы прекратить выполнение получившейся программы.
Результат, как обычно, можно увидеть в моём репозитории.
Игра «Виселица» на bc под «Линукс»
Я планировал заняться адаптацией «Виселицы» на bc под «Линукс» на следующий день, то есть сегодня, но не выдержал и вчера просидел над этим почти до полуночи. Линуксовый bc, конечно, гораздо скромнее по возножностям, было где поломать мозг, но больше всего времени я, кажется, потратил на рандомизацию.
Я вчера писал уже, что в документации для линуксового bc нет упоминаний функций для генерации случайных чисел, но чтение исходников показало, что документации не всегда можно верить. Функция есть, называется она random(), но не работает так как хочется — при вызове генерирует всегда одну и ту же последовательность.
Видимо, её забыли правильно проинициализировать. Непонятно почему за столько лет это нельзя исправить, ведь новые версии иногда выходят, правда в списке изменений последних версий сплошь
Так как никаких способов исправить ситуацию, не меняя исходный код bc, я не обнаружил, решил обойтись хаком — при запуске вызвать random() случайное количество раз, чтобы в коде моей программу его внутреннее состояние тоже стало случайным.
Для этого я изменил способ запуска программы — сделал его
#!/bin/bash
x=/*
BC_LINE_LENGTH=0 bc -l -q <(/usr/bin/seq -f "x=random()+%g" $RANDOM) "$0"
exit; */1Обвязка с комментарием сделана так, чтобы и шелл, и bc не увидели в коде ничего странно и не выдали ошибку синтаксиса, а вся мякотка происходит в строке /usr/bin/seq -f "x=random()+%g" $RANDOM. Команда seq генерирует последовательность x=random()+число столько раз, сколько указано в переменной $RANDOM. $RANDOM — это специальная переменная шелла, которая содержит случайное значение.
Подстановка %g необходима, чтобы заработал шаблон в параметре команды seq, без этого она ругается на неправильный аргумент.
Ещё одной крупной проблемой стали строки — в этой версии bc не поддерживается присваивание их переменным.
Для вывода, вместо кучи переменных, пришлось сделать функцию, которая выводит букву по её коду через горку условий. Код получился похожим на то место, где я выбираю слово по номеру:
define void print_letter(c) {
if(!--c){"a"}
if(!--c){"b"}
if(!--c){"c"}
/* …тут все 26 букв */
if(!--c){"y"}
if(!--c){"z"}
}Я не стал ставить return, чтобы сделать функцию более визуально компактной, тем более, что на производительность мне, по большому счёту, наплевать.
Кстати, ввод удалось сильно упростить, перейдя на тридцатишестиричную систему исчисления, где все английские буквы соответствую числам:
define read_letter() {
auto z, b
b = ibase
ibase = 36
z = read()
ibase = b
return z - 9
}Переменная ibase отвечает за систему исчисления при вводе чисел.
Таким образом, численное исчисление получили не только все одиночные буквы, но и все слова. В теории, это можно использовать для ввода одиночных команд на английском языке. Я использую это свойство, чтобы распознать команду exit.
Остальные проблемы решались попроще. Ещё я немного поломал голову чем заменить ceil (округление вверх), потом догадался прибавить единицу и, выставив через специальную переменную scale нулевую точность для цифр после запятой, разделил получившееся значение на один, чтобы отбросить дробную часть:
define word_len(c) {
auto r
scale=10
r = l(c) / l(256) + 1
scale=0
return r/1
}Функцию логарифма log() тут пришлось заменить на натуральный логарифм l() — это одна из немногих математических функций, которые поддерживаются в этой версии bc.
Остальные замены довольно тривиальны, я писал о них ещё в прошлый раз. Результат положил отдельным файлом в моём репозитории.
Игра «Виселица» на bc
После того как я написал «бутылки пива» на bc, стало интересно попробовать написать на этом языке
В силу специфики обработки ввода bc, писать нужно
На пути к успеху сразу начались трудности. Несмотря на то, что в bc есть многообещающая функция read(), оказалось, что с её помощью можно вводить только выражения языка и числа. В вышеупомянутом «пасьянсе» автор обошёлся числами, а мне нужны буквы, иначе играть будет невозможно.
К счастью, «выражения» — это ещё и переменные. Если задать значения всем однобуквенным переменным, мы сможем определить какую именно букву нажал пользователь.
К тому же можно ещё сразу сделать таблицу, которая позволит нам рисовать клавиатуру, так как других способов вывести последовательно буквы в языке нет.
a=1;b=2;c=3;d=4;e=5;f=6;g=7;h=8;i=9;j=10;k=11;l=12;m=13;n=14;o=15;p=16;q=17;r=18;s=19;t=20
u=21;v=22;w=23;x=24;y=25;z=26
co[a]="a";co[b]="b";co[c]="c";co[d]="d";co[e]="e";co[f]="f";co[g]="g";co[h]="h";co[i]="i";co[j]="j"
co[k]="k";co[l]="l";co[m]="m";co[n]="n";co[o]="o";co[p]="p";co[q]="q";co[r]="r";co[s]="s";co[t]="t"
co[u]="u";co[v]="v";co[w]="w";co[x]="x";co[y]="y";co[z]="z"
exit = -1Заодно я задал значение переменной exit, это слово пользователь может ввести, чтобы выйти.
Весь интерфейс рисуется при помощи управляющих последовательностей, благо хоть тут проблем нет — указываешь коды прямо в строке и всё работает.
Кстати о строках. В языке они присутствуют, но, по большому счёту, сделать с ними ничего нельзя. Например, в игре нужна функция поиска буквы в строке или хотя бы получения символа по индексу. Ничего такого нет даже в помине. Как тогда быть?
Единственное решение, которое приходит в голову — кодировать слова, как числа. 26 букв легко умещаются в один байт, поэтому можно кодировать по байту на цифру. Я написал функцию на несколько тысяч строк, которая содержит закодированные слова и возвращает одно из них, в зависимости от аргумента:
define get_word(c) {
if(!--c) return 4328588043
if(!--c) return 4328588820
/* и так далее */
if(!--c) return 437194509
return -c
}
word = get_word(irand(get_word(0)) + 1)Как видно, если передать в функцию ноль, она вернёт количество элементов, это полезно для того, чтобы знать до какого предела генерировать случайное число.
Функция irand, используемая тут, является расширением bc, у меня на «Маке» она поддерживается, но под «Линуксом» её нет, так же как и вообще любой другой функции для работы со случайными числами.
С такими «строками» можно уже
define word_len(c) {
scale=10
return ceil(log(c, 256), 0)
}Или вывести на печать:
define void print_word(c) {
auto word, i, mod
scale=0
for (i = 0; c; i++) {
c = divmod(c, 256, mod[])
word[i] = mod[0]
}
while (i--) {
print co[word[i]]
}
}В общем, всё как с обычными строками, только
Остальное,
В теории, игру можно портировать и на «Линукс», надо только избавиться от хранения строк в переменных, заменить генерацию случайных чисел на внешний генератор, заменить log() на l(), а divmod() на деление и взятие модуля и избавиться от length() — массив в качестве аргумента в «Линуксе» не поддерживается.
99 бутылок: калькулятор bc
Разбирал одну старенькую научную статью с некоторым количеством математики, а внутри увидел вычисления на bc.
Не особо с ним знаком, но несколько раз пользовался, даже не вникая что он умеет. Честно говоря, не помню зачем он мне тогда понадобился, наверное надо было
В общем, оказалось, там внутри целый язык программирования, с циклами, ветвлениями, локальными переменными, строками, массивами и числам. Поддерживаются даже пользовательские функции. Решил попробовать написать на нём очередную «песню о пиве».
82. bc — интерактивный интерпретатор
/* Beer song. Evgeny Stepanischev, Jan 2025 */
define void bottles(b) {
if (b) print b else print "No"
" bottle"
if (b-1) print "s"
" of beer"
}
for(beer = 99; beer;) {
bottles(beer); " on the wall, "
bottles(beer); print "!\n"
print "Take one down, pass it around,\n"
bottles(--beer); print " on the wall!\n\n"
}
print "No more bottles of beer on the wall,\n"
print "No more bottles of beer!\n"
print "Go to the store and buy some more,\n"
bottles(99); " on the wall!"Единственное, что вызвало у меня затруднение — это как сделать так, чтобы функция ничего не возвращала. Дело в том, что функции в bc
Никаких других сложностей с этой программой у меня не возникло, язык простой, хорошо продуманный, лёгок в освоении.
Добавлено позднее: как оказалось, у языка есть свои особенности о которых нелишне знать. Например, подключение математической библиотеки меняет точность, определяемую переменной scale, что влияет, например, на операцию вычисления модуля, она начинает работать иначе.
Другая странность — операции сдвига работают в десятичной системе. Не понимаю какой от этого толк. Например, 1<<2 даст 100.
Возвращение плагина PogodaStatusBar
У меня есть традиция — время от времени чинить мой старенький и порядком уже поднадоевший мне плагин для редактора «Саблайм 3». Плагин называется PogodaStatusBar, потому что он
Эпоха бесплатных сервисов кончилась и «Яндекс» постепенно стал закрывал все те ходы, которыми я пользовался, чтобы получать эти данные. Я искал новые,
Всё нормально работало,
После новогоднего отдыха появились время и силы и я решил их часть израсходовать на плагин.
Надо сказать, что я не знаю как правильно отлаживать плагины в «Саблайме». В документации ничего не нашёл, поэтому написал небольшую обвязку и запускаю свой плагин просто из командной строки. Но в этот раз этот способ не сработал — в обвязке плагин работал, а внутри «Сайблайма» — нет.
Я уже с таким сталкивался. У «Саблайма» внутри две версии «Пайтона» — 3.3 и 3.8. Обе,
Таким образом между «Пайтоном» у меня в командной строке и внутри «Сайблайма» — десять версий. Иногда я использую в коде
И вот непонятно как об этом узнавать. Можно, думаю, раздобыть старенький «Пайтон» в контейнере, но лень.
Обычно я просто пристально смотрю на код, пока не понимаю как его исправить. А в этот раз так не вышло. Мне несколько раз казалось, что я понимаю в чём дело, я коммитил новый код, собирал релиз, дожидался, пока тулчейн репозитория плагинов «Саблайма» увидит новую версию, обновлялся и не получал положительного результата.
В итоге, мне это надоело. Я нашёл в какой папке «Саблайм» держит плагины, они там лежат, к слову, в архивированном виде, и попытался подсунуть туда свой код — паковал папку с модифицированным кодом и заменял файл плагина. Но гадкий «Саблайм»
С этим я опять поленился разбираться, вместо этого попытался отредактировать код прямо в архиве через консольный файловый менеджер mc. Неожиданно это сработало. Так я узнал, что ошибка была в том, что я использую вызов html.unescape, который появился только в «Пайтоне» 3.4. Пришлось заменить его на вполне устроивший меня в этом месте replace('"', '"') и всё заработало.
«Киберслав»
Вчера вышла первая серия отечественного сериала «Киберслав». Я выхода первой серии ждал несколько лет, с тех пор как впервые увидел тизер. Очень боялся, что результат окажется очень слабым,
Но обошлось, смотрится отлично.
В заставке серии, кстати, используется идея,
Заодно посмотрел как модуль curses в «Пайтоне» позволяет облегчить работу в консоли. Много про него слышал, но не доводилось попробовать.
UnSnow 2
Полтора десятилетия назад я написал скрипт «UnSnow», выключающий снежинки, которыми иногда украшают сайты. Мне очень нравится Новый год и новогодняя движуха, но эти снежинки частенько раздражают, тем более, что их нередко делают крупными и навязчивыми.
За столько лет способы, которыми фронтендеры добавляют на сайт снежинки, сильно изменились, поэтому прежний скрипт перестал работать, но снежинки никуда не делись. Так как за эти годы никто не поднял упавшее знамя борьбы с визуальным безумием, придётся мне.
Новая версия скрипта можем замедлять некоторые сайты, зато, кажется, справляется с любыми случаями падения вкуса. В браузер скрипт можно установить при помощи плагина Tampermonkey или любого другого, умеющего выполнять UserScript.
// ==UserScript==
// @name UnSnow
// @author Evgeny Stepanischev aka Bolk
// @version 2024-12-31
// @namespace /files/unsnow2.js
// @description Turn off stupid snow
// @match *://*/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
if (window.location.hostname === 'aliexpress.ru') {
return;
}
Array.from(document.querySelectorAll('*')).forEach(e => {
for (let a of e.attributes) {
if (/(?<!un)snow/i.test(a.name + a.value)) {
e.style = 'visibility:hidden;position:absolute;margin-left:-10000px;' +
'display:none;transform:translateX(-100vw);opacity:0';
}
}
});
})();