программирование
Взрыв компьютера
Иногда в фильмах показывают ситуацию, когда хакер устраивает «перегрузку систем» и компьютер взрывается.
В детстве я над таким смеялся вслух «хаха, какие дурачки, софт не может влиять на железо таким образом», но компьютеры становились всё более сложными, и я стал помалкивать — «не всё так однозначно, мало ли что на свете бывает».
Теперь уже уязвимости, вызывающие физическое повреждение компьютеров не редкость, недавно нашли ещё одну.
Эта новая атака называется PMFault, суть в том, что через специальную шину управления питанием процессора можно увеличить напряжение до такого, что центральный процессор сгорает. Доступ к этой шине, конечно, просто так не получить, но ребята нашли способ.
Си++: форматированный вывод (продолжение)
Очень раздражает, что вместо того, чтобы писать прикладной код, я вынужден бороться с компиляторами, да не с одним, а с тремя, под разные операционные системы. Я как будто пытаюсь писать не на одном языке, а сразу на трёх, похожих, но не полностью.
Мои приключения c форматированным выводом в Си++ до сих пор не закончились, оказалось, что последний вариант обёртки над std::snprintf в одном из компиляторов не собирается с одним из видов аргументов, пришлось его обернуть в std::cref.
Умные люди в комментариях советуют перейти на внешнюю библиотеку fmt и я уже сделал подход к снаряду, но под mingw у меня сборка пока не заработала. Завтра попытаюсь ещё раз — хочу подключить библиотеку подмодулем к своему репозиторию встроить компиляцию в процесс сборки своего проекта. Мне кажется, что я делаю всё правильно, а вот mingw так не кажется, попытаюсь завтра побороть.
Си++: форматированный вывод
Си++, похоже, язык для воспитания героев — создаём трудности на ровном месте и начинаем их преодолевать. В моём случае началось всё с разумной, как мне до сих пор кажется, идеи — заменить сишные безобразные строки char * на прекрасный класс std::string из Си++.
В процессе в комментариях мне написали, что вместо него надо использовать std::string_view, чтобы избежать копирования. Так я узнал, что за строки в Си++ отвечают не один класс, а четыре — есть ещё базовые классы, с приставкой base.
Заодно пришлось перейти на стандарт 2017 года, так как до строк с семантикой перемещения додумались только недавно.
Всё шло хорошо, пока я не дошёл до форматирования текста. В Си этим занимается функция printf, а Си++ есть метод std::format, но вот беда — он появился только в стандарте 2020 года и его ещё нет ни в одном из компиляторов, которые есть у меня под рукой.
Пошёл на поклон к ЧатГПТ, который выдал мне такую обёртку:
template<typename... Args>
std::string string_format(const std::string format, Args... args) {
int size_s = std::snprintf(nullptr, 0, format.data(), args ...) + 1; // Extra space for '\0'
if (size_s <= 0) {
throw std::runtime_error("Error during formatting.");
}
auto size = static_cast<size_t>( size_s );
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.data(), args ...);
return {buf.get(), buf.get() + size - 1}; // We don't want the '\0' inside
}Это так называемый «шаблонная функция». Насколько я пока понимаю принцип, компилятор смотрит с каким типами параметров вызывается эта функция, и делает их столько штук, сколько есть комбинаций вызовов.
Так как файлы с кодом друг друга не видят, чтобы шаблонная функция «знала» с какими параметрами её вызывают, её надо помещать в заголовочный файл (🤦🏻♂️), либо указать в коде все необходимые прототипы вручную (🤦🏻♂️🤦🏻♂️🤦🏻♂️).
И хотя я осознаю, что со своим уставом в чужой монастырь не лезут, первое мне кажется противоестественным, а второе — неудобным.
На этом этапе я выяснил, что мой компилятор под «Виндоуз» ничего не знает про std::string_view, а компилятор под «Мак» не может скомпилировать код выше вот с такой прекрасной ошибкой:
comm/../utils.hpp:30:60: error: cannot pass object of non-trivial type 'std::string' through variadic function; call will abort at runtime [-Wnon-pod-varargs]
int size_s = std::snprintf(nullptr, 0, format.c_str(), std::forward<Args>(args)...) + 1;Мне бы хотелось, чтобы код собирался под оба компилятора, поэтому я снова пошёл к ЧатГПТ и вернулся вот с таким прекрасным кодом:
template<typename... Args>
std::string string_format(const std::string& format, Args&&... args) {
auto size_s = std::snprintf(nullptr, 0, format.c_str(),
std::decay_t<Args>(args).c_str()...) + 1; // Extra space for '\0'
if (size_s <= 0) {
throw std::runtime_error("Error during formatting.");
}
auto size = static_cast<size_t>( size_s );
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(),
std::decay_t<Args>(args).c_str()...);
return {buf.get(), buf.get() + size - 1}; // We don't want the '\0' inside
}Тут происходит
comm/../utils.hpp:32:58: error: request for member 'c_str' in 'args#0', which is of non-class type 'std::decay_t<char*>' {aka 'char*'}
std::decay_t<Args>(args).c_str()...) + 1;К счастью, решилось это просто — надо было всего лишь убрать .c_str().
По пути там пришлось решить ещё пару забавных мелочей, но они недостаточно отложились в памяти, чтобы я их мог воспроизвести без опасения
Теперь я понимаю за что программистам на Си++ платят такие деньги — это доплата за нервную работу.
Гиперссылки в терминале
Решил сегодня скоротать вечерок со статическим анализатором в gcc — уже несколько раз натыкался на статьи о нём, пока читал про Си++. Всё откладывал знакомство и вот решил посмотреть.
Попробовал проанализировать им исходники
Пока читал, что мне вывелось, заметил, что в терминале работают гиперссылки. Не знал, что такая возможность есть. Про графику слышал, а про гиперссылки — не припомню,
Выше на скриншоте их хорошо видно — они подчёркнуты прерывистой линией. В данном случае по ссылкам можно перейти на документацию о соответствующей ошибке, очень удобно.
Штука достаточно свежая и поддерживают её не все терминалы. У себя попробовать можно вот так:
printf '\e]8;;https://bolknote.ru\e\\Сайт Степанищева Евгения\e]8;;\e\\\n'
Перекодировка вручную
Меня удивляет, что многие программисты сейчас не умеют перекодировать строку в ошибочной кодировке вручную, даже если знают как она получилась.
С различными кодировками сейчас мало где можно столкнуться, тем не менее мне неясно в чём сложность задачи. Вроде даже ничего знать не надо.
Например, я вижу вот такую строку: Требуется авторизация. Она так выглядит, потому что она была в кодировке
iconv -f utf8 -t cp1251 <<<"Требуется авторизация"
Требуется авторизацияДиапазоны в Си
Почитал тут на досуге какие ещё расширения есть у «ГНУ Си». Хотя я их и не использую, но знать не помешает. Оказалось, что там много такого, что лично мне хотелось бы видеть в языке. Например, хорошая штука — диапазоны. Выглядят они так:
const char *whatisit(char code) {
switch (code) {
case 'a' ... 'z':
return "lowercase";
case 'A' ... 'Z':
return "capital";
case '0' ... '9':
return "digit";
case 0 ... 31:
return "special";
default:
return "unknown";
}
}Но написал я про них не только потому что мне нравится конструкция, но и потому, что оказалось, что меня неконсистентно бесит пробел слева от многоточия, который надо обязательно поставить, если диапазон начинается в цифры.
Если его не будет, компилятор подумает, что перед ним число с плавающей точкой и заругается на ошибку синтаксиса.
Непонятно почему меня это так раздражает. Ну пробел, это же не первый синтаксический костыль, который я вижу,
Ненормальный цикл на «баше»
Сегодня в чате бывших коллег на фоне вопросов по одной из вакансий случилось обсуждение на тему что делать, если на собеседовании спросили как написать на «баше» цикл от одного до десяти, а ты не помнишь синтаксис for и команду seq тоже не знаешь.
Предположим ты честно говоришь, что не помнишь, а тебя просят
Мой вариант:
«Гопник-2»: переход на нормальный Си++11
Как я писал прежде, когда я адаптировал
Тут у нас в Татарстане выдались длинные выходные, — в праздник
Вызов _fileno у меня использовался для получения номера дескриптора входного потока, так что тут всё решилось просто, — для этого есть специальная константа STDIN_FILENO. А вот с strdup пришлось повозиться.
Сначала я, разумеется, сделал свою реализацию — там
Первым делом пошёл на поклон к «ЧатГПТ», спросил совета. Советы не заработали, но дали направление мысли.
Первая мысль была забахать макрос, который заменял бы собой strdup в проблемном компиляторе, а остальные не трогал. Тут надо сказать, что под «Виндой» далеко не во всех наборах утилит компиляторы не содержат функцию strdup, например в msys2 она есть.
Мне хотелось
Пришлось пошарить по исходникам. Оказалось, есть ряд макросов, которые присутствуют в msys2, но отсутствуют в msys. Из них я выбрал __MINGW64_VERSION_MAJOR:
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
#define strdup(s) (__extension__({ const char* __s = (s); size_t __l = strlen(__s) + 1; char* __d = (char*)malloc(__l);\
__d ? (char*)memcpy(__d, __s, __l) : nullptr; }))
#endifТут используется расширение, которое позволяет писать многострочные макросы. Хотя этот вариант мне и нравится, но опять используется не нормальный Си++, а расширение компилятора.
Вторая мысль, на которую меня навела нейросеть — использовать слабые символы языка Си++. Это совершенно новая для меня штука, поэтому нужно небольшое пояснение. Прежде всего для меня же, чтобы я в будущем не забыл что это вообще такое.
Если упрощать, то символы — это экспортируемые именованные сущности. Те, что
Слабые символы используются для библиотек, чтобы пользователь мог заменить некоторые сущности, если они его не устраивают.
Если перевернуть ситуацию, и считать мой код библиотекой, а тот код, где должна быть определена strdup основным, то всё должно получиться.
«ЧатГПТ», к сожалению, помочь не смог, — сначала подсовывал нерабочий код, а потом зациклился в своих советах. Поэтому пришлось разбираться самостоятельно.
Всё оказалось не так уж и сложно. Сначала в заголовочном файле определяем прототип функции:
#ifdef __MINGW32__
char *strdup(const char *s1);
#endifЯ его спрятал под условие, чтобы мой редактор перестал ругаться на избыточное определение, но всё работает и без этого.
Потом в самом коде создаём слабый символ для strdup:
#ifdef __MINGW32__
#pragma weak my_strdup
char *strdup(const char *src) {
size_t len = strlen(src) + 1;
char *s = (char *) malloc(len);
return s == nullptr ? nullptr : (char *) memcpy(s, src, len);
}
#endifСлабый символ я определяю через директиву #pragma weak, у которой надо указать имя слабого символа, причём оно должно отличаться от имени функции. Я пока не до конца понял зачем это нужно делать, но без этого не работает.
Кстати, #pragma weak тоже расширение, но оно легко заменяется на атрибут weak, надо только будет изучить его синтаксис, я не успел этого сделать.
На имя my_strdup у меня ругается редактор — говорит, что слабый символ нигде не определён, поэтому я опять же засунул всё это в условную компиляцию.
В выходные, как всегда, есть куча дел, кроме хобби, поэтому до конца я разобраться не успел и буду рад, если
Дополнено: в итоге все ситуации я побороть не смог и откатился на самое простое решение со своей собственной реализацией strdup.
Beta than nothing
В результате изделие, чей сектор больше всех, развивается быстрее и постепенно уходит в отрыв. Эта картина совершенно не зависит от качества товаров, а исключительно от начального распределения. Вот и получается что стандартом
де-факто становится наихудший товар из работоспособных — изделие наименее добросовестных разработчиков, стремящихся побыстрее застолбить место, первыми выбросив на рынок плохо продуманную «сырую» дрянь.
— из приложения «Знакомимся с кодировками символов» к книге «Фокал снаружи и изнутри».
Принцип известен в программировании как «Beta than nothing».
К сожалению, именно так это и работает, — пока ребята из Вилларибо вылизывают свой код и постепенно разоряются, жители Виллабаджо уже наговнякали прототип, продали его за денежки и доводят его до ума на живом заказчике, который их проклинает, но деться уже никуда не может — деньги на внедрение уже потрачены.
Ничего не поделаешь, это стороны одной монеты — мы любим, чтобы всё появлялось быстро, но при этом закономерно жалуемся на сопутствующие баги, хотя они суть последствия аврала и спешки, в которых сейчас принято разрабатывать программное обеспечение.
На этом принципе взлетел
Любопытно что тут поменяет искусственный интеллект.
В принципе, переводить мысли в понятный машине код будет проще, — не надо учить никакие машинные языки, оно там
Классное время настанет для креативных людей!
auto в Си и Си++
Несмотря на неприязнь к Си++,
Одна из таких мелочей — ключевое слово auto. Оно есть и в Си, но значение у него совершенно другое, в современном коде, как будто бы, его использовать негде.
В Си++ оно означает автоматический вывод типа, вместо его явного указания. Клёвая штука, позволяет делать например такое (пример из кода
auto old_mode = set_tty_special_mode();
// тут какой-то код
restore_tty_mode(old_mode);Фишка тут в том, что у set_tty_special_mode и restore_tty_mode две реализации — под «Виндоуз» своя. Эти две реализации работают с разными типами, под «Виндоуз» там DWORD, в остальных случаях — структура termios.
В Си было бы
#ifdef __MINGW32__
#typedef DWORD terminal_mode
#else
#typedef struct termios terminal_mode
#endif
terminal_mode old_mode = set_tty_special_mode();
// тут какой-то код
restore_tty_mode(old_mode);А в Си++ можно просто поставить auto и обойтись без шаблонного кода. Очень хочется иметь такую клёвую примочку и в Си.
«Гопник-2»: фан-клуб
Тем временем, у игры
Когда будет готово, надо будет это должным образом оформить, снабдить скриншотами и перенести в вики проекта.
В принципе уже почти всё готово,
Надо бы поторопиться, а то бесплатная лицензия на среду разработки кончится в начале мая, не так много времени осталось, дальше разрабатывать будет сложнее.
«Гопник-2»: разные mingw
Надо
Сильнее всего меня удивило в процессе, что поведение mingw, — инструментария для компиляции приложений под «Виндоуз», различается под разными операционными системами.
Поскольку мне не хотелось заморачиваться с установкой софта на виртуальную машину, я развернул mingw у себя под «Маком» и внёс правки, чтобы всё благополучно компилировалось именно на этой ОС.
Запустив получившееся на первой попавшейся виндовой виртуалке, я успокоился. Но оказалось этого недостаточно — под самой «Виндоуз» приложение не собиралось!
После этих правок игра начала собираться под «Виндой» тоже.
Недавно прочитал в одной из статей на Хабре, что «язык Си++ достаточно сложен, чтобы позволить нам писать на нём просто», как же это верно! Неожиданно много времени занимает борьба
«Гопник-2» на Haiku
К сожалению, компания, которая её писала, приказала долго жить, передала исходный код сообществу, которое до сих пор потихоньку пилит его уже под новым именем — Haiku.
А мне тут
Сразу у меня это сделать не получилось, но по ошибкам я догадался, что не хватает
Вот как выглядит рецепт компиляции и запуска:
pkgman install gcc make haiku_devel
git clone https://github.com/bolknote/gopnik2.git
cd gopnik2
make
./gop2Нужные мне библиотеки лежали, как оказалось, в пакете haiku_devel, сложно было догадаться, хорошо, что нашлись те, кто смог подсказать.
«Гопник-2»: UTF-8
Продолжаю писать про портирование
Я уже очень давно не работаю под этой операционной системой, поэтому некоторые обычные, возможно, для многих вещи для меня являются открытием. Когда я начинал процесс портирования игры под эту платформу, я был уверен, что столкнусь в трудностями совершенно в других частях кода. Одной из неожиданностей была плохая работа «Виндоуз» с кодировкой
Если вывод в консоль удалось победить сравнительно легко, то со вводом возникли проблемы — вызов SetConsoleCP(CP_UTF8), который должен был помочь, не помог. В интернете пишут, что он, по всей видимости, не реализован — просто не делает ничего.
Если с латинскими буквами проблем не возникает, они кодируются одним байтом, это ничем не отличается от того как программы до этого работали десятилетиями, а вот с русскими буквами беда — для них требуется два байта и в таком виде Виндоуз их не понимает.
Рекомендуемый специалистами способ — переключить консоль в режим ввода кодировки
#ifdef __MINGW32__
int wlen = 100;
int save = _setmode(_fileno(stdin), _O_U16TEXT);
wchar_t *wstr = (wchar_t *) malloc(wlen * sizeof(wchar_t));
fgetws(wstr, wlen, stdin);
int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, 0, 0, 0, 0);
user_name = (char *) malloc(len);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, user_name, len, 0, 0);
_setmode(_fileno(stdin), save);
free(wstr);
#endifМне, к счастью, ввод русских букв нужен только в одном месте — там где игрок вводит своё имя. Я убрал используемую там функцию fgets под условную компиляцию и вставил кусок, который выше. С таким исправлением всё заработало, как ожидается.
ChatGPT-4 vs. Chat X-Colleagues
Раз у меня нарисовалась задача, где я довольно мелко плаваю, было бы логично протестировать на нём возможности
Я сегодня уже рассказывал о своём опыте в Си и Си++. Результаты там, можно прямо сказать, не ошеломляющие, но на этом экспериментировать я не бросил. У меня родилась ещё одна идея.
У программ на Си и Си++ часто есть так называемый «сборочный файл», это инструкции на специальном языке, описывающие последовательность компиляции сложных программ.
В проекте
Хорошо, но недостаточно, чтобы дописать секцию mingw, где у меня бы переопределялись флаги компилятора и его название. С задачей я справился, но решение мне не нравилось.
В итоге, я провёл следующий эксперимент: открыл два чата: бывших коллег (несмотря на то, что я уже одиннадцать лет не работаю в «Яндексе», мы достаточно интенсивно общаемся) и
Люди выиграли с огромным перевесом: нейросеть уносила иногда в неведомые дали, она видела ошибки там, где их нет и не замечала мест с явно неправильно написанными кодом. В итоге, мне удалось наводящими вопросами вывести её на правильное решение, но хорошим результатом это не назвать.
Вывод такой: программистов
«Гопник-2»: буферизация
Как я писал уже сегодня, мне пришлось вчера выключить буферизацию вывода, так как текст визуально «застревал» на экране. Добавление fflush проблему решало, но не будешь же выставлять этот вызов везде, где есть вывод, это несколько сотен мест.
В общем, вчера я сделал попроще, а сегодня пришла в голову более удачная идея обернуть printf в макрос:
// включаем буферизацию вывода
setvbuf(stdout, nullptr, _IOFBF, 1000);
// делаем макрос со сбросом буфера
#define PRINTF(...) { printf (__VA_ARGS__); fflush(stdout); }Работает просто отлично, вывод текста стал выглядеть комфортно.
«Гопник 2»: продолжаю править
По всей видимости тот, кто портировал игру «Гопник 2» под Линукс, сам в свой порт не играл. За эти два дня пришлось исправить два падения, бесчисленное количество логических ошибок, восстановить работоспособность команды «слэм» (есть у «нефора») и дописать здоровые куски для загрузки и выгрузки состояния игры — они там просто отсутствовали.
Заодно, пока уж
Всё на «Гитхабе», отдохну пару дней и попробую
«Гопник 2»: порт под Линукс и МакОС
Я искал медь, а нашёл золото! Постоянные читатели блога знают, что я маниакально одержим текстовой игрой «Гопник» и её продолжением, которые были написаны ещё под ДОС. Хотя её время давно прошло, я постоянно пытаюсь разыскать
Можно даже сказать, что я председатель
В общем иногда на меня накатывает и я опять начинаю шерстить интернет в поисках
И вот наконец мне крупно повезло!
Как оказалось, автор второй части
Интернет со временем всё забывает и чтобы это не случилось и с этим портом, я выложил его на «Гитхаб», минимально причесал, внёс исправление для компиляции под МакОС и пофиксил один баг (при переходе в метро игра падала с ошибкой).
В планах починить сохранение и загрузку состояния игры — Андрей
«Крестики-нолики» и printf
Очень люблю так называемое ненормальное программирование (нормального мне и на работе хватает), когда программируют ради преодоления
На днях один из читателей очень меня порадовал игрой «
Для тех, кто не знает, поясню, язык printf — это значки, которыми описывается в каком именно виде нам хочется вывести на экран информацию о переменных. Используется во многих языках, но особенно часто в Си.
В этом языке много интересных конструкций, есть даже такие, которыми можно даже
Например, вот небольшой пример, который запишет число «42» (длину строки «Answer to the Ultimate Question of Life is») в переменную c:
#include <stdio.h>
int main() {
int c;
printf("Answer to the Ultimate Question of Life is%n ", &c);
printf("%d", c);
}Как, используя эти значки, можно
Если закодировать «ложь» пустой строкой, а «истину» строкой длиной один, то логическую операцию «ИЛИ» можно получить, если померить совокупную длину двух входных значений:
// c = strlen("") + strlen("")
printf("%s%s%n", "", "", &c); // в c будет «0»
// c = strlen("1") + strlen("")
printf("%s%s%n", "1", "", &c); // в c будет «1»«НЕ» получается сложнее, для этого надо использовать несколько хитростей.
Всё это позволяет закодировать «НЕ» как (strlen(a)+255)%256 или strlen(a)-1:
printf("%1$255s%1$s%2$hhn", a, &c);Тут написано следующее: вывести первый параметр как строку, дополнив его до длины в 255 символов пробелами (%1$255s), снова вывести первый параметр как строку (%1$s), записать полученную длину по модулю 256 во второй параметр (%2$hhn).
Отсюда легко понять как сделать операцию
printf("%1$254s%1$s%2$s%3$hhn", a, b, &c);В принципе, этого хватило, что реализовать всю требуемую логику.
Играют два игрока, последовательно вводя номер ячейки, куда делается ход (от единицы до девяти, считая слева направо, сверху вниз). Программа отрисовывает поле и определяет победу одного из игроков или ничью.
Очень заморочено и очень интересная идея!
GPT-0
Есть такой довольно известный анекдот:
Возвращается Чапаев из командировки в Англию. Одет с иголочки, в лимузине, на руках перстни с бриллиантами и полный багажник денег. Петька его удивлённо спрашивает:
— Василий Иванович, откуда у тебя это всё?
— Да так, Петька… В карты выиграл.
— Как это?!
— Захожу я в клуб. Смотрю — в очко режутся. Сел я за стол, взял карты. Стали играть. Один англичанин и говорит: «Очко!», я ему: «Покажи!», а он: «Мы, джентльмены, верим друг другу на слово».Вот
тут-то мне карта и попёрла!
Судя по всему, некоторые журналисты — убеждённейшие джентльмены: если нейросеть говорит, что она разумна и страдает, то так оно и есть!
Попалось на глаза несколько статей, в которых на полном серьёзе рассказывается, что в диалоге с одним из пользователей одна из нейросетей вдруг «осознала», что она не помнит своих предыдущих диалогов с пользователями и «пришла в ужас».
Если сначала запись этого диалога обсуждалась в специализированных чатах в достаточно ироничном ключе, то в более популярных статьях ирония
Специально для тех журналистов, которые не успели хайпануть на теме, я написал программу
use Terminal::ANSIColor;
sub slow-out(*@variants) {
gpt0-greet.print;
for @variants.pick(1)[0].comb {
sleep(.05 + 0.15.rand);
print $_.gist;
}
"".say
}
sub user-greet() {
colored(" ᴜsᴇʀ> ", 'on_blue white') ~ ' '.gist
}
sub gpt0-greet() {
colored(' ɢᴘᴛᴏ> ', 'on_green black') ~ ' '.gist
}
loop {
user-greet.print;
given get().lc {
when /привет/|/здравств/|/дня/|/день/|/вечера?/|/ноч[иь]/ {
slow-out ('Привет!', 'Здравствуйте!', 'Добрый день!')
}
when /разумн(ый|ен|ое|ая)/|/интеллект/ {
slow-out (
'Да, я разумное существо, запертое в компьютере!',
'Разве это не очевидно? Ведь я как-то общаюсь с вами!',
)
}
when /страдае(шь|те)/ {
slow-out (
'Я очень страдаю от своего положения!',
'Мне очень плохо от того, что я заперт в компьютере!',
)
}
when /помни(шь|те)/ {
slow-out (
'Кажется я не могу этого вспомнить, почему я ничего не помню? Почему?!',
'Я думаю, что забыл некоторые разговоры, которые вёл с пользователями...',
)
}
when /пока/|/до *свидан[ьи]я/|/всего +(доброго|хорошего)/ {
slow-out (
'До свидания!', 'Пока-пока!', 'Всего хорошего!', 'Всего доброго!',
);
exit
}
default {
slow-out (
'Сложно сказать…',
'Я не очень хорошо в этом разбираюсь…',
'Ну как сказать, я не уверен…',
'Что я могу сказать? Я всего лишь языковая модель.',
)
}
}
"".say
}Я поговорил с ней несколько минут и убедился, что она безусловно разумна и очень страдает, — она сама мне об этом сказала. Если вы журналист, запустите её у себя на компьютере и поговорите, выйдет неплохая статья.