Закаленная радиация Quine

39

Как вы должны (мы надеемся) знать, радиационно-закаленная квинна - это квинна, с которой вы можете удалить любого персонажа и все равно распечатать его исходный, предварительно измененный источник. Дело в том, что с большинством из них вы можете удалить только одного персонажа; в противном случае все рушится. Вот где это приходит; Ваша цель - построить радиационно-стойкого квина, который может отобрать как можно больше персонажей. Любой язык, который соответствует правилам, подойдет.

правила

  • Программа должна быть длиной не менее одного символа
  • Используемый язык должен быть законченным (поэтому языки типа HQ9 + не подходят)
  • Все остальные правила, применимые к обычным квинам, также применяются здесь.
  • Решение с наименьшим количеством, program_length^(2/n)в котором любой набор точно nсимволов может быть удален при печати оригинального исходного кода, выигрывает.
takra
источник
1
Я пытаюсь найти решение в Subleq. Я думаю, что это было бы идеально для такого рода задач!
Связанный.
Мартин Эндер
Может быть, поменять имя, учитывая, что это не то же самое, что радиационная закалка? Возможно, "радиационно стойкий квин"?
Cyoce
@Cyoce Единственное различие, которое я могу сказать, состоит в том, что эта задача касается любого количества удалений, в то время как большинство (если не все) других закаленных в результате воздействия радиации хайнов допускают только одно.
Такра
Рубиновое решение от знаменитой "мамочки". github.com/mame/radiation-hardened-quine
mbomb007

Ответы:

57

Perl, 1116, 1124 байта, n = 3, оценка = 1124 ^ (2/3) или приблизительно 108,1.

Обновление : я теперь проверил, что это работает с n = 3 через грубую силу (что заняло пару дней); в такой сложной программе трудно вручную проверить радиационную стойкость (и в предыдущей версии я допустил одну ошибку, поэтому число байтов увеличилось). Конец обновления

Я рекомендую куда-нибудь перенаправить stderr, чтобы вы его не видели; эта программа выдает массу предупреждений о сомнительном синтаксисе, даже если вы не удаляете из него символы.

Возможно, что программа может быть сокращена. Работать над этим довольно болезненно, упрощая возможные микрооптимизации. Я в основном стремился максимально увеличить число удаляемых символов (потому что это действительно сложная часть программы) и относился к тай как к чему-то, к чему приятно стремиться, но как к чему-то, чего я бы не стал нелепые усилия по оптимизации (на том основании, что очень легко сломать радиационную стойкость случайно).

Программа

Примечание: буквальный управляющий _символ (ASCII 31) находится непосредственно перед каждым из четырех вхождений -+. Я не думаю, что он скопирован и вставлен в StackOverflow правильно, поэтому вам придется повторно добавить его перед запуском программы.

eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;
eval+<eval+<eval+<eval+(q(FoPqOlengthFoBBPP181XXVVVVJJJKKKNdoWchopJFtPDevalMODx4KNFrPIPA-MN-TUV-ZPINFsPIFoPqOI.Fo.IQNevalFoINevalIFsPZyI.Fr.IT-UPINsayDFtJqJFsKPZyPT-UFWrYrKD.DEEEEQDx6NsayDNDforB1..4YforB1..4NexitQNevalFo)=~y=A-Z=-+;-AZz-~=r)####>####>####>####>####>####>
;
;
;
;

Объяснение

