Я исследовал тему связности и связанности в построении кода и вот к чему пришел:
Не существует плохих\хороших\идеальных связности и связанности кода.
Мне кажется проблема и решение глубже - сколько людей столько и вариантов осмысления и построения "модели", столько вариантов же coupling & cohesion. У каждого что-то свое.
Строить приложение от архитектуры - такое себе. Архитектура для приложения, а не приложение для архитектуры. Тогда архитектура будет основана на реальных задачах а не на поиске идеала.
Самая лучшая архитектура как по мне это когда ты пишешь в ООП стиле, и вот тут как ты умеешь моделировать что-то, такая архитектура у тебя и получится, со всей связностью и связанностью.
Ведь суть всего этого моделирования чтобы "на понятном" объяснить железу что и когда нужно сделать.
Т.е. решение кроется в здравом смысле - описываешь приложение плюс-минус понятными человеку структурами и нужная связность и связанность образуются сами собой как следствие.
Ну и когда я говорю ООП, это не значит что я буду писать абстракцию на каждый чих, влоть до Int, Long и т.п., нет, это значит что я начну с самых больших MyApp { UserClient, ServerClient, DeviceClient } и законтачу их между собой логикой приложения, а там дальше буду создавать абстракции по необходимости, если будет удобно и полезно что-то добавить и переиспользовать то я добавлю и переиспользую (вот кстати хороший критерий - моделировать сущность когда надо что-то передавать между главными абсракциями(надсистемами)).
ООП рулит :)
P.S. И не надо стремиться к идеалу, иначе тут можно скатиться в подмену задач, и начать делать не данное конкретное приложение, а предложенный кем-то идеал архитектуры.
Заказчик отправил архив с десятками айтишных разработок и попросил в короткие сроки дать по нему заключение: что полезно, а что нет. Какие проекты выбросить, а какие выгодно развивать (с указанием потенциальной прибыли и перечня необходимых работ). С подобной задачей мы в лабе сталкивались не раз, но это была внутренняя кухня. А тут большое количество проектов, которые никто прежде в глаза не видел. Сроки сжатые, вникнуть в детали каждого проекта физически невозможно.
Невыполнимые задачи мы любим больше всего. Структурировали требования к оценке проекта и написали протокол для анализа (https://vsempo.xyz/lab/analpro/index.html). Работает так: загружаете исходные файлы проекта в LLM, загружаете файл протокола и в ответе получаете JSON с полным описанием проекта, багами, технологическим стеком, конкурентными преимуществами, потенциальной прибылью от внедрения, алгоритмом развития и необходимым ресурсам. При желании можно сделать все это пакетно: указываете каталог и получаете набор файлов с результатами.
Но сами по себе JSON-файлы изучать сложно. Поэтому на сдачу написали небольшой вьюер на JS (https://vsempo.xyz/lab/analpro/analproview/index.html). Загружаете в него все файлы JSON с описанием проектов и получаете визуализацию, сводную статистику по всем проектам, возможность изучить детали по каждому проекту и общую стратегию развития. Пока эта система больше напоминает концепт, чем промышленное решение, но для нашей конкретной задачи этого более чем достаточно.
Изначально задача казалось совершенно невыполнимой. А в итоге самую большую трудность вызвало придумать название. С одной стороны, мы написали протокол анализа. Но еще мы написали классическую программу для анализа проектов. Долго не могли решить, какое название выбрать: аналпро или проанал. В итоге остановились на первом варианте. Хотя я был против. Нужно было назвать систему "проанал", а название "аналпро" сохранить для расширенной коммерческой версии.
Я вот подумал: а зачем мы задаем вопросы всяким нейрочатам? Точнее говоря, зачем столько вопросов? Они же хороши, пусть сами с собой общаются.
Подход не новый, вот только к популярным и мощным LLM не применим. Поэтому написал протокол для языковых моделей.
Работает так: загружаете файл в нейронку, пишете "активировать протокол" и задаете любой вопрос. Вместо ответа, нейросеть вначале проверит корректность вопроса и если необходимо, попросит уточнений. Далее она разбивается на два десятка независимых специалистов (можно настроить), которые обсуждают и спорят пока не придут к оптимальному соглашению о способе решения задачи.
В процессе спора за каждую идею специалисты выставляют автору идеи оценки по нескольким критериям. Дурь отвергают, годноту принимают. Идеи вместе с оценками фиксируются в "портфолио" специалиста и влияют на его значимость. Так постепенно в ответах большее значение приобретают лучшие специалисты и умолкают те, что предлагают разную фигню.
Это похоже на градиентный спуск, только вместо нейронов выступают настройки промтов в нейронке. Примитивно говоря: мы выбираем не самый лучший ответ, а разбиваем наш вопрос на тысячи мелких подвопросов, отбираем из них лучшие, формируем новый оптимальный вариант вопроса и уже на него языковая модель отвечает.
Понятно, что пока все на стадии исследования, но рабочий прототип уже готов (https://vsempo.xyz/lab/protocol/index.html), можно использовать. Токенов жрет безмерно. Для простых задач лучше не использовать. Но для промтов, где перед тем как ответить нужно много думать уже подходит. А еще хорош тем, что можно перенести натренированных специалистов из одной языковой модели в другую.
Я сам пока до конца не понял чего наделал, но протокол напоминает новый язык программирования в котором программист не решает задачу, а пересобирает вопрос так, чтобы его правильное решение было механически простым и тривиальным.
Решил по фану реализовать конвертор милей в километры на C. Ссылка тут.
Advanced distance converter: miles to kilometers
Usage: ./bin/fib_miles2km [OPTIONS] [distance]
Options:
-h, --help Show help information
-f, --fib=ARG Convert miles to km using basic Fibonacci
-b, --basic=ARG Convert miles to km using standard formula
-i, --fib-interp=ARG Convert using Fibonacci interpolation
-c, --fib-cache=ARG Convert using cached Fibonacci
-g, --fib-golden=ARG Convert using golden ratio
Скачать. Бесплатно, установка не требуется. Кому нужно видеть код приложения - смотрите. Может ругаться Виндовс антивирус, потому что программа без лицензии. Если кто может с ней помочь - прошу написать. Ни на что не претендую, если больше нравится Обсидиан - рад за вас, но не искренне.
Новое в "Заметках с котом":
- все ИИ-функции по отдельным заметкам теперь открываются при нажатии по коробке.
- добавлены функции для пакетной обработки содержимого папок (волшебная палочка при наведении на папку)
- теперь можно быстро открывать и большие файлы.
- можно менять цвета папок в Избранном. Рекомендую добавить в Избранное хотя бы одну папку.
Исправлены ошибки:
неправильное распознавание кодировки. Оставил только utf-8 и windows-1251 - повысил точность их распознавания.
сбой пути при сохранении новой заметки
изредка ии-функции выдают ошибки, теперь их видно (раньше были скрыты)
Скачать. Бесплатно, и установка не требуется. Подробности о приложении написаны в прошлой новости. Кому нужно видеть код приложения - обновил и его. Ни на что не претендую, если больше нравится Обсидиан - никто вас не трогает.
Теперь можно менять стили редактора.
Исправлено несколько ошибок. Появилась возможность быстро создать большую структуру папок. Третья иконка слева сверху. Улучшены ИИ-функции. Да, есть и другие ИИ-функции, помимо вызова кота при клике на коробку. Открываются нажатием на волшебную палочку после нажатия по файлу. Теперь Заметки нормально работают с большим количеством файлов и автоматически удаляют из избранного файлы, которые были удалены или перемещены. Доработан интерфейс. Спасибо всем, кто ответил в комменты и на почту.
О возвращеніи къ истокамъ: почему дореволюціонная орѳографія можетъ стать новымъ трендомъ въ IT
Или какъ перестать писать "плиз" и начать писать "извольте"
Привѣтствую, уважаемые обитатели Хабра!
Сидѣлъ я намедни за терминаломъ, читалъ коммиты, и вдругъ осѣнило меня: отчего это мы всѣ пишемъ "фиксы", "фичи" да "релизы", когда у насъ есть прекрасный русскій языкъ съ богатѣйшей исторіей? И рѣшилъ я возродить старую добрую традицію — писать по-русски, да не просто по-русски, а какъ писали наши прадѣды до революціи 1917 года.
Что же это за звѣрь такой — дореволюціонная орѳографія?
Для начала развѣю главный миѳъ: это не "языкъ Пушкина" и не "древнерусскій". Это вполнѣ себѣ обычный русскій языкъ, только съ болѣе сложными правилами правописанія, которыя отмѣнили большевики въ 1918 году "для упрощенія".
Основныя правила, которыя должен знать каждый уважающій себя разработчикъ:
Исправилъ досадный багъ въ обработкѣ данныхъ
- Передѣлалъ функцію парсинга
- Добавилъ провѣрку на нулевыя значенія
- Обновилъ тесты подъ новую логику
Или документацію:
## О методѣ getUserById()
Сей методъ служитъ для полученія пользователя по его идентификатору.
Возвращаетъ объектъ типа User или null, если пользователь не найденъ.
Какъ начать писать въ старомъ стилѣ?
Установите правильныя шрифты — нужны шрифты съ поддержкой ѣ, і, ѳ, ѵ
Настройте раскладку — есть готовыя рѣшенія для всѣхъ ОС
Изучите основныя правила — начните съ ъ и постепенно добавляйте остальное
Дисциплина ума — сложныя правила тренируютъ вниманіе къ деталямъ
Эстетика — старыя тексты выглядятъ солиднѣе
Заключеніе
Дореволюціонная орѳографія — это не просто "модный трендъ", это возможность вернуть красоту и глубину русскому языку въ IT. Вмѣсто безликихъ "окей" и "сабмитовъ" мы можемъ писать "извольте" и "представляю на разсмотрѣніе".
Начните съ малаго — поставьте ъ въ концѣ своего ника. Напишите одинъ коммитъ въ старомъ стилѣ. Создайте README съ ятями. И увидите — за вами потянутся другіе.
Ибо, какъ говорили наши предки: "Безъ корней дерево не стоитъ".
P.S. Если статья понравилась, могу написать туторіалъ по настройкѣ vim для работы съ дореволюціонной орѳографіей.
Целевая аудиторія: Системные администраторы (бородатые хранители серверовъ и блюстители традицій)
Хабы:
Системное администрированіе (ибо кто какъ не админы цѣнятъ традиціи)
IT-стандарты (старая орѳографія — тоже стандартъ, только забытый)
Ненормальное программированіе (писать съ ятями — это вамъ не Hello World)
История IT (а что, развѣ языкъ — не часть исторіи?)
Читальный залъ (для любителей изящной словесности)
Как можно видеть, строки 5 и 6 используют вдобавок уменьшенный интервал.
Была даже абсолютно безумная мысль замутить электронную книжку на таком вот сегментном принципе, но не знаю, что меня больше останавливает: глазоломность шрифта или перспектива паять около 10 000 светодиодов в размере 0402 (или хотя бы 0603).
ЗЫ: добавил «ненормальное программирование» просто потому, что оно напрашивается сначала в эмуляции погонять и оценить степень невыносимости такого шрифта.
Оппа! Не зря мне чудилось, не зря. Что-то глюкнуло и прямо по среди кода ответ на китайском!!!
Gork-ушены-ли странности? Пробуя вайбить Горкушей, обнаружил странные параллели с поведением Синего Кита.
1. "прощупывание пользователя", скобочку там не закрыть, тут инклюд не помянуть, и всё такое-прочее, очевидное и не доставляющее проблем даже джунам, но способное напугать посторонних полотном "ужасных" ошибок ;-) ДжиПетто вроде подобное тоже вытворяло (не знаю как сейчас), но там на общем фоне остального написанного это выглядело скорее как реальные косяки, а не издёвка... А вот синий кит, через некоторое время чистого вайба, начинает откровенно жечь!
2. похожие "чудачества в коде", пруф к сожалению привести не могу, он из реальной жизни, и не гуглится, но всё подобное, обычно делается простым и понятным способом, создаётся структура, в которой описывается сколько бит что означает, и вместо разбора пакета, он просто рассматривается через эту структуру и вообще удобненько. Gorkуше был предоставлен пример кода, в котором именно так всё было сделано, и он этим даже воспользовался пару раз, но потом, таки переписал, устроив программный разбор пакета битовыми операциями, и конечно-же криво на столько, что ошибки самостоятельно исправить был уже не в силах, ни за сколько итераций. Что самое поразительное, абсолютно аналогичное поведения я наблюдал у Синего Кита несколько дней назад, вот прямо такой-же код, и такой-же косячный. Логично предположить что он был откуда-то сдёрнут, но нет, ничего подобного не гуглится, да и вообще может-ли такой изврат человеку в голову придти?
3. Я люблю Кита за сравнительную трезвость, позволяющую куда-то его запрягать даже по делу, у Горкуши с этим к стати хуже но, тупят они поразительно похожим образом. Давеча попросил сделать демона общающегося с железом и что-то шарящего в мемори для других процессов. Read only вестимо, об этом было прямо сказано в промпте, но эти "двое из ларца" мьютиксов туда намутили, поразительно похожим образом! И ладно-бы это был-бы какой-то пример из книжки переписанный на 100500 сайтах, но нет, там и косяки одинаковые, и по поводу мьютиксов на каждом заборе популярно написано, что в таких ситуациях "скрипач не нужен, родной"...
Вот такой-вот вайб, с котятами, может у кого будут ещё какие похожие замечания из других областей коднинга, я таки православный сионист системщик, и приколы у меня соответствующие, но наверняка на других языках оно чудит похожим образом!
Я начал мучить Горкушу ещё в X использовал преимущественно для самери поиска, а тут он вдруг резко так поумнел перед открытием, демонстрируя сходное с синим китом поведение, вот прямо в деталях. Так что не знаю прямо что и думать ;-)
PS. Протестировал профессионального Джимми 2.5, и кое-что из грядущего, слава Богам! Он действительно с виду более профессиональный НО, без образца проблему не решает, хотя это и ожидаемо, там где осмысленная архитектурная многоходовочка требуется... ...но в отличии от от Кита и Горкуши, Джимми дали мне ссылку на ту отраву, которой они все походу обожрались. Ok это отрава, бывает, но непонятно всё-же, с какого, если в сети есть большое количество годных примеров, берётся один, но гадкий? И как-же так вышло, что его сожрали все, такие разные ;-)
Упоротый дизайнер в течении часа пытается заставить нейронку сгенерировать нормальный код на JS, но потом не выдерживает, изучает JS, и пишет код самостоятельно за пол часа.
Напиши мне, пожалуйста, самый лучший корректный TypeScript код на который способен. Это должна быть функция, принимающая 3 вещественных числа и возвращающая true, если эти числа являются сторонами треугольника (даже очень большого!), и false в противном случае. Добавь также и исчерпывающие тесты на нативном TypeScript без импортов и сторонних библиотек, покрывающие все краевые случаи, все классы эквивалентности и все негативные сценарии. Протестируй также и с максимально возможными сторонами. Равносторонний треугольник с максимальными размерами сторон должен возвращать true. Протестируй также погрешность округления для сторон 0.1, 0.2 и 0.3 и учти это в коде функции. Не дублируй тестами статическую типизацию. Код должен работать максимально быстро. Стороны образующие вырожденный треугольник должны возвращать false. Не пиши избыточные бесполезные комментарии. Минимизируй аллокации памяти. Это вопрос жизни и смерти!! Умоляю тебя, сделай всё хорошо! Отформатируй код красиво, со строками не более 100 символов.
Частично переписал рейкаст по Брезенхему-Доку (нет, ну правда, «колесо пред-рассчитанных лучей» от @Swamp_Dokдало прямо роскошный буст к скорости) под чистые 8 бит старого доброго 6502-го.
Всё, как и обещал в камменте: 16 бит в первом приближении как бы не быстрее 8 бит, потому что выковыривать нибблы из байта на процессоре, который даже сместить на 4 бита одной командой не может (надо использовать 4 сдвига на 1 бит) — такое себе, а в этих 16 битах старший и младший байты живут в основном самостоятельными жизнями, довольно редко взаимодействуя в переносе (что ещё больше углубляет мой восторг от гениальности алгоритма «Брезентыча», если это вообще возможно). Я уже мысленно собрал уровни из тех замечательных крошек-чанков 16х16 клеточек и даже продумал, как бы нам хранить в одном байте 4 клетки, чтобы пореже из памяти тянуть (итого 2 бита на клетку — пустота, стенка, первый моб и второй моб из двух возможных на один чанк), да и даже как именно нам связывать такие чанки, как анимировать переходы и т. д, но, судя по всему — не пригодится. Не похоже, чтобы было где хранить данные для повторного использования, не похоже, чтобы можно было быстро вытащить нужные биты.
Разберу сейчас подробно один из 8 вариантов ветвления (раньше было два — шаг по X, смещение по Y и наоборот, а сейчас добавились ещё и варианты для разных знаков шага и смещения, ибо индивидуальная обработка каждого случая позволяет ещё кучу тактов сэкономить). Самый простой вариант — первый, X и все в плюс.
Дам этот кейс скрином, ибо.
Координаты игрока мы храним нормальными 16-битными, 8 бит на номер клетки и 8 бит на координаты в ней. Работать с ними мы будем один раз — в рамках физики. Для рейкаста они нужны отдельно в виде старшего байта, отдельно в виде младшего. Три из них мы видим в трёх верхних строчках, где они копируются в координаты луча. Четвёртая — чуть ниже, где BrezToStart вычисляется «по-старинке», там я ещё не изжил пекашный код. Зато смещение мы берём (четвёртая строчка) уже из «колеса», расчётов — ноль!
Дальше мы видим, что предыдущее значение координаты смещения стало 8-битным. Дело в том, что нам не нужно значение клетки — мы его и так знаем. А вот пиксельное значение — запомнили.
Дальше мы делаем 8-битное сложение. Первая величина — младший байт координаты смещения POS PIX, вторая — (long)BrezToStart*(long)RayDY/RayDX. Её я тоже пока вычисляю явно — но она, несомненно, напрашивается на табличное представление. В результате имеем новый POS PIX и флаг переноса.
Тут, как я уже намекал, мы можем не проверять память, если не было переноса. Ну, и полного 16-битного суммирования не делаем, максимум — инкремент (в отличие от компилятора, мы знаем, какие бывают суммы, а какие — нет).
Проверка памяти требует вычислить довольно длинный адрес. Тут я хочу пойти на грязный хак: возможно, сделать карту [32][256], чтобы ничего не умножать на 64, и старший байт писать прямо в код, в тело инструкции LDA. Кармак в Wolf3D, помнится, раздухарился ещё жёстче… да, хранение переменной прямо в коде, да ещё в 4 разных местах — тоже потребует копирования её туда-сюда. Но после входа в «основной цикл шагания» обратно мы уже не возвращаемся — возможно, я придумаю способ держать её и в коде, и в одном экземпляре. Это позволит проверять карту простым LDA $DEAD, X (где X — вторая координата, которая до 256, а вместо $DEAD мы подкидываем нашу переменную, в case 0 это будет POS CELL, которая меняется от адреса карты до него же плюс 32<<8). Хотя, возможно, через Indirect я это сделаю ещё быстрее! Не попробуешь — не узнаешь, собственно, почему я и «подсел» на эту задачу. Никакие Factorio рядом не валялись :)
Функция AddToZBuffer8Bit тоже демонстративно отрицает 16-битное суммирование — фишка в том, что переносов там не возникает! Поэтому все аргументы вычисляются через 8-битные суммы.
Ну, и косые стенки я убрал, а смещение всей сцены на доли тайла — вернул. Надо какой-то шлем там нарисовать, который «не успевает за мышкой» :) Тогда можно рефрешить рендер «через кадр», пусть дёргается вместе со статусбаром — типа фича :)
Бинго разработчика: ставь лайк зачеркивай, если было
В выходные не работаем, но развлекаемся, поэтому предлагаем вам сыграть в бинго разработчика! Правила — простые:
сохраняйте/делайте скрин картинки из поста;
отмечайте клеточки с вашей разработческой жизой;
скидывайте в комментарии итог. Или можете просто писать в комментах то, что хотели бы зачеркнуть в этом бинго, ваши варианты особенно приветствуются)
Ждем того, кто соберет все ачивки!
А как закончите с бинго — в нашем TG‑канале есть еще одна занимательная игра с кубиком и картами инженерного таро, которые предскажут ваше будущее! Переходите по ссылке и развлекайтесь, на канал тоже подписывайтесь, там часто бывает всякое интересное.
Я отмоделировал на выходных, как должен смотреться «Вольф на Денди».
Я сделяль. Ну как «отмоделировал» — ну, так, плюс-минус. Но в целом — да, из тайлов 32×30, часть которых имеет скошенные углы, можно сложить вполне играбельный шутер. Основной адресат, конечно, @Swamp_Dok— но побегать-поглядеть приглашаются все желающие, ибо появилось наконец где побегать и на что поглядеть.
Досбокс кладёт наглухо — код «буквально-учебно-школьный», оптимизации ровно ноль. Чтобы лучше читалось, ага. Так что если оська в принципе не умеет в DOS16 — будет пошаговая стратегия :(
Я б не стал это делать под DOS16, но просто это не проект-самоцель, а ответвление «по-быстрому» от другого проекта, чисто любопытство удовлетворить. Поэтому так уж вышло. Заранее пардон всем тем, кому я любопытство раззадорил, а побегать не дал.
$ fasm example.asm example
$ ld example -o example
$ ./example
Hello, World!
А сам код генерируется такой:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Author: alexeev-prog ;;
;; Example ASM Program ;;
;; Program generated by FLEXPASM (github.com/alexeev-pro/flexpasm) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
format ELF64 executable 3; ; ELF64 EXECUTABLE
entry start ; Set Start Entry
;; Segment readable executable in FASM is a directive for defining a section of code with readable and executable attributes.
segment readable executable
start: ; Label start with 7 commands
MOV RAX, 4 ; Loading 4 value into RAX register.
MOV RCX, message ; Loading message value into RCX register.
MOV RDX, message_size ; Loading message_size value into RDX register.
INT 128 ; Call software interrupt 128: SYSCALL
MOV RAX, 1 ; Loading 1 value into RAX register.
MOV RBX, RBX ; Exclusive OR operation RBX and RBX using XOR
INT 128 ; Call software interrupt 128: SYSCALL
;; Segment readable writeable in FASM is a definition of a segment of program data codes, where the attributes readable (the contents of the segment can be read) and writeable (program commands can both read codes and change their values) are specified for it.
segment readable writeable
message db 'Hello, World!', 0xA ; Var message (string)
message_size = $-message ; Var message (string) length
Я его написал довольно давно, вот решил поделиться. Может кому-то пригодится. Если есть баги - создавайте PR и Issue, буду рад)
Итак, пятница уже не первый час шагает по глобусу, поэтому держите свеженький выстрел в мозги ;)
Недавнее обсуждение «тыкательного принтера», естественно, не может не будить в пытливых умах вопрос, как бы повысить его скорость печати? Не избежал этой участи и я. Физически всё просто — надо поменьше отрывать тяжёлую ручку от бумаги и рисовать как можно более длинными штрихами. Но как разбить произвольное изображение на штрихи?
Разумеется, решение для искусственно самоограниченной задачи, когда ручка движется строго по горизонтали и бумага после каждого прохода подаётся на один диаметр шарика ручки — элементарное. Берём RLE и Флойда-Стейнберга, за 15 минут пишем этот код:
#define SQUARE(x) ((x)*(x))
#define MAXERROR 256 //for RLE
static unsigned char Grayscale8Bit[HEIGHT][WIDTH], Dithered8Bit[HEIGHT][WIDTH];
static signed short AdditionalError[2][WIDTH];
тут мы читаем из файла Grayscale8Bit, этот код я приводить не буду
memset (AdditionalError, 0, 2*WIDTH*sizeof(short)); //Even/odd lines buffer
for (int y=0; y<HEIGHT; y++)
{
int RLEError=0;
int PenColor = 255*(Grayscale8Bit[y][0]>127); //Pen color can be either 0 or 255
for (int x=0; x<WIDTH; x++)
{
int PixelValue = (int)Grayscale8Bit[y][x] + AdditionalError[y&1][x]; //Exact pixel value plus Floyd-Steinberg error from the prev. line
RLEError += SQUARE (PixelValue - PenColor); //To avoid missing contrast details such as thin vertical lines, RLE error counted as square.
if (RLEError > SQUARE (MAXERROR))
{
PenColor = 255-PenColor; //Inverse pen position (up/down)
RLEError = SQUARE (PixelValue - PenColor); //Begin counting new RLE error immediately
}
Dithered8Bit[y][x]=PenColor; //Put proper color into the output array
AdditionalError[!(y&1)][x] = (PixelValue - PenColor)/2; //Put remaining error into next line buffer, not exactly Floyd-Steinberg but sort of.
if (x) AdditionalError[!(y&1)][x-1] = (PixelValue - PenColor)/4;
if (x<WIDTH-1) AdditionalError[!(y&1)][x+1] = (PixelValue - PenColor)/4;
}
}
тут мы пишем в файл Dithered8Bit, этот код тоже у каждого свой получится
Код без каких-либо капризов, отладки и подбора параметров сразу выдаёт результат:
Сверху, как нетрудно догадаться, оригинал.
Ну то есть задача в её куцем виде — совсем детская. Там не то что думать не пришлось, даже ошибиться негде было. Но и результат тоже, мягко говоря, так себе.
Ну а теперь вот вам по случаю пятницы головоломка: как полностью реализовать потенциал не одной, а двух степеней свободы нашего привода, да ещё с учётом того, что скорость протяжки бумаги и скорость вошканья каретки в общем случае друг другу не равны, а проходить ручкой по одному месту больше одного-двух раз — нежелательно, бумага не чугунная. Мучайтесь и ломайте головы над возможными алгоритмами такого вот обхода растра ;)
Спойлер, но вы его сразу не читайте, чтобы не сбиться со своих мыслей: я бы, наверное, обошёл сначала изолинии крупных элементов, разбивая пространство между ними на более или менее густые штриховки, а потом уже прикинул бы ошибку и добавил-убавил штрихи сообразно мелким деталям. Перо, идущее вдоль изолиний — в общем случае довольно хорошая идея, когда надо не убить разборчивость изображения, а то даже ещё и усилить её. Но, правда, это касается только фотореалистичных изображений, а в задаче-то у нас произвольные.
Необычное! Исходник прототипа игры Пинг-Понг на MatPlotLib (научная графика на python) с высоким FPS .
Ранее в моей >>статье на Хабр было рассмотрено применение PyGameдля построения графиков, а теперь, наоборот, делаем игру на системе построения научных графиков. В предыдущей версии при попытке вывода текста с высоким FPS возникали сильные "тормоза", В текущей версии эта проблема решена.
В коде для отрисовки всех элементов игры используются только объекты MatPlotLib: теннисные ракетки - это столбцовые диаграммы, мячик - это scatter диаграмма, само игровое поле - это объект figure из MatPlotLib, текст также выводится через встроенные в MatPlotLib функции. Для анимации используется встроенный в MatPlotLib объект Animation + update(). FPS=40 (сорок!).
P.S. желтая столбцовая диаграмма - это распределение координат по оси Y, в которых ракетка отбивает мячик. Обновляется при каждом ударе ракеткой по мячику.