Utwardzona promieniowaniem Quine

39

Jak powinieneś (miejmy nadzieję) wiedzieć, utwardzona promieniowaniem Quine to quine, w której możesz usunąć dowolną postać i wydrukować jej oryginalne, wstępnie zmodyfikowane źródło. Chodzi o to, że w większości z nich możesz usunąć tylko jedną postać; inaczej wszystko się zepsuje. To jest, gdzie to wchodzi; Twoim celem jest zbudowanie zahartowanej na promieniowanie kabiny, która może zabrać jak najwięcej postaci. Każdy język zgodny z zasadami jest w porządku.

Zasady

  • Program musi mieć co najmniej jeden znak
  • Używany język musi być kompletny (więc języki takie jak HQ9 + nie kwalifikują się)
  • Tutaj obowiązują również wszystkie inne zasady dotyczące normalnych quines.
  • Wygrywa rozwiązanie z najmniejszą ilością, program_length^(2/n)w której można usunąć dowolny zestaw dokładnie nznaków podczas drukowania oryginalnego kodu źródłowego.
takra
źródło
1
Próbuję znaleźć rozwiązanie Subleq. Myślę, że byłoby to idealne rozwiązanie dla tego rodzaju wyzwań!
Związane z.
Martin Ender
Może zmień nazwę, biorąc pod uwagę, że nie jest to to samo, co utwardzony promieniowaniem quine? Być może „ odporny na promieniowanie quine”?
Cyoce
@Cyoce Jedyną różnicą, jaką mogę powiedzieć, jest to, że wyzwanie dotyczy dowolnej liczby przeprowadzek, podczas gdy większość (jeśli nie wszystkie) zahartowane promieniowaniem quiny dopuszcza tylko jedną.
takra
Rozwiązanie Ruby od słynnego „mame”. github.com/mame/radiation-hardened-quine
mbomb007

Odpowiedzi:

57

Perl, 1116 1124 bajtów, n = 3, wynik = 1124 ^ (2/3) lub około 108,1

Aktualizacja : Sprawdziłem teraz, że działa to przy n = 3 za pomocą brutalnej siły (co zajęło kilka dni); z programem tego kompleksu trudno jest ręcznie sprawdzić odporność na promieniowanie (i popełniłem jeden błąd w poprzedniej wersji, dlatego wzrosła liczba bajtów). Zakończ aktualizację

Polecam przekierowanie stderr gdzieś, gdzie go nie zobaczysz; ten program generuje mnóstwo ostrzeżeń o podejrzanej składni, nawet jeśli nie usuwasz z niej znaków.

Możliwe jest, że program może zostać skrócony. Praca nad tym jest dość bolesna, dzięki czemu łatwo przeoczyć możliwe mikrooptymalizacje. Miałem głównie na celu jak największą liczbę usuwalnych znaków (ponieważ jest to naprawdę trudna część programu) i potraktowałem grę w jako coś, co było miłym celem, ale jako coś, czego nie chciałbym umieścić niedorzeczny wysiłek na rzecz optymalizacji (na podstawie tego, że bardzo łatwo jest przypadkowo przełamać odporność na promieniowanie).

Program

Uwaga: _tuż przed każdym z czterech wystąpień występuje dosłowny znak kontrolny (ASCII 31) -+. Nie sądzę, aby poprawnie skopiował i wkleił do StackOverflow, więc będziesz musiał dodać go ponownie przed uruchomieniem programu.

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)####>####>####>####>####>####>
;
;
;
;

Wyjaśnienie

