Как вы должны (мы надеемся) знать, радиационно-закаленная квинна - это квинна, с которой вы можете удалить любого персонажа и все равно распечатать его исходный, предварительно измененный источник. Дело в том, что с большинством из них вы можете удалить только одного персонажа; в противном случае все рушится. Вот где это приходит; Ваша цель - построить радиационно-стойкого квина, который может отобрать как можно больше персонажей. Любой язык, который соответствует правилам, подойдет.
правила
- Программа должна быть длиной не менее одного символа
- Используемый язык должен быть законченным (поэтому языки типа HQ9 + не подходят)
- Все остальные правила, применимые к обычным квинам, также применяются здесь.
- Решение с наименьшим количеством,
program_length^(2/n)
в котором любой набор точноn
символов может быть удален при печати оригинального исходного кода, выигрывает.
Subleq
. Я думаю, что это было бы идеально для такого рода задач!Ответы:
Perl,
1116,1124 байта, n = 3, оценка = 1124 ^ (2/3) или приблизительно 108,1.Обновление : я теперь проверил, что это работает с n = 3 через грубую силу (что заняло пару дней); в такой сложной программе трудно вручную проверить радиационную стойкость (и в предыдущей версии я допустил одну ошибку, поэтому число байтов увеличилось). Конец обновления
Я рекомендую куда-нибудь перенаправить stderr, чтобы вы его не видели; эта программа выдает массу предупреждений о сомнительном синтаксисе, даже если вы не удаляете из него символы.
Возможно, что программа может быть сокращена. Работать над этим довольно болезненно, упрощая возможные микрооптимизации. Я в основном стремился максимально увеличить число удаляемых символов (потому что это действительно сложная часть программы) и относился к разрыву тай -кода с гольфом как к чему-то, к чему приятно стремиться, но как к чему-то, чего я бы не стал нелепые усилия по оптимизации (на том основании, что очень легко сломать радиационную стойкость случайно).
Программа
Примечание: буквальный управляющий
_
символ (ASCII 31) находится непосредственно перед каждым из четырех вхождений-+
. Я не думаю, что он скопирован и вставлен в StackOverflow правильно, поэтому вам придется повторно добавить его перед запуском программы.Объяснение
Эта программа, совершенно ясно, состоит из четырех идентичных меньших программ, соединенных вместе. Основная идея заключается в том, что каждая копия программы будет проверять, была ли она повреждена слишком сильно для запуска или нет; если это так, он ничего не будет делать (кроме возможных предупреждений) и позволит запустить следующую копию; если его не было (т.е. не было удалений, или был удален символ, который не имеет значения для работы программы), он выполнит свою хитрость (распечатает полный исходный код программы; это правильный quine, с каждой частью, содержащей кодировку всего исходного кода), а затем завершите работу (не позволяя другим неповрежденным копиям снова распечатать исходный код и, таким образом, разрушить квинну, напечатав слишком много текста).
Каждая часть в свою очередь состоит из двух частей, которые являются функционально независимыми; внешняя обертка и некоторый внутренний код. Таким образом, мы можем рассмотреть их отдельно.
Внешняя обертка
Внешняя оболочка - это, в основном,
eval<+eval<+eval< ... >####>####...>###
(плюс несколько точек с запятой и символов новой строки, цель которых должна быть достаточно очевидной; она заключается в том, чтобы гарантировать, что части программы останутся разделенными независимо от того, будут ли удалены некоторые точки с запятой или символы новой строки перед ними. ). Это может показаться довольно простым, но это не так во многих отношениях, и причина, по которой я выбрал Perl для этой задачи.Во-первых, давайте посмотрим, как функционирует обертка в неповрежденной копии программы.
eval
разбирает как встроенную функцию, которая принимает один аргумент. Поскольку ожидается спор,+
вот унарный+
(который будет очень знаком игрокам в Perl к настоящему моменту; они оказываются полезными на удивление часто). Мы все еще ожидаем аргумент (мы только что видели унарный оператор), поэтому<
следующий за ним интерпретируется как начало<>
оператора (который не принимает префиксного или постфиксного аргумента и, следовательно, может использоваться в позиции операнда).<>
довольно странный оператор. Его обычно цель состоит в том, чтобы прочитать дескрипторы файлов, и вы поместите ссылку на файл имя в угловых скобках. Альтернативно, если выражение недопустимо в качестве имени дескриптора файла, оно выполняет глобализацию (в основном, тот же процесс, который оболочки UNIX используют для преобразования текста, введенного пользователем, в последовательность аргументов командной строки; фактически использовались гораздо более старые версии Perl). оболочка для этого, но в настоящее время Perl обрабатывает глобализацию внутренне). Следовательно, предполагаемое использование происходит по принципу<*.c>
, который обычно возвращает список вроде("foo.c", "bar.c")
. В скалярном контексте (например, аргументeval
), он просто возвращает первую запись, которую находит при первом запуске (эквивалент первого аргумента), и возвращает другие записи в гипотетических будущих прогонах, которые никогда не произойдут.Теперь оболочки часто обрабатывают аргументы командной строки; если вы дадите что-то вроде
-r
без аргументов, оно просто будет дословно передано программе, независимо от того, существует файл с таким именем или нет. Perl действует точно так же, поэтому, пока мы гарантируем, что нет никаких символов, которые являются специальными для оболочки или Perl между<
и соответствием>
, мы можем эффективно использовать это как действительно неуклюжую форму строкового литерала. Более того, синтаксический анализатор Perl для операторов, подобных кавычкам, имеет навязчивую тенденцию сопоставлять скобки даже в таких контекстах, как этот, где это не имеет смысла, поэтому мы можем<>
безопасно вкладывать (что необходимо для обнаружения этой программы). Основным недостатком всех этих вложенных<>
является то, что экранирование содержимого<>
почти невозможно; Кажется, что у каждого есть два слоя эскалации<>
, поэтому, чтобы избежать чего-то на внутренней стороне всех трех, ему должно предшествовать 63 обратных слэша. Я решил, что хотя размер кода является лишь второстепенным соображением в этой проблеме, почти наверняка не стоит платить такого рода штраф за мой счет, поэтому я просто решил написать остальную часть программы без использования оскорбительных символов.Так что же произойдет, если части оболочки будут удалены?
eval
превращают его в голое слово , строку без смысла. Perl не любит их, но обращается с ними так, как будто они заключены в кавычки; таким образомeal<+eval<+...
интерпретируется как"eal" < +eval<+...
, Это никак не влияет на работу программы, потому что она в основном просто берет результат из сильно вложенных evals (которые мы не используем в любом случае), приводим его к целому числу и выполняем некоторые бессмысленные сравнения с ним. (Подобные вещи вызывают много спам-предупреждений, поскольку в обычных условиях это явно бесполезно; мы просто используем их для поглощения удалений.) Это изменяет количество необходимых угловых скобок (потому что открывающая скобка теперь вместо этого интерпретируется как оператор сравнения), но цепочка комментариев в конце обеспечивает безопасное завершение строки независимо от того, сколько раз она была вложена. (Здесь больше#
знаков, чем строго необходимо; я написал так, как сделал, чтобы сделать программу более сжимаемой, позволяя мне использовать меньше данных для хранения квин.)<
удаляется, код теперь анализируется какeval(eval<...>)
. Вторичный, external неeval
имеет никакого эффекта, потому что программы, которые мы оцениваем, не возвращают ничего, что имеет какой-либо реальный эффект, как программа (если они вообще возвращаются нормально, обычно это нулевая строка или голое слово; чаще они возвращаются через исключение, которое вызываетeval
возврат пустой строки или использование,exit
чтобы вообще не возвращать).+
удаляется, это не имеет немедленного эффекта, если смежный код не поврежден; Унарный+
не влияет на программу. (Причина, по которой исходные+
s существуют, заключается в том, чтобы помочь исправить повреждения; они увеличивают число ситуаций, в которых<
интерпретируется как унарный,<>
а не как реляционный оператор, что означает, что вам нужно больше удалений для создания недопустимой программы.)Оболочка может быть повреждена с достаточным количеством удалений, но вам нужно выполнить серию удалений, чтобы создать что-то, что не анализируется. С помощью четырех удалений вы можете сделать это:
а в Perl реляционный оператор
<
неассоциативен, и, таким образом, вы получаете синтаксическую ошибку (ту же, что и с1<2<3
). Таким образом, ограничение для написанной программы составляет n = 3. Добавление более унарного кода+
кажется многообещающим способом его увеличения, но, поскольку это повысит вероятность того, что внутренняя часть оболочки также может сломаться, проверить, что новая версия программы работает, может быть очень сложно.Причина, по которой оболочка так ценна, заключается в том, что
eval
в Perl ловит исключения, такие как (например) исключение, которое вы получаете, когда пытаетесь скомпилировать синтаксическую ошибку. Поскольку этоeval
строковый литерал, компиляция строки происходит во время выполнения, и если литерал не компилируется, полученное исключение будет поймано. Это заставляетeval
возвращать пустую строку и устанавливать индикатор ошибки$@
, но мы никогда не проверяем это (кроме случаев, когда время от времени выполняем возвращенную пустую строку в нескольких видоизмененных версиях программы). Важно то, что если что-то должно случиться с кодом внутриОболочка, вызывающая синтаксическую ошибку, затем оболочка просто заставит код ничего не делать (и программа продолжит выполнение в попытке найти неповрежденную копию себя). Следовательно, внутренний код не должен быть почти таким же защищенным от радиации, как оболочка; все, что нас волнует, - это то, что если он будет поврежден, он будет либо действовать идентично неповрежденной версии программы, либо завершится сбоем (позволяяeval
перехватить исключение и продолжить), либо завершится нормально, ничего не печатая.Внутри обертки
Код внутри оболочки, по сути, выглядит следующим образом (опять же, есть элемент управления,
_
который Stack Exchange не покажет сразу перед-+
):Этот код написан полностью с использованием глобальных символов, и его целью является добавление нового алфавита знаков препинания, который позволяет писать настоящую программу, путем транслитерации и оценки строкового литерала (мы не можем использовать
'
или в"
качестве нашей цитаты отмечает, ноq(
...)
также является допустимым способом формирования строки в Perl). (Причина непечатаемого символа в том, что нам нужно транслитерировать что-то в символ пробела без буквального пробела в программе; таким образом, мы формируем диапазон, начинающийся с ASCII 31, и ловим пробел как второй элемент диапазона.) Очевидно, что если мы производим некоторые символы посредством транслитерации, мы должны пожертвовать символами, чтобы транслитерировать их из, но заглавные буквы не очень полезны, и гораздо проще писать без доступа к ним, чем без доступа к знакам препинания.Вот алфавит знаков препинания, которые становятся доступными в результате глобуса (верхняя строка показывает кодировку, нижняя строка - символ, который она кодирует):
В частности, у нас есть куча знаков препинания, которые не являются глобальными, но полезны при написании Perl-программ вместе с символом пробела. Я также сохранил две заглавные буквы, литерал
A
иZ
(которые кодируют не сами по себе, а вT
иU
, потому что этоA
было необходимо в качестве конечной точки верхнего и нижнего диапазона); это позволяет нам написать саму инструкцию транслитерации, используя новый набор кодированных символов (хотя заглавные буквы не так полезны, они полезны при определении изменений заглавных букв). Наиболее заметными символами, которых у нас нет, являются[
,\
и]
, но ни один из них не нужен (когда мне понадобился символ новой строки в выводе, я создал его, используя неявный символ новой строки изsay
вместо того, чтобы писать\n
;chr 10
также сработало бы, но более многословно).Как обычно, нам нужно беспокоиться о том, что произойдет, если внутренняя часть оболочки будет повреждена вне строкового литерала. Поврежденный
eval
предотвратит что-либо работающее; мы в порядке с этим. Если кавычки повреждены, внутренняя часть строки не является допустимым Perl, и, таким образом, оболочка поймает его (а многочисленные вычитания в строках означают, что даже если вы могли бы сделать это допустимым Perl, он ничего не будет делать, что это приемлемый результат). Повреждение в транслитерацию, если это не ошибка синтаксиса, будет искажать строку оценивается, как правило , в результате чего он стал синтаксической ошибкой; Я не уверен на 100%, что нет ни одного случая, когда это сломалось бы, но я в настоящий момент пытаюсь это сделать грубо, и это должно быть достаточно легко исправить, если таковые имеются.Закодированная программа
Посмотрев внутрь строкового литерала, изменив кодировку, которую я использовал, и добавив пробел, чтобы сделать его более читабельным, мы получим это (опять же, представьте подчеркивание элемента управления перед символом
-+
, который закодирован какA
):Люди, привыкшие к куинам, узнают эту общую структуру. Самая важная часть находится в начале, где мы проверяем, что $ o не поврежден; если символы были удалены, его длина не будет соответствовать
181
, поэтому мы бежим ,zzzz((()))
которые, если это не ошибка синтаксиса из - за несогласованные скобки, будет ошибкой времени выполнения , даже если вы удалите все три символа, потому что ни один изzzzz
,zzz
,zz
, иz
является функцией, и нет способа предотвратить ее синтаксический анализ как функцию, кроме удаления(((
и появления явной синтаксической ошибки. Сам чек также невосприимчив к повреждениям;||
может быть поврежден в|
но заставитzzzz((()))
вызов бежать безоговорочно; повреждая переменные или константы приведет к несоответствию , потому что вы сравниваете один из0
,180
,179
,178
Для равенства некоторого подмножества цифр181
; и удаление одного=
вызовет сбой разбора, а два=
неизбежно приведут к тому, что LHS вычислит либо целое число 0, либо пустую строку, обе из которых являются ложными.Обновление : эта проверка была немного ошибочной в предыдущей версии программы, поэтому мне пришлось отредактировать ее, чтобы исправить проблему. Предыдущая версия выглядела так после декодирования:
и было возможно удалить первые три знака препинания, чтобы получить это:
lengtho179
, будучи голым словом, правдива и, таким образом, нарушает чек. Я исправил это, добавив два дополнительныхB
символа (которые кодируют пробелы), что означает, что последняя версия quine делает это:Теперь невозможно скрыть как
=
знаки, так и$
знак без синтаксической ошибки. (Мне пришлось добавить два пробела, а не один, потому что из-за длины в источник помещался180
бы буквальный0
символ, который можно было бы использовать в этом контексте для сравнения целых чисел с нулевым словом, что успешно.) Конец обновленияПосле того, как проверка длины прошла, мы знаем, что копия не повреждена, по крайней мере, с точки зрения удаления символов из нее, так что все это просто прямое цитирование оттуда (замены знаков препинания из-за поврежденной таблицы декодирования не будут пойманы с этой проверкой , но я уже подтвердил с помощью грубой форсировки, что никакие три удаления только из таблицы декодирования не нарушают квинну (предположительно, большинство из них вызывают синтаксические ошибки). У нас уже
$o
есть переменная, поэтому все, что нам нужно сделать, - это жестко закодировать внешние оболочки (с небольшой степенью сжатия; я не пропустил полностью часть вопроса о коде-гольфе ). Одна хитрость в том, что мы храним основную часть таблицы кодирования в$r
; мы можем либо распечатать его буквально, чтобы сгенерировать секцию таблицы кодирования внутренней оболочки, либо объединить некоторый код вокругeval
него, чтобы запустить процесс декодирования в обратном порядке (что позволяет нам выяснить, что такое кодированная версия $ o , имея только декодированную версию, доступную на данный момент).Наконец, если мы были неповрежденной копией и, таким образом, могли вывести всю оригинальную программу, мы вызываем ее
exit
, чтобы другие копии также не пытались распечатать программу.Скрипт проверки
Не очень красиво, но размещать его, потому что кто-то спросил. Я запускал это несколько раз с различными настройками (обычно меняющимися
$min
и$max
проверяющими различные области интереса); это был не полностью автоматизированный процесс. Он имеет тенденцию останавливаться из-за большой загрузки процессора в других местах; когда это произошло, я просто переключился$min
на первое значение,$x
которое не было полностью проверено, и продолжил выполнение сценария (таким образом, гарантируя, что все программы в диапазоне были проверены в конечном итоге). Я проверил удаление только из первой копии программы, потому что совершенно очевидно, что удаление из других копий не может сделать больше.источник
Befunge-98 , 884, n = 14, оценка ≈ 2.636
Попробуйте онлайн!
Это работает не только при удалении ровно 14 символов, но даже при удалении до 14 символов.
n = 14
может показаться очень произвольным выбором, но метод, который я использовал, на самом деле может использоваться только для радиационной стойкости от 1 до 14, но не так просто (возможно, но я понятия не имею, как). Quine порядка 1 составляет всего 73 байта (хотя он использует некоторые уловки игры в гольф, которые не относятся к большемуn
):объяснение
Когда я работал над этим ответом, я обнаружил, что можно установить дельту указателя инструкции в
(2,0)
условиях радиационной защиты с помощью следующего фрагмента:Посмотрите этот ответ, почему это работает. Я обнаружил это лишь немного повозившись вручную, но это подняло вопрос о том, существуют ли подобные шаблоны, которые являются устойчивыми при удалении нескольких символов. Поэтому я написал короткий сценарий Mathematica для поиска их грубой силой:
Это очень быстро выявило закономерность. Чтобы получить соответствующий фрагмент, который работает для удаления до
n
символов, вы можете использовать(m0x){n}m0
гдеm
естьn+1
иx
есть либоm
или0
. Таким образом, все следующее будет работать для удаления до двух символов:Я уверен, что это можно доказать, но я просто проверил
n
до7
. Конечно, это работает только до тех пор, пока мы можем представлятьn+1
одну цифру, и самая большая такая цифра в Befunge 98 -f
это 15. Поэтому мой подход ограниченn = 14
. Если кто-то найдет способ надежно установить дельту большеn+1
, вероятно, будет возможно увеличивать порядок этой закаленной в радиации хины бесконечно.Давайте посмотрим на фактический код. Есть в основном две части к этому. Сначала мы устанавливаем дельту,
(15,0)
как я только что упомянул:И в оставшейся части кода каждая команда повторяется 15 раз и печатает исходный код. Если мы удалим повторение, это будет выглядеть так:
Это
"
стандартная техника двумерного цитирования: она запускает строковый режим, помещает все символы (кроме себя) в стек и снова завершает строковый режим после переноса. Это помогает нам получить все кодовые точки второй половины, но не получит нам ничего полезного из первой половины, потому что на протяжении всегоf00f00...f0
бита он будет записывать только два символа (которые могут быть либоf
или в0
зависимости от того, какие символы удалены ). Но так как эта часть не состоит из символов, повторяющихся 15 раз, нам все равно придется печатать ее отдельно.Для удобства в неизмененной квине длина строки перед символом
"
is-1 (mod 15)
. Это гарантирует, что независимо от того, сколько символов (до 14) мы удаляем, количество записанных символов всегда равно 3 (одинx
и два изf
и0
). Это действительно верно для любого порядка излучения до 14.Теперь мы начнем с печати
f00f00...f0
части:Следующий
3k$
просто отбрасывает это,0
а также три символа, которые были выдвинуты"
с начала программы. Стопка теперь содержит только символы после символа,"
а также некоторые ненужные элементы из оригинала вf00f00...f0
зависимости от того, какие символы были удалены.Теперь нам просто нужно перевернуть вершину стека (содержащую оставшиеся символы) и напечатать каждый из них 15 раз.
Вот и все. :)
источник
JavaScript (ES6), 927 байт, n = 1, оценка = 859329
примечание: не используйте REPL (например, консоль браузера) для запуска этого.
Это было написано до того, как длина кода стала фактором, поэтому он еще не в гольфе.
Это было очень сложно и заслуживает тщательного объяснения. Который я напишу позже, после того, как я исследую эту проблему немного больше!
примечание: есть перевод строки
По сути, первая строка тщательно построена, чтобы переименовать все «неправильные»
setTimeout
в допустимые функции, чтобы в случае удаления символа из одного изsetTimeout
кодов код не выдавал ошибку, и неиспользованная версия могла выполняться. Также написано, что если какой-либо один символ будет удален из первой строки, ошибки не будет, а остальная часть кода может работать без изменений.Второй и третий блоки в точности эквивалентны. Если один выполняется до завершения, он устанавливает
_
переменную так, чтобы другой знал, не дублировать квин. Если один из этих блоков выдает ошибку, он не влияет на другой блок, потому что он был вызван асинхронно с помощьюsetTimeout
. Ошибка приведет к тому, что она_
не будет установлена, поэтому другой блок успешно завершит работу. Основной код находится в строке, которая проверяется на длину в каждом блоке, чтобы убедиться в отсутствии удалений.Шаблонные строки в следующей строке с некоторыми комментариями в конце шаблонных строк защищают код от ошибок при удалении одного из обратных символов, образующих шаблонную строку. Если конечный обратный удар удаляется, строка шаблона заканчивается комментарием в комментарии. Если начальный обратный тик удаляется, setTimeout оценивается как неназначенная функция (без операции), и код выполняется как обычно, без setTimeout. Окончание обратной черты аннулируется другим комментарием.
Что ты говоришь? Вы хотите попробовать это? Больше ни слова!
Режим полной страницы рекомендуется.
Игнорировать поле ввода, этот фрагмент не принимает никаких данных.
Попробуйте удалить любой код из кода!
Не верь этому? Нормальный код все еще будет работать (но он не будет quine ...) Попробуйте что-то вроде
console.log(5)
!примечание: фрагмент нужно было немного изменить, чтобы отключить функцию REPL, поэтому я удалил несколько возможностей вывода только для этого ответа.
Лучшее объяснение не за горами. В то же время не стесняйтесь пинговать меня в чате @jrich с любыми комментариями / вопросами / критикой!
источник
this
вместоwindow
.