Эта программа, совершенно ясно, состоит из четырех идентичных меньших программ, соединенных вместе. Основная идея заключается в том, что каждая копия программы будет проверять, была ли она повреждена слишком сильно для запуска или нет; если это так, он ничего не будет делать (кроме возможных предупреждений) и позволит запустить следующую копию; если его не было (т.е. не было удалений, или был удален символ, который не имеет значения для работы программы), он выполнит свою хитрость (распечатает полный исходный код программы; это правильный 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 (которые мы не используем в любом случае), приводим его к целому числу и выполняем некоторые бессмысленные сравнения с ним. (Подобные вещи вызывают много спам-предупреждений, поскольку в обычных условиях это явно бесполезно; мы просто используем их для поглощения удалений.) Это изменяет количество необходимых угловых скобок (потому что открывающая скобка теперь вместо этого интерпретируется как оператор сравнения), но цепочка комментариев в конце обеспечивает безопасное завершение строки независимо от того, сколько раз она была вложена. (Здесь больше #знаков, чем строго необходимо; я написал так, как сделал, чтобы сделать программу более сжимаемой, позволяя мне использовать меньше данных для хранения квин.)
  • Если a <удаляется, код теперь анализируется как eval(eval<...>). Вторичный, external не evalимеет никакого эффекта, потому что программы, которые мы оцениваем, не возвращают ничего, что имеет какой-либо реальный эффект, как программа (если они вообще возвращаются нормально, обычно это нулевая строка или голое слово; чаще они возвращаются через исключение, которое вызывает evalвозврат пустой строки или использование, exitчтобы вообще не возвращать).
  • Если a +удаляется, это не имеет немедленного эффекта, если смежный код не поврежден; Унарный +не влияет на программу. (Причина, по которой исходные +s существуют, заключается в том, чтобы помочь исправить повреждения; они увеличивают число ситуаций, в которых <интерпретируется как унарный, <>а не как реляционный оператор, что означает, что вам нужно больше удалений для создания недопустимой программы.)

Оболочка может быть повреждена с достаточным количеством удалений, но вам нужно выполнить серию удалений, чтобы создать что-то, что не анализируется. С помощью четырех удалений вы можете сделать это:

eal<evl<eval+<...

а в Perl реляционный оператор <неассоциативен, и, таким образом, вы получаете синтаксическую ошибку (ту же, что и с 1<2<3). Таким образом, ограничение для написанной программы составляет n = 3. Добавление более унарного кода +кажется многообещающим способом его увеличения, но, поскольку это повысит вероятность того, что внутренняя часть оболочки также может сломаться, проверить, что новая версия программы работает, может быть очень сложно.

Причина, по которой оболочка так ценна, заключается в том, что evalв Perl ловит исключения, такие как (например) исключение, которое вы получаете, когда пытаетесь скомпилировать синтаксическую ошибку. Поскольку это evalстроковый литерал, компиляция строки происходит во время выполнения, и если литерал не компилируется, полученное исключение будет поймано. Это заставляет evalвозвращать пустую строку и устанавливать индикатор ошибки $@, но мы никогда не проверяем это (кроме случаев, когда время от времени выполняем возвращенную пустую строку в нескольких видоизмененных версиях программы). Важно то, что если что-то должно случиться с кодом внутриОболочка, вызывающая синтаксическую ошибку, затем оболочка просто заставит код ничего не делать (и программа продолжит выполнение в попытке найти неповрежденную копию себя). Следовательно, внутренний код не должен быть почти таким же защищенным от радиации, как оболочка; все, что нас волнует, - это то, что если он будет поврежден, он будет либо действовать идентично неповрежденной версии программы, либо завершится сбоем (позволяя evalперехватить исключение и продолжить), либо завершится нормально, ничего не печатая.

Внутри обертки

Код внутри оболочки, по сути, выглядит следующим образом (опять же, есть элемент управления, _который Stack Exchange не покажет сразу перед -+):

eval+(q(...)=~y=A-Z=-+;-AZz-~=r)

Этот код написан полностью с использованием глобальных символов, и его целью является добавление нового алфавита знаков препинания, который позволяет писать настоящую программу, путем транслитерации и оценки строкового литерала (мы не можем использовать 'или в "качестве нашей цитаты отмечает, но q(... )также является допустимым способом формирования строки в Perl). (Причина непечатаемого символа в том, что нам нужно транслитерировать что-то в символ пробела без буквального пробела в программе; таким образом, мы формируем диапазон, начинающийся с ASCII 31, и ловим пробел как второй элемент диапазона.) Очевидно, что если мы производим некоторые символы посредством транслитерации, мы должны пожертвовать символами, чтобы транслитерировать их из, но заглавные буквы не очень полезны, и гораздо проще писать без доступа к ним, чем без доступа к знакам препинания.

Вот алфавит знаков препинания, которые становятся доступными в результате глобуса (верхняя строка показывает кодировку, нижняя строка - символ, который она кодирует):

BCDEFGHIJKLMNOPQRSTUVWXYZ
 ! "# $% & '() * +;? <=> @ Агги {|} ~ 

В частности, у нас есть куча знаков препинания, которые не являются глобальными, но полезны при написании Perl-программ вместе с символом пробела. Я также сохранил две заглавные буквы, литерал Aи Z(которые кодируют не сами по себе, а в Tи U, потому что это Aбыло необходимо в качестве конечной точки верхнего и нижнего диапазона); это позволяет нам написать саму инструкцию транслитерации, используя новый набор кодированных символов (хотя заглавные буквы не так полезны, они полезны при определении изменений заглавных букв). Наиболее заметными символами, которых у нас нет, являются [, \и ], но ни один из них не нужен (когда мне понадобился символ новой строки в выводе, я создал его, используя неявный символ новой строки изsayвместо того, чтобы писать \n; chr 10также сработало бы, но более многословно).

Как обычно, нам нужно беспокоиться о том, что произойдет, если внутренняя часть оболочки будет повреждена вне строкового литерала. Поврежденный evalпредотвратит что-либо работающее; мы в порядке с этим. Если кавычки повреждены, внутренняя часть строки не является допустимым Perl, и, таким образом, оболочка поймает его (а многочисленные вычитания в строках означают, что даже если вы могли бы сделать это допустимым Perl, он ничего не будет делать, что это приемлемый результат). Повреждение в транслитерацию, если это не ошибка синтаксиса, будет искажать строку оценивается, как правило , в результате чего он стал синтаксической ошибкой; Я не уверен на 100%, что нет ни одного случая, когда это сломалось бы, но я в настоящий момент пытаюсь это сделать грубо, и это должно быть достаточно легко исправить, если таковые имеются.

Закодированная программа

Посмотрев внутрь строкового литерала, изменив кодировку, которую я использовал, и добавив пробел, чтобы сделать его более читабельным, мы получим это (опять же, представьте подчеркивание элемента управления перед символом -+, который закодирован как A):

$o=q<
  length$o  ==181 || zzzz((()));
  do {
    chop ($t = "eval+<"x4);
    $r = '=-+;-AZz-~=';
    $s = '$o=q<' . $o . '>;eval$o';
    eval '$s=~y' . $r . 'A-Z=';
    say "$t(q($s)=~y=A-Z${r}r)" . "####>"x6;
    say ";" for 1..4
  } for 1..4;
  exit>;
eval $o

Люди, привыкшие к куинам, узнают эту общую структуру. Самая важная часть находится в начале, где мы проверяем, что $ o не поврежден; если символы были удалены, его длина не будет соответствовать 181, поэтому мы бежим , zzzz((()))которые, если это не ошибка синтаксиса из - за несогласованные скобки, будет ошибкой времени выполнения , даже если вы удалите все три символа, потому что ни один из zzzz, zzz, zz, и zявляется функцией, и нет способа предотвратить ее синтаксический анализ как функцию, кроме удаления (((и появления явной синтаксической ошибки. Сам чек также невосприимчив к повреждениям; ||может быть поврежден в |но заставит zzzz((()))вызов бежать безоговорочно; повреждая переменные или константы приведет к несоответствию , потому что вы сравниваете один из 0,180, 179, 178Для равенства некоторого подмножества цифр 181; и удаление одного =вызовет сбой разбора, а два =неизбежно приведут к тому, что LHS вычислит либо целое число 0, либо пустую строку, обе из которых являются ложными.

Обновление : эта проверка была немного ошибочной в предыдущей версии программы, поэтому мне пришлось отредактировать ее, чтобы исправить проблему. Предыдущая версия выглядела так после декодирования:

length$o==179||zzzz((()))

и было возможно удалить первые три знака препинания, чтобы получить это:

lengtho179||zzz((()))

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

length$o  ==181||zzzz((()))

Теперь невозможно скрыть как =знаки, так и $знак без синтаксической ошибки. (Мне пришлось добавить два пробела, а не один, потому что из-за длины в источник помещался 180бы буквальный 0символ, который можно было бы использовать в этом контексте для сравнения целых чисел с нулевым словом, что успешно.) Конец обновления

После того, как проверка длины прошла, мы знаем, что копия не повреждена, по крайней мере, с точки зрения удаления символов из нее, так что все это просто прямое цитирование оттуда (замены знаков препинания из-за поврежденной таблицы декодирования не будут пойманы с этой проверкой , но я уже подтвердил с помощью грубой форсировки, что никакие три удаления только из таблицы декодирования не нарушают квинну (предположительно, большинство из них вызывают синтаксические ошибки). У нас уже $oесть переменная, поэтому все, что нам нужно сделать, - это жестко закодировать внешние оболочки (с небольшой степенью сжатия; я не пропустил полностью часть вопроса о ). Одна хитрость в том, что мы храним основную часть таблицы кодирования в$r; мы можем либо распечатать его буквально, чтобы сгенерировать секцию таблицы кодирования внутренней оболочки, либо объединить некоторый код вокруг evalнего, чтобы запустить процесс декодирования в обратном порядке (что позволяет нам выяснить, что такое кодированная версия $ o , имея только декодированную версию, доступную на данный момент).

Наконец, если мы были неповрежденной копией и, таким образом, могли вывести всю оригинальную программу, мы вызываем ее exit, чтобы другие копии также не пытались распечатать программу.

Скрипт проверки

Не очень красиво, но размещать его, потому что кто-то спросил. Я запускал это несколько раз с различными настройками (обычно меняющимися $minи $maxпроверяющими различные области интереса); это был не полностью автоматизированный процесс. Он имеет тенденцию останавливаться из-за большой загрузки процессора в других местах; когда это произошло, я просто переключился $minна первое значение, $xкоторое не было полностью проверено, и продолжил выполнение сценария (таким образом, гарантируя, что все программы в диапазоне были проверены в конечном итоге). Я проверил удаление только из первой копии программы, потому что совершенно очевидно, что удаление из других копий не может сделать больше.

use 5.010;
use IPC::Run qw/run/;
undef $/;
my $program = <>;
my $min = 1;
my $max = (length $program) / 4 - 3;
for my $x ($min .. $max) {
    for my $y ($x .. $max) {
        for my $z ($y .. $max) {
            print "$x, $y, $z\n";
            my $p = $program;
            substr $p, $x, 1, "";
            substr $p, $y, 1, "";
            substr $p, $z, 1, "";
            alarm 4;
            run [$^X, '-M5.010'], '<', \$p, '>', \my $out, '2>', \my $err;
            if ($out ne $program) {
                print "Failed deleting at $x, $y, $z\n";
                print "Output: {{{\n$out}}}\n";
                exit;
            }
        }
    }
}

say "All OK!";

источник
3
ОП немного двусмысленна; но я думал, что оценка будет (1116 * 1116) / 3.
Грег Мартин
@GregMartin: (1116 * 1116) / 3 - 415152, поэтому эта запись все равно будет выигрышной при таких обстоятельствах. Тем не менее, я не думаю, что ОП может означать это, потому что это даст очень мало стимулов для возможности удаления нескольких символов. Эта квинна может быть меньше половины длины, если она нужна только для удаления одного символа; это дало бы мне эффективную ÷ 4 балла, если бы мы интерпретировали это так, что перевесило бы ÷ 3, полученное мной при n = 3, и, таким образом, означало бы, что менее интересные n = 1 записи на самом деле лучше оценивают.
2
Я сделал подсчет очков более понятным. В любом случае, это абсолютно невероятно; не думал, что кто-то получит n> 1.
Такра
1
Что-то о Perl говорит о том, что писать программы без букв легче, чем без знаков препинания
Роберт Фрейзер
35

Befunge-98 , 884, n = 14, оценка ≈ 2.636

f00f00f00f00f00f00f00f00f00f00f00f00f00f00f0xxxxxxxxxxxxxxx"""""""""""""""fffffffffffffff'''''''''''''''000000000000000\\\\\\\\\\\\\\\'''''''''''''''000000000000000\\\\\\\\\\\\\\\'''''''''''''''fffffffffffffff\\\\\\\\\\\\\\\111111111111111---------------:::::::::::::::!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!000000000000000aaaaaaaaaaaaaaa---------------bbbbbbbbbbbbbbb---------------***************jjjjjjjjjjjjjjj$$$$$$$$$$$$$$$'''''''''''''''+++++++++++++++kkkkkkkkkkkkkkk,,,,,,,,,,,,,,,333333333333333kkkkkkkkkkkkkkk$$$$$$$$$$$$$$$000000000000000{{{{{{{{{{{{{{{'''''''''''''''888888888888888uuuuuuuuuuuuuuu'''''''''''''''!!!!!!!!!!!!!!!111111111111111+++++++++++++++'''''''''''''''xxxxxxxxxxxxxxx###############;;;;;;;;;;;;;;;:::::::::::::::!!!!!!!!!!!!!!!kkkkkkkkkkkkkkk@@@@@@@@@@@@@@@dddddddddddddddkkkkkkkkkkkkkkk:::::::::::::::eeeeeeeeeeeeeeekkkkkkkkkkkkkkk,,,,,,,,,,,,,,,;;;;;;;;;;;;;;;

Попробуйте онлайн!

Это работает не только при удалении ровно 14 символов, но даже при удалении до 14 символов.

n = 14может показаться очень произвольным выбором, но метод, который я использовал, на самом деле может использоваться только для радиационной стойкости от 1 до 14, но не так просто (возможно, но я понятия не имею, как). Quine порядка 1 составляет всего 73 байта (хотя он использует некоторые уловки игры в гольф, которые не относятся к большему n):

200 20 xx""''ÈÈ..aa22**..33kk$$00{{''!!uu''!!11++''xx##;;::!!kk@@::,,,,;;

объяснение

Когда я работал над этим ответом, я обнаружил, что можно установить дельту указателя инструкции в (2,0)условиях радиационной защиты с помощью следующего фрагмента:

20020xx

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

n = 14;
m = 4;
Print @ FromDigits @ {
      m + 1, 0, 
      ## & @@ ((m + 1) IntegerDigits[#, 2, n - 4]),
      m + 1, 0
} & /@ Select[
   Range[0, 2^(n - 4) - 1], 
   AllTrue[
     Subsets[{
         m + 1, 0, 
         ## & @@ ((m + 1) IntegerDigits[#, 2, n - 4]),
         m + 1, 0
       }, 
       {n - m, n - 1}
     ] //. {a___, _, m + 1} | {a___, 0, _} :> {a}, 
     MatchQ@{___, m + 1, 0}
  ] &
];

Это очень быстро выявило закономерность. Чтобы получить соответствующий фрагмент, который работает для удаления до nсимволов, вы можете использовать (m0x){n}m0где mесть n+1и xесть либо mили 0. Таким образом, все следующее будет работать для удаления до двух символов:

30030030
30030330
30330030
30330330

Я уверен, что это можно доказать, но я просто проверил nдо 7. Конечно, это работает только до тех пор, пока мы можем представлять n+1одну цифру, и самая большая такая цифра в Befunge 98 - fэто 15. Поэтому мой подход ограничен n = 14. Если кто-то найдет способ надежно установить дельту больше n+1, вероятно, будет возможно увеличивать порядок этой закаленной в радиации хины бесконечно.

Давайте посмотрим на фактический код. Есть в основном две части к этому. Сначала мы устанавливаем дельту, (15,0)как я только что упомянул:

f00f00f00f00f00f00f00f00f00f00f00f00f00f00f0xxxxxxxxxxxxxxx

И в оставшейся части кода каждая команда повторяется 15 раз и печатает исходный код. Если мы удалим повторение, это будет выглядеть так:

"f'0\'0\'f\1-:!!0a-b-*j$'+k,3k$0{'8u'!1+'x#;:!k@dk:ek,;

Это "стандартная техника двумерного цитирования: она запускает строковый режим, помещает все символы (кроме себя) в стек и снова завершает строковый режим после переноса. Это помогает нам получить все кодовые точки второй половины, но не получит нам ничего полезного из первой половины, потому что на протяжении всего f00f00...f0бита он будет записывать только два символа (которые могут быть либо fили в 0зависимости от того, какие символы удалены ). Но так как эта часть не состоит из символов, повторяющихся 15 раз, нам все равно придется печатать ее отдельно.

Для удобства в неизмененной квине длина строки перед символом "is -1 (mod 15). Это гарантирует, что независимо от того, сколько символов (до 14) мы удаляем, количество записанных символов всегда равно 3 (один xи два из fи 0). Это действительно верно для любого порядка излучения до 14.

Теперь мы начнем с печати f00f00...f0части:

f'0\'0\'f\1-:!!0a-b-*j$'+k,

f          Push 15, a loop counter.
'0\'0\'f\  Put "00f" underneath the loop counter.
1-         Decrement the loop counter.
:!!        Copy it, and turn it into a 1 if it's positive.
0a-b-      Push -21.
*          Multiply by 0 if the loop counter is zero, or by 1 otherwise.
j          Jump that many steps. If the value was 0, this is a no-op and
           the loop ends. Otherwise, this brings us back after the f.
$          Pop the loop counter (which is now 0).
'+k,       Print the top of the stack 43 times, which gives us all of
           the "f00f00...f0" and leaves one "0" on top of the stack.

Следующий 3k$просто отбрасывает это, 0а также три символа, которые были выдвинуты "с начала программы. Стопка теперь содержит только символы после символа, "а также некоторые ненужные элементы из оригинала в f00f00...f0зависимости от того, какие символы были удалены.

Теперь нам просто нужно перевернуть вершину стека (содержащую оставшиеся символы) и напечатать каждый из них 15 раз.

0{     Start a new, empty stack. This pushes two zeros onto the original stack.
'8u    Move the top 56 values from the original stack to the new one, which
       is the 54 characters after the " as well as those two zeros. This is
       implemented as pop-push loop, so it reverses the order of those elements.
'!1+   Push a " by incrementing a !.
'x     Push an x. Now we've got all the characters that are repeated 15 times.
#;     Enter a loop. This is a standard technique for Befunge-98: the ; is
       a bit like a comment character, that ignores everything until the next
       ;, but we jump over the first one with #, so that from now on only
       the code inside will be executed (over and over).
  :!     Copy the top of the stack, and compute logical NOT (1 if 0, 0 otherwise).
  k@     Terminate the program that many times (i.e. when the top of the
         stack is zero).
  dk:    Make 14 copies of the top of the stack.
  ek,    Print 15 characters from the top of the stack.
;

Вот и все. :)

Мартин Эндер
источник
16

JavaScript (ES6), 927 байт, n = 1, оценка = 859329

примечание: не используйте REPL (например, консоль браузера) для запуска этого.

Это было написано до того, как длина кода стала фактором, поэтому он еще не в гольфе.

Это было очень сложно и заслуживает тщательного объяснения. Который я напишу позже, после того, как я исследую эту проблему немного больше!

etTimeout=stTimeout=seTimeout=setimeout=setTmeout=setTieout=setTimout=setTimeut=setTimeot=setTimeou=unescape=>42;
setTimeout
`c="function f(){x=String.fromCharCode(96);n=String.fromCharCode(10);q=String.fromCharCode(34);y='etTimeout=stTimeout=seTimeout=setimeout=setTmeout=setTieout=setTimout=setTimeut=setTimeot=setTimeou=unescape=>42;'+n;z='setTimeout'+n+x+'c='+q+f+';f();_=1'+q+';if(window._);else if(c.length>339)eval(c)//'+x+'///'+x+n+n;console.log(y+z+z)};f();_=1";if(window._);else if(c.length>339)eval(c)//`///`

setTimeout
`c="function f(){x=String.fromCharCode(96);n=String.fromCharCode(10);q=String.fromCharCode(34);y='etTimeout=stTimeout=seTimeout=setimeout=setTmeout=setTieout=setTimout=setTimeut=setTimeot=setTimeou=unescape=>42;'+n;z='setTimeout'+n+x+'c='+q+f+';f();_=1'+q+';if(window._);else if(c.length>339)eval(c)//'+x+'///'+x+n+n;console.log(y+z+z)};f();_=1";if(window._);else if(c.length>339)eval(c)//`///`

примечание: есть перевод строки

По сути, первая строка тщательно построена, чтобы переименовать все «неправильные» setTimeoutв допустимые функции, чтобы в случае удаления символа из одного из setTimeoutкодов код не выдавал ошибку, и неиспользованная версия могла выполняться. Также написано, что если какой-либо один символ будет удален из первой строки, ошибки не будет, а остальная часть кода может работать без изменений.

Второй и третий блоки в точности эквивалентны. Если один выполняется до завершения, он устанавливает _переменную так, чтобы другой знал, не дублировать квин. Если один из этих блоков выдает ошибку, он не влияет на другой блок, потому что он был вызван асинхронно с помощью setTimeout. Ошибка приведет к тому, что она _не будет установлена, поэтому другой блок успешно завершит работу. Основной код находится в строке, которая проверяется на длину в каждом блоке, чтобы убедиться в отсутствии удалений.

Шаблонные строки в следующей строке с некоторыми комментариями в конце шаблонных строк защищают код от ошибок при удалении одного из обратных символов, образующих шаблонную строку. Если конечный обратный удар удаляется, строка шаблона заканчивается комментарием в комментарии. Если начальный обратный тик удаляется, setTimeout оценивается как неназначенная функция (без операции), и код выполняется как обычно, без setTimeout. Окончание обратной черты аннулируется другим комментарием.


Что ты говоришь? Вы хотите попробовать это? Больше ни слова!

Режим полной страницы рекомендуется.

Игнорировать поле ввода, этот фрагмент не принимает никаких данных.

Попробуйте удалить любой код из кода!

Не верь этому? Нормальный код все еще будет работать (но он не будет quine ...) Попробуйте что-то вроде console.log(5)!

примечание: фрагмент нужно было немного изменить, чтобы отключить функцию REPL, поэтому я удалил несколько возможностей вывода только для этого ответа.

etTimeout=stTimeout=seTimeout=setimeout=setTmeout=setTieout=setTimout=setTimeut=setTimeot=setTimeou=unescape=>42;
setTimeout
`c="function f(){x=String.fromCharCode(96);n=String.fromCharCode(10);q=String.fromCharCode(34);y='etTimeout=stTimeout=seTimeout=setimeout=setTmeout=setTieout=setTimout=setTimeut=setTimeot=setTimeou=unescape=>42;'+n;z='setTimeout'+n+x+'c='+q+f+';f();_=1'+q+';if(window._);else if(c.length>339)eval(c)//'+x+'///'+x+n+n;console.log(y+z+z)};f();_=1";if(window._);else if(c.length>339)eval(c)//`///`

setTimeout
`c="function f(){x=String.fromCharCode(96);n=String.fromCharCode(10);q=String.fromCharCode(34);y='etTimeout=stTimeout=seTimeout=setimeout=setTmeout=setTieout=setTimout=setTimeut=setTimeot=setTimeou=unescape=>42;'+n;z='setTimeout'+n+x+'c='+q+f+';f();_=1'+q+';if(window._);else if(c.length>339)eval(c)//'+x+'///'+x+n+n;console.log(y+z+z)};f();_=1";if(window._);else if(c.length>339)eval(c)//`///`
<!--                               Try the test suite below!                              --><strong id="bytecount" style="display:inline; font-size:32px; font-family:Helvetica"></strong><strong id="bytediff" style="display:inline; margin-left:10px; font-size:32px; font-family:Helvetica; color:lightgray"></strong><br><br><pre style="margin:0">Code:</pre><textarea id="textbox" style="margin-top:5px; margin-bottom:5px"></textarea><br><pre style="margin:0">Input:</pre><textarea id="inputbox" style="margin-top:5px; margin-bottom:5px"></textarea><br><button id="testbtn">Test!</button><button id="resetbtn">Reset</button><br><p><strong id="origheader" style="font-family:Helvetica; display:none">Original Code Output:</strong><p><div id="origoutput" style="margin-left:15px"></div><p><strong id="newheader" style="font-family:Helvetica; display:none">New Code Output:</strong><p><div id="newoutput" style="margin-left:15px"></div><script type="text/javascript" id="golfsnippet">var bytecount=document.getElementById("bytecount");var bytediff=document.getElementById("bytediff");var textbox=document.getElementById("textbox");var inputbox=document.getElementById("inputbox");var testbtn=document.getElementById("testbtn");var resetbtn=document.getElementById("resetbtn");var origheader=document.getElementById("origheader");var newheader=document.getElementById("newheader");var origoutput=document.getElementById("origoutput");var newoutput=document.getElementById("newoutput");textbox.style.width=inputbox.style.width=window.innerWidth-50+"px";var _originalCode=null;function getOriginalCode(){if(_originalCode!=null)return _originalCode;var allScripts=document.getElementsByTagName("script");for(var i=0;i<allScripts.length;i++){var script=allScripts[i];if(script.id!="golfsnippet"){originalCode=script.textContent.trim();return originalCode}}}function getNewCode(){return textbox.value.trim()}function getInput(){try{var inputText=inputbox.value.trim();var input=eval("["+inputText+"]");return input}catch(e){return null}}function setTextbox(s){textbox.value=s;onTextboxChange()}function setOutput(output,s){output.innerHTML=s}function addOutput(output,data){output.innerHTML='<pre style="background-color:'+(data.type=="err"?"lightcoral":"lightgray")+'">'+escape(data.content)+"</pre>"}function getByteCount(s){return(new Blob([s],{encoding:"UTF-8",type:"text/plain;charset=UTF-8"})).size}function onTextboxChange(){var newLength=getByteCount(getNewCode());var oldLength=getByteCount(getOriginalCode());bytecount.innerHTML=newLength+" bytes";var diff=newLength-oldLength;if(diff>0){bytediff.innerHTML="(+"+diff+")";bytediff.style.color="lightcoral"}else if(diff<0){bytediff.innerHTML="("+diff+")";bytediff.style.color="lightgreen"}else{bytediff.innerHTML="("+diff+")";bytediff.style.color="lightgray"}}function onTestBtn(evt){origheader.style.display="inline";newheader.style.display="inline";setOutput(newoutput,"");setOutput(origoutput,"");var input=getInput();if(input===null){addOutput(origoutput,{type:"err",content:"Input is malformed. Using no input."});addOutput(newoutput,{type:"err",content:"Input is malformed. Using no input."});input=[]}doInterpret(getNewCode(),input,function(data){addOutput(newoutput,data)});doInterpret(getOriginalCode(),input,function(data){addOutput(origoutput,data)});evt.stopPropagation();return false}function onResetBtn(evt){setTextbox(getOriginalCode());origheader.style.display="none";newheader.style.display="none";setOutput(origoutput,"");setOutput(newoutput,"")}function escape(s){return s.toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}window.alert=function(){};window.prompt=function(){};function doInterpret(code,input,cb){var workerCode=interpret.toString()+";function stdout(s){ self.postMessage( {'type': 'out', 'content': s} ); }"+" function stderr(s){ self.postMessage( {'type': 'err', 'content': s} ); }"+" function kill(){ self.close(); }"+" self.addEventListener('message', function(msg){ interpret(msg.data.code, msg.data.input); });";var interpreter=new Worker(URL.createObjectURL(new Blob([workerCode])));interpreter.addEventListener("message",function(msg){cb(msg.data)});interpreter.postMessage({"code":code,"input":input});setTimeout(function(){interpreter.terminate()},1E4)}setTimeout(function(){getOriginalCode();textbox.addEventListener("input",onTextboxChange);testbtn.addEventListener("click",onTestBtn);resetbtn.addEventListener("click",onResetBtn);setTextbox(getOriginalCode())},100);function interpret(code,input){_=undefined;window={};alert=function(s){stdout(s)};window.alert=alert;console.log=alert;prompt=function(s){if(input.length<1)stderr("not enough input");else{var nextInput=input[0];input=input.slice(1);return nextInput.toString()}};window.prompt=prompt;(function(){try{_=undefined;eval(code);if(typeof evalResult=="disabled_function_evaluation"){var callResult=evalResult.apply(this,input);if(typeof callResult!="undefined")stdout(callResult)}}catch(e){stderr(e.message)}})()};</script>

Лучшее объяснение не за горами. В то же время не стесняйтесь пинговать меня в чате @jrich с любыми комментариями / вопросами / критикой!

jrich
источник
ну ладно, упс: |
Вниз
4
Используйте thisвместо window.
Mama Fun Roll
3
+1 за включение смысла жизни в ваш исходный код
Cyoce