Program ten jest dość wyraźnie złożony z czterech identycznych mniejszych programów połączonych razem. Podstawową ideą jest to, że każda kopia programu sprawdzi, czy została zbyt poważnie uszkodzona, aby ją uruchomić, czy nie; jeśli tak, nie zrobi nic (oprócz ostrzeżeń wypluwających) i uruchomi następną kopię; jeśli tak nie było (tzn. nie usunięto, lub usunięty znak był tym, który nie ma znaczenia dla działania programu), zrobi to, co w jego mocy (wypisanie kodu źródłowego pełnego programu; to jest właściwa quine, z każdej strony zawierającej kodowania całego kodu źródłowego), następnie wyjście (zapobieganie inne nieuszkodzone kopii drukowanie kodu źródłowego się ponownie i w ten sposób niszcząc Quine'a drukując zbyt tekst).

Każda część z kolei składa się z dwóch części, które są funkcjonalnie niezależne; opakowanie zewnętrzne i trochę kodu wewnętrznego. Jako takie możemy rozpatrywać je osobno.

Zewnętrzne opakowanie

Zewnętrzne opakowanie jest w zasadzie eval<+eval<+eval< ... >####>####...>###(plus wiązka średników i znaków nowej linii, których cel powinien być dość oczywisty; ma to zapewnić, że części programu pozostaną oddzielone niezależnie od tego, czy niektóre średniki lub nowe znaki przed nimi zostaną usunięte ). To może wydawać się dość proste, ale jest subtelne na wiele sposobów i dlatego wybrałem Perla do tego wyzwania.

Najpierw przyjrzyjmy się, jak działa opakowanie w nieuszkodzonej kopii programu. evalparsuje jako funkcja wbudowana, która przyjmuje jeden argument. Ponieważ spodziewany jest spór, +tutaj jest jedno +(co będzie już bardzo znane golfistom Perla; przydają się zaskakująco często). Nadal oczekujemy argumentu (właśnie widzieliśmy jednoargumentowy operator), więc <następny jest interpretowany jako początek <>operatora (który nie przyjmuje argumentów przedrostkowych lub postfiksowych, a zatem może być użyty w pozycji argumentu).

<>jest dość dziwnym operatorem. Jego zwykłym celem jest odczytywanie uchwytów plików i umieszczanie nazwy uchwytu pliku w nawiasach kątowych. Alternatywnie, jeśli wyrażenie nie jest poprawne jako nazwa uchwytu pliku, dokonuje globowania (w zasadzie ten sam proces, którego używają powłoki UNIX do tłumaczenia tekstu wprowadzonego przez użytkownika na sekwencję argumentów wiersza poleceń; faktycznie używane były znacznie starsze wersje Perla powłoka do tego, ale obecnie Perl zajmuje się globowaniem wewnętrznie). Dlatego zamierzone użycie jest zgodne z <*.c>, co zwykle zwraca listę podobną do ("foo.c", "bar.c"). W kontekście skalarnym (np. Argument doeval), po prostu zwraca pierwszy wpis, który znajdzie przy pierwszym uruchomieniu (odpowiednik pierwszego argumentu), i zwraca inne wpisy dotyczące hipotetycznych przyszłych przebiegów, które nigdy się nie zdarzają.

Teraz powłoki często obsługują argumenty wiersza poleceń; jeśli podasz coś -rbez argumentów, zostanie ono przekazane dosłownie do programu, niezależnie od tego, czy istnieje plik o takiej nazwie, czy nie. Perl działa w ten sam sposób, tak długo, jak upewniamy się, że nie ma żadnych znaków, które byłyby specjalne dla powłoki lub Perla między <dopasowaniem a dopasowaniem >, możemy efektywnie używać tego jak naprawdę niezręcznej formy literału łańcuchowego. Co więcej, parser Perla dla operatorów podobnych do cytatów ma kompulsywną tendencję do dopasowywania nawiasów, nawet w kontekstach takich jak ten, w którym nie ma to sensu, więc możemy <>bezpiecznie zagnieżdżać się (co jest odkryciem potrzebnym dla tego programu). Głównym minusem wszystkich tych zagnieżdżonych <>jest to, że uciekają one od zawartości<>jest prawie niemożliwe; Wydaje się, że istnieją dwie warstwy odskakiwania z każdą <>, więc aby uciec przed czymś we wszystkich trzech, należy poprzedzić je 63 odwrotnymi ukośnikami. Uznałem, że mimo iż rozmiar kodu jest jedynie drugorzędną kwestią w tym problemie, prawie na pewno nie warto było płacić tego rodzaju kar za mój wynik, więc postanowiłem napisać resztę programu bez użycia obrażających znaków.

Co się stanie, jeśli części opakowania zostaną usunięte?

  • Usunięcie słowa evalpowoduje, że zamienia się ono w jedno słowo , ciąg bez znaczenia. Perl ich nie lubi, ale traktuje je tak, jakby były otoczone cytatami; dlatego eal<+eval<+...jest interpretowane jako"eal" < +eval<+.... Nie ma to wpływu na działanie programu, ponieważ po prostu bierze wynik z mocno zagnieżdżonych evals (których i tak nie używamy), rzucając go na liczbę całkowitą i dokonując na nim bezsensownych porównań. (Ten rodzaj rzeczy powoduje wiele spamujących ostrzeżeń, ponieważ najwyraźniej nie jest to przydatne w normalnych okolicznościach; używamy go tylko do pochłaniania usunięć). Zmienia to liczbę potrzebnych nawiasów zamykających (ponieważ nawias otwierający jest potrzebny jest teraz interpretowany jako operator porównania), ale łańcuch komentarzy na końcu zapewnia, że ​​łańcuch zakończy się bezpiecznie bez względu na to, ile razy jest zagnieżdżony. (Jest tu więcej #znaków niż jest to absolutnie potrzebne; napisałem to tak, jak zrobiłem, aby uczynić program bardziej kompresowalnym, pozwalając mi używać mniej danych do przechowywania quine.)
  • Jeśli a <zostanie usunięte, kod będzie teraz analizowany jako eval(eval<...>). Drugorzędne, zewnętrzne evalnie ma żadnego efektu, ponieważ oceniane przez nas programy nie zwracają niczego, co ma jakikolwiek rzeczywisty efekt jako program (jeśli w ogóle zwracają normalnie, zwykle jest to ciąg zerowy lub jedno słowo; częściej wracają przez wyjątek, który powoduje evalzwrócenie ciągu zerowego lub użycie, exitaby w ogóle nie zwracać).
  • Usunięcie a +nie ma natychmiastowego efektu, jeśli sąsiedni kod jest nienaruszony; unary +nie ma wpływu na program. (Powodem, dla którego +są tam oryginalne, jest pomoc w naprawianiu szkód; zwiększają one liczbę sytuacji, w których <interpretowane są jako jednoargumentowe, <>a nie jako operator relacyjny, co oznacza, że ​​potrzebujesz więcej usunięć, aby utworzyć nieprawidłowy program).

Opakowanie może zostać uszkodzone przez wystarczającą liczbę usunięć, ale musisz wykonać serię usunięć, aby uzyskać coś, co nie zostanie przeanalizowane. Dzięki czterem usunięciom możesz to zrobić:

eal<evl<eval+<...

a w Perlu operator relacyjny nie <jest asocjatywny, dlatego otrzymujesz błąd składniowy (ten sam, który dostaniesz 1<2<3). W związku z tym ograniczenie dla programu w formie, w jakiej napisano, wynosi n = 3. Dodanie większej liczby jednostek jednoosobowych +wydaje się obiecującym sposobem na jego zwiększenie, ale ponieważ zwiększałoby to prawdopodobieństwo, że wnętrze opakowania również mogłoby się zepsuć, sprawdzając, czy nowa wersja programu może być bardzo trudna.

Powodem, dla którego opakowanie jest tak cenne, jest to, że evalw Perlu wyłapuje się wyjątki, takie jak (na przykład) wyjątek, który występuje podczas próby skompilowania błędu składniowego. Ponieważ evaljest to literał łańcuchowy, kompilacja łańcucha zachodzi w czasie wykonywania, a jeśli literał się nie skompiluje, wyjątek zostanie przechwycony. Powoduje evalto zwrócenie łańcucha zerowego i ustawienie wskaźnika błędu $@, ale my też nigdy nie sprawdzamy (z wyjątkiem sporadycznego wykonywania zwróconego łańcucha zerowego w kilku zmutowanych wersjach programu). Co najważniejsze, oznacza to, że jeśli coś się stanie z kodem w środkuotoki, powodując błąd składniowy, otoki po prostu powodują, że kod nic nie robi (zamiast tego program będzie kontynuował próbę znalezienia nieuszkodzonej kopii siebie). Dlatego wewnętrzny kod nie musi być prawie tak odporny na promieniowanie jak opakowanie; wszystko, na czym nam zależy, to to, że jeśli zostanie uszkodzony, będzie działał identycznie jak w przypadku nieuszkodzonej wersji programu, albo zawiesi się (pozwalając evalzłapać wyjątek i kontynuować) lub zakończy normalnie bez drukowania niczego.

Wewnątrz opakowania

Zasadniczo kod wewnątrz opakowania wygląda następująco (ponownie, istnieje Kontrola - _stos wymiany nie pokaże się bezpośrednio przed -+):

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

Ten kod jest napisany w całości ze znakami bezpiecznymi dla całego świata, a jego celem jest dodanie nowego alfabetu znaków interpunkcyjnych, które umożliwiają napisanie prawdziwego programu, poprzez transliterację i ocenę literału łańcuchowego (nie możemy używać 'ani "jako naszego cytatu znaczniki, ale q()jest także prawidłowym sposobem tworzenia łańcucha w Perlu). (Powodem, dla którego nie można drukować, jest to, że musimy transliterować coś do znaku spacji bez dosłownego znaku spacji w programie; w ten sposób tworzymy zakres zaczynający się od ASCII 31 i przechwytujemy spację jako drugi element zakresu.) oczywiście, jeśli mamy do produkcji niektórych znaków poprzez transliteracji, musimy poświęcić znaki transliterować je od, ale wielkie litery nie są zbyt przydatne i o wiele łatwiej jest pisać bez dostępu do nich niż bez dostępu do znaków interpunkcyjnych.

Oto alfabet znaków interpunkcyjnych, które stają się dostępne w wyniku glob (górna linia pokazuje kodowanie, dolna linia koduje znak):

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

Przede wszystkim mamy kilka znaków interpunkcyjnych, które nie są globalnie bezpieczne, ale są przydatne do pisania programów w języku Perl, wraz ze znakiem spacji. Zapisałem również dwie wielkie litery, literalne Ai Z(które kodują nie same w sobie, ale do Ti U, ponieważ Abyły potrzebne jako punkt końcowy górnego i dolnego zakresu); pozwala nam to napisać instrukcję transliteracji z użyciem nowego zestawu zakodowanych znaków (chociaż wielkie litery nie są tak przydatne, są przydatne do określania zmian wielkich liter). Najbardziej zauważalną znaków, że nie mamy dostępne są [, \i ], ale nie są potrzebne (gdy potrzebowałem nowego wiersza na wyjściu, wyprodukowałem go za pomocą nowej linii z niejawnegosayzamiast pisać \n; chr 10również by działało, ale jest bardziej szczegółowe).

Jak zwykle musimy martwić się, co się stanie, jeśli wnętrze opakowania zostanie uszkodzone poza dosłownym ciągiem znaków. Zepsuty evaluniemożliwi działanie czegokolwiek; nic nam nie jest. Jeśli znaki cudzysłowu ulegną uszkodzeniu, wnętrze łańcucha nie jest prawidłowe w Perlu, a zatem opakowanie go złapie (a liczne odejmowanie ciągów oznacza, że ​​nawet gdybyś mógł uczynić go poprawnym w Perlu, nie zrobiłby nic, co jest akceptowalnym wynikiem). Uszkodzenie transliteracji, jeśli nie jest to błąd składniowy, zakłóci oceniany ciąg znaków, zwykle powodując, że stanie się on błędem składni; Nie jestem w 100% pewien, że nie ma przypadków, w których to się psuje, ale w tej chwili zmuszam się do brutalnego zmuszania, aby się upewnić, i jeśli tak, to powinno być łatwe do naprawienia.

Zakodowany program

Zaglądając do literału ciągu, odwracając używane kodowanie i dodając białe znaki, aby było bardziej czytelne, otrzymujemy to (ponownie, wyobraź sobie kontrolny znak podkreślenia przed -+, który jest kodowany jako 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

Ludzie przyzwyczajeni do quinesów rozpoznają tę ogólną strukturę. Najważniejsza część znajduje się na początku, gdzie weryfikujemy, czy $ nie jest uszkodzony; Jeżeli znaki zostały usunięte, jego długość nie będzie pasował 181, więc prowadzimy zzzz((()))które, jeśli to nie jest błąd składni powodu nawiasach niedopasowanych, będzie runtime error, nawet jeśli usunąć wszystkie trzy znaki, ponieważ żaden z zzzz, zzz, zz, i zjest funkcją, i nie ma innego sposobu, aby zapobiec analizowaniu go jako funkcji innej niż usunięcie (((i spowodowanie błędnego składni. Sam czek jest również odporny na uszkodzenia; ||mogą być uszkodzone |, ale to spowoduje zzzz((()))wywołanie uruchomić bezwarunkowo; uszkodzenie zmiennych lub stałych spowoduje niedopasowanie, ponieważ porównujesz jedną z nich 0,180, 179, 178Równości do pewnego podzbioru cyfr 181; a usunięcie jednego =spowoduje błąd analizy, a dwa =nieuchronnie spowodują, że LHS będzie oceniać na liczbę całkowitą 0 lub ciąg zerowy, z których oba są falsey.

Aktualizacja : Ta kontrola była nieco niepoprawna w poprzedniej wersji programu, więc musiałem ją edytować, aby rozwiązać problem. Poprzednia wersja wyglądała tak po dekodowaniu:

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

możliwe było usunięcie pierwszych trzech znaków interpunkcyjnych:

lengtho179||zzz((()))

lengtho179, będąc gołym słowem, jest prawdomówne i tym samym łamie czek. Naprawiłem to, dodając dodatkowe dwa Bznaki (które kodują spacje), co oznacza, że ​​najnowsza wersja quine robi to:

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

Teraz nie można ukryć zarówno =znaków, jak i $znaku bez spowodowania błędu składniowego. (Musiałem dodać dwie spacje zamiast jednej, ponieważ długość 180wstawiłaby dosłowny 0znak do źródła, który może być wykorzystany w tym kontekście do porównania liczb całkowitych z jednym gołym słowem, co się powiedzie.) Koniec aktualizacji

Po sprawdzeniu długości wiemy, że kopia jest nieuszkodzona, przynajmniej pod względem usuwania znaków z niej, więc wszystko jest po prostu proste stamtąd (zastąpienie znaków interpunkcyjnych z powodu uszkodzonej tabeli dekodowania nie zostałoby złapane przy tym sprawdzeniu) , ale już sprawdziłem za pomocą brutalnego wymuszania, że ​​żadne trzy usunięcia tylko z tabeli dekodowania nie powodują przerwania quine; przypuszczalnie większość z nich powoduje błędy składniowe). Mamy $ojuż zmienną, więc wszystko, co musimy zrobić, to na stałe zakleić zewnętrzne opakowania (z pewnym niewielkim stopniem kompresji; nie pominąłem całkowicie pytania dotyczącego ). Jedną sztuczką jest to, że przechowujemy większość tabeli kodowania$r; możemy albo wydrukować go dosłownie, aby wygenerować sekcję tabeli kodowania wewnętrznego opakowania, lub połączyć w sobie część kodu i evalto w celu uruchomienia procesu dekodowania w odwrotnej kolejności (co pozwala nam dowiedzieć się, co to jest zakodowana wersja $ o , mając w tym momencie tylko zdekodowaną wersję).

Wreszcie, jeśli byliśmy nienaruszoną kopią, a tym samym mogliśmy exitwypisać cały oryginalny program, wywołujemy , aby inne kopie również nie próbowały wydrukować programu.

Skrypt weryfikacyjny

Niezbyt ładnie, ale publikowanie, ponieważ ktoś zapytał. Uruchomiłem to kilka razy z różnymi ustawieniami (zwykle zmieniając $mini $maxsprawdzając różne obszary zainteresowań); nie był to w pełni zautomatyzowany proces. Ma tendencję do zatrzymywania się z powodu dużego obciążenia procesora w innym miejscu; kiedy to się stało, właśnie zmieniłem $minna pierwszą wartość, $xktóra nie była w pełni sprawdzona i kontynuowałem uruchamianie skryptu (tym samym upewniając się, że ostatecznie wszystkie programy w zakresie zostały sprawdzone). Sprawdziłem tylko usunięcia z pierwszej kopii programu, ponieważ jest całkiem oczywiste, że usunięcie z innych kopii nie może zrobić więcej.

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!";

źródło
3
OP jest trochę niejednoznaczny; ale myślałem, że wynik to (1116 * 1116) / 3.
Greg Martin
@GregMartin: (1116 * 1116) / 3 to 415152, więc w tych okolicznościach nadal wygrywa ten wpis. Jednak nie sądzę, że OP może to oznaczać, ponieważ dawałoby bardzo niewielką motywację do radzenia sobie z usuwaniem wielu postaci. Quine może mieć mniej niż połowę długości, jeśli potrzebujesz go tylko do usunięcia jednej postaci; to dałoby mi efektywne ÷ 4 na wynik, jeśli interpretujemy go w ten sposób, który przeważałby ÷ 3, które otrzymuję od n = 3, a tym samym oznacza, że ​​mniej interesujące n = 1 wpisy są tak naprawdę lepsze.
2
Doprecyzowałem punktację. W każdym razie jest to absolutnie niewiarygodne; nie sądził, że ktokolwiek dostanie n> 1.
takra
1
Mówi coś o Perlu, że łatwiej jest pisać program bez liter niż bez interpunkcji
Robert Fraser
35

Befunge-98 , 884, n = 14, wynik ≈ 2,636

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

Wypróbuj online!

Działa to nie tylko po usunięciu dokładnie 14 znaków, ale nawet po usunięciu dowolnej liczby do 14 znaków.

n = 14może się wydawać bardzo arbitralnym wyborem, ale zastosowaną przeze mnie technikę można w rzeczywistości stosować tylko do zamówień wzmacniających promieniowanie od 1 do 14, ale nie jest to łatwe (może to być możliwe, ale nie mam pojęcia, jak to zrobić). Quine rzędu 1 ma zaledwie 73 bajty (chociaż wykorzystuje kilka sztuczek golfowych, które nie dotyczą większych n):

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

Wyjaśnienie

Kiedy pracowałem nad tą odpowiedzią , stwierdziłem, że możliwe jest ustawienie delty wskaźnika instrukcji na (2,0)warunki zahartowane promieniowaniem za pomocą następującego fragmentu:

20020xx

Zobacz tę odpowiedź, dlaczego to działa. Znalazłem to tylko z odrobiną ręcznego majstrowania, ale to nasuwało pytanie, czy istnieją podobne wzorce, które są wytrzymałe przy usuwaniu wielu znaków. Napisałem więc krótki skrypt Mathematica, aby wyszukać je brutalną siłą:

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}
  ] &
];

To bardzo szybko ujawniło pewien wzór. Aby uzyskać odpowiedni fragment kodu, który działa na usunięcie nawet do nznaków, można użyć (m0x){n}m0gdzie mjest n+1i xjest albo malbo 0. Tak więc wszystkie poniższe elementy będą działać w przypadku usuwania maksymalnie dwóch znaków:

30030030
30030330
30330030
30330330

Jestem pewien, że można to udowodnić, ale po prostu zweryfikowałem ndo 7. Oczywiście działa to tylko tak długo, jak długo możemy reprezentować n+1jedną cyfrę, a największa taka cyfra w Befunge 98 to f15, dlatego moje podejście jest ograniczone n = 14. Jeśli ktoś znajdzie sposób, aby wiarygodnie ustawić deltę na większą n+1, prawdopodobnie będzie możliwe zwiększenie kolejności tego utwardzanego promieniowaniem quinu w nieskończoność.

Spójrzmy na rzeczywisty kod. Są w zasadzie dwie części. Najpierw ustawiliśmy deltę (15,0)tak, jak właśnie wspomniałem:

f00f00f00f00f00f00f00f00f00f00f00f00f00f00f0xxxxxxxxxxxxxxx

Pozostała część kodu ma każde polecenie powtórzone 15 razy i drukuje źródło. Jeśli usuniemy powtórzenie, wygląda to tak:

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

"Jest standardową techniką quining 2D: rozpoczyna tryb ciąg, popycha wszystkie postacie (oprócz siebie) na tryb ciąg komina i końce ponownie po owinięciu wokół. To pomaga nam uzyskać wszystkie punkty kodowe z drugiej połowy, ale to nie uda nam się dostać coś pożytecznego z pierwszej połowy, bo w całym f00f00...f0kawałku, to tylko rejestruje dwa znaki (który może być albo flub 0na które znaki są usuwane w zależności ). Ale ponieważ ta część nie składa się z powtarzających się znaków 15 razy, i tak będziemy musieli wydrukować ją osobno.

Dogodniej, w niemodyfikowanym quine, długość sznurka przed "jest -1 (mod 15). Gwarantuje to, że bez względu na to jak wiele znaków (do 14) usuwamy, że liczba znaków zapisanych jest zawsze 3 (jeden xi dwa fi 0). Dotyczy to w rzeczywistości każdego rzędu promieniowania do 14.

Teraz zaczynamy od wydrukowania f00f00...f0części:

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.

Następny 3k$po prostu odrzuca to, 0a także trzy postacie, które zostały popchnięte "od początku programu. Stos zawiera teraz tylko postacie po "śmieciach oraz część śmieci pod oryginałem, w f00f00...f0zależności od tego, które postacie zostały usunięte.

Teraz musimy tylko odwrócić górę stosu (zawierającą pozostałe znaki) i wydrukować każdy z nich 15 razy.

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.
;

I to wszystko. :)

Martin Ender
źródło
16

JavaScript (ES6), 927 bajtów, n = 1, wynik = 859329

Uwaga: nie używaj REPL (np. konsoli przeglądarki), aby to uruchomić.

Zostało to napisane, zanim długość kodu była czynnikiem, więc nie jest jeszcze golfa.

Było to bardzo trudne i zasługuje na dokładne wyjaśnienie. Które napiszę później, po tym, jak zbadam to wyzwanie jeszcze trochę!

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)//`///`

Uwaga: pojawia się nowa linia

Zasadniczo pierwszy wiersz jest starannie skonstruowany w celu zmiany nazwy wszystkich „błędów ortograficznych” setTimeoutna prawidłowe funkcje, dzięki czemu jeśli znak zostanie usunięty z jednego z setTimeoutnich, kod nie będzie błędny i będzie można uruchomić wersję bez szwanku. Jest również napisany, aby jakikolwiek znak został usunięty z pierwszego wiersza, nie wystąpi błąd, a reszta kodu będzie działać bez zmian.

Drugi i trzeci blok są dokładnie równoważne. Jeśli jeden biegnie do końca, ustawia _zmienną, aby drugi wiedział, że nie powiela quine. Jeśli jeden z tych bloków się nie powiedzie, nie wpływa to na drugi blok, ponieważ został wywołany asynchronicznie przy użyciu setTimeout. Błąd spowoduje, że _nie zostanie ustawiony, więc drugi blok pomyślnie zakończy pracę. Główny kod znajduje się w ciągu, który jest sprawdzany pod kątem długości w każdym bloku, aby upewnić się, że nie było żadnych usunięć.

Ciągi szablonów w następnym wierszu z niektórymi komentarzami na końcu ciągów szablonów chroni kod przed błędami, jeśli jeden z odwrotnych znaków tworzących ciąg szablonu zostanie usunięty. Jeśli końcowy znak wsteczny zostanie usunięty, ciąg szablonu zostanie zakończony przez strzałkę wsteczną w komentarzu. Jeśli początkowy backtick zostanie usunięty, setTimeout jest oceniany jako nieprzypisana funkcja (no-op), a kod działa normalnie, bez setTimeout. Końcowy zwrot wsteczny jest unieważniany przez inny komentarz.


Co ty mówisz Chcesz to wypróbować? Nie mów nic więcej!

Zalecany tryb pełnej strony.

Zignoruj ​​pole wprowadzania, ten fragment kodu nie wymaga wprowadzania danych.

Spróbuj usunąć dowolny kod z kodu!

Nie wierzysz w to? Normalny kod nadal będzie działał (ale nie będzie quine ...) Spróbuj czegoś takiego console.log(5)!

Uwaga: fragment kodu musiał zostać nieco zmodyfikowany, aby wyłączyć funkcję REPL, więc usunąłem wiele możliwości wyjścia tylko dla tej odpowiedzi.

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>

Nadchodzi lepsze wyjaśnienie. W międzyczasie zachęcamy do pingowania mnie na czacie @jrich z wszelkimi komentarzami / pytaniami / krytykami!

jrich
źródło
ah okej, zaraz: |
Downgoat
4
Użyj thiszamiast window.
Mama Fun Roll
3
+1 za uwzględnienie sensu życia w kodzie źródłowym
Cyoce