Radiation Hardened Quine

39

9

As you should (hopefully) know, a radiation hardened quine is a quine that you can remove any one character from and still print its original, pre-modified source. The thing is, with most of these you can only remove one character; otherwise everything breaks down. This is where this comes in; your goal is to build a radiation-hardened quine that can take as many character removals as possible. Any language that complies with the rules is fine.

Rules

  • The program must be at least one character long
  • The language used must be turing complete (So languages like HQ9+ don't qualify)
  • All other rules that apply to normal quines also apply here.
  • The solution with the least program_length^(2/n) in which any set of exactly n characters can be removed while still printing the original source code wins.

takra

Posted 2015-09-08T02:05:09.723

Reputation: 793

1I'm trying to come up with a solution in Subleq. I think it would be ideal for this kind of challenge! – None – 2015-09-08T17:31:49.113

Related. – Martin Ender – 2016-01-15T15:10:45.410

Maybe change the name, given this isn't the same as a radiation hardened quine? Perhaps "radiation-proof quine"? – Cyoce – 2016-11-27T03:47:56.300

@Cyoce The only difference I can tell is that this challenge is for any number of removals, while most (if not all) other radiation-hardened quines only allow for one. – takra – 2016-11-27T03:55:49.560

Ruby solution from the famous "mame". https://github.com/mame/radiation-hardened-quine

– mbomb007 – 2016-12-13T21:44:31.397

IMO It's a bit too easy in GolfScript: 1 – Matthew Roh – 2017-03-18T04:43:09.850

Answers

57

Perl, 1116 1124 bytes, n=3, score=1124^(2/3) or approximately 108.1

Update: I've now verified that this works with n=3 via brute force (which took a couple of days); with a program this complex, it's hard to check for radiation-resistance by hand (and I made one mistake in a previous version, which is why the byte count increased). End update

I recommend redirecting stderr somewhere that you won't see it; this program produces a ton of warnings about dubious syntax even when you aren't deleting characters from it.

It's possible that the program can be shortened. Working on this is fairly painful, making it easy to miss possible micro-optimizations. I was mostly aiming to get the number of deletable characters as high as possible (because that's the really challenging part of the program), and treated the tiebreak as something that was nice to aim for but as something I wouldn't put ridiculous effort into optimizing (on the basis that it's very easy to break radiation resistance by accident).

The program

Note: there's a literal Control-_ character (ASCII 31) immediately before each of the four occurrences of -+. I don't think it copy-and-pasted onto StackOverflow correctly, so you'll have to re-add it before running the program.

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

The explanation

This program is, quite clearly, made out of four identical smaller programs concatenated together. The basic idea is that each copy of the program will verify whether it's been damaged too badly to run or not; if it has been, it will do nothing (other than possibly spew warnings) and let the next copy run; if it hasn't been (i.e. no deletions, or the character that got deleted was one that makes no difference to the program's operation), it will do its quiny thing (printing out the full program's source code; this is a proper quine, with each part containing an encoding of the entire source code) and then exit (preventing any other undamaged copies from printing the source code out again and thus ruining the quine by printing too much text).

Each part is in turn made of two parts which are effectively functionally independent; an outside wrapper and some internal code. As such, we can consider them separately.

Outside wrapper

The outside wrapper is, basically, eval<+eval<+eval< ... >####>####...>### (plus a bunch of semicolons and newlines whose purpose should be pretty obvious; it's to ensure that the parts of the program will remain separated regardless of if some of the semicolons, or the newlines before them, get deleted). This might look fairly simple, but it's subtle in a number of ways, and the reason I picked Perl for this challenge.

First, let's look at how the wrapper functions in an undamaged copy of the program. eval parses as a built-in function, which takes one argument. Because an argument is expected, + here is a unary + (which will be very familiar to Perl golfers by now; they come in useful surprisingly often). We're still expecting an argument (we just saw a unary operator), so the < that comes next is interpreted as the start of the <> operator (which takes no prefix or postfix arguments, and thus can be used in operand position).

<> is a fairly weird operator. Its usual purpose is to read filehandles, and you place the filehandle name inside the angle brackets. Alternatively, if the expression isn't valid as a filehandle name, it does globbing (basically, the same process that UNIX shells use to translate text entered by the user to a sequence of command-line arguments; much older versions of Perl actually used the shell for this, but nowadays Perl handles the globbing internally). The intended use, therefore, is along the lines of <*.c>, which would typically return a list like ("foo.c", "bar.c"). In a scalar context (such as the argument to eval), it just returns the first entry it finds the first time it's run (the equivalent of the first argument), and would return other entries on hypothetical future runs that never happen.

Now, shells often handle command-line arguments; if you give something like -r with no arguments, it'll just be passed to the program verbatim, regardless of whether there's a file with that name or not. Perl acts the same way, so as long as we ensure that there are no characters that are special to the shell or to Perl between the < and the matching >, we can effectively use this like a really awkward form of string literal. Even better, Perl's parser for quote-like operators has a compulsive tendency to match brackets even in contexts like this one where it makes no sense, so we can nest <> safely (which is the discovery needed for this program to be possible). The major downside of all these nested <> is that escaping the contents of the <> is almost impossible; there seem to be two layers of unescaping with each <>, so to escape something on the inside of all three, it needs to be preceded with 63 backslashes. I decided that even though code size is only a secondary consideration in this problem, it almost certainly wasn't worth paying this sort of penalty to my score, so I just decided to write the rest of the program without using the offending characters.

So what happens if parts of the wrapper get deleted?

  • Deletions in the word eval cause it to turn into a bareword, a string with no meaning. Perl doesn't like these, but it treats them as though they were surrounded with quotes; thus eal<+eval<+... is interpreted as "eal" < +eval<+.... This has no effect on the program's operation, because it's basically just taking the result from the heavily nested evals (which we don't use anyway), casting it to an integer, and doing some pointless comparisons on it. (This sort of thing causes a lot of warning spam as it's clearly not a useful thing to do under normal circumstances; we're just using it to absorb deletions.) This changes the number of closing angle brackets we need (because the opening bracket is now being interpreted as a comparison operator instead), but the chain of comments at the end ensures the string will end safely no matter how many times it's nested. (There are more # signs than strictly needed here; I wrote it like I did in order to make the program more compressible, letting me use less data to store the quine.)
  • If a < gets deleted, the code now parses as eval(eval<...>). The secondary, outside eval has no effect, because the programs we're evaluating don't return anything that has any real effects as a program (if they return normally at all, it's normally a null string or a bareword; more commonly they return via exception, which causes eval to return a null string, or use exit to avoid returning at all).
  • If a + gets deleted, this has no immediate effect if the adjacent code is intact; unary + has no effect on the program. (The reason the original +s are there is to help repair damage; they increase the number of situations in which < is interpreted as a unary <> rather than as a relational operator, meaning you need more deletions to produce an invalid program.)

The wrapper can be damaged with enough deletions, but you need to do a series of deletions in order to produce something that doesn't parse. With four deletions, you can do this:

eal<evl<eval+<...

and in Perl, the relational operator < is nonassociative, and thus you get a syntax error (the same one you'd get with 1<2<3). As such, the cap for the program as written is n=3. Adding more unary +s seems like a promising way to increase it, but as that'd make it increasingly likely that the inside of the wrapper could break too, verifying that the new version of the program works could be very difficult.

The reason the wrapper is so valuable is that eval in Perl catches exceptions, such as (for example) the exception that you get when you try to compile a syntax error. Because this eval is of a string literal, the compile of the string happens at runtime, and if the literal fails to compile, the resulting exception gets caught. This causes eval to return a null string and set the error indicator $@, but we never check either (except by occasionally executing the returned null string in a few mutated versions of the program). Crucially, this means that if something should happen to the code inside the wrapper, causing a syntax error, then the wrapper will just cause the code to do nothing instead (and the program will keep executing in an attempt to find an undamaged copy of itself). Therefore, the inside code doesn't have to be nearly as radiation-proof as the wrapper; all that we care about is that if damaged, it'll either act identically to the nondamaged version of the program, or else it'll crash (allowing eval to catch the exception and continue) or exit normally without printing anything.

Inside the wrapper

The code inside the wrapper, fundamentally, looks like this (again, there's a Control-_ that Stack Exchange won't show immediately before the -+):

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

This code is written entirely with glob-safe characters, and its purpose is to add a new alphabet of punctuation marks that make it possible to write a real program, via transliterating and evaluating a string literal (we can't use ' or " as our quote marks, but q() is also a valid way to form a string in Perl). (The reason for the unprintable character is that we need to transliterate something into the space character without a literal space character in the program; thus we form a range starting at ASCII 31, and catch the space as the second element of the range.) Obviously, if we're producing some characters via transliteration, we have to sacrifice characters to transliterate them from, but uppercase letters aren't very useful and it's much easier to write without access to those than without access to punctuation marks.

Here's the alphabet of punctuation marks that become available as a result of the glob (the upper line shows the encoding, the lower line the character it encodes):

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

Most notably, we have a bunch of punctuation marks that aren't glob-safe but are useful in writing Perl programs, together with the space character. I also saved two uppercase letters, the literal A and Z (which encode not to themselves, but to T and U, because A was needed as an upper as well as a lower range endpoint); this allows us to write the transliteration instruction itself using the new encoded character set (although uppercase letters aren't that useful, they're useful in specifying changes to the uppercase letters). The most notable characters that we don't have available are [, \, and ], but none are needed (when I needed a newline in the output, I produced it using the implicit newline from say rather than needing to write \n; chr 10 would also have worked but is more verbose).

As usual, we need to worry about what happens if the inside of the wrapper gets damaged outside the string literal. A corrupted eval will prevent anything running; we're fine with that. If the quote marks get damaged, the inside of the string isn't valid Perl, and thus the wrapper will catch it (and the numerous subtractions on strings mean that even if you could make it valid Perl, it'd do nothing, which is an acceptable outcome). Damage to the transliteration, if it isn't a syntax error, will mangle the string being evaluated, typically causing it to become a syntax error; I'm not 100% sure there are no cases in which this breaks, but I'm brute-forcing it at the moment to make sure, and it should be easy enough to fix if there are.

The encoded program

Looking inside the string literal, reversing the encoding I used, and adding whitespace to make it more readable, we get this (again, imagine a control-underscore before the -+, which is encoded as 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

People who are used to quines will recognise this general structure. The most crucial part is at the start, where we verify that $o is undamaged; if characters have been deleted, its length won't match 181, so we run zzzz((())) which, if it isn't a syntax error due to unmatched brackets, will be a runtime error even if you delete any three characters, because none of zzzz, zzz, zz, and z is a function, and there's no way to prevent it parsing as a function other than deleting ((( and causing an obvous syntax error. The check itself is also immune to damage; the || can be damaged to | but that will cause the zzzz((())) call to run unconditionally; damaging variables or constants will cause a mismatch because you're comparing one of 0, 180, 179, 178 for equality to some subset of the digits of 181; and removing one = will cause a parse failure, and two = to inevitably cause the LHS to evaluate to either integer 0 or a null string, both of which are falsey.

Update: This check was slightly wrong in a preceding version of the program, so I had to edit it to fix the issue. The previous version looked like this after the decode:

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

and it was possible to delete the first three punctuation marks to get this:

lengtho179||zzz((()))

lengtho179, being a bareword, is truthy and thus breaks the check. I fixed this by adding an extra two B characters (which encode space characters), meaning the latest version of the quine does this:

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

Now it's impossible to hide both the = signs and the $ sign without producing a syntax error. (I had to add two spaces rather than one because a length of 180 would put a literal 0 character into the source, which could be abused in this context to integer-compare zero with a bareword, which succeeds.) End update

Once the length check passes, we know the copy is undamaged, at least in terms of character deletions from it, so it's all just straightforward quining from there (substitutions of punctuation marks due to a corrupted decoding table wouldn't be caught with this check, but I've already verified via brute-forcing that no three deletions from only the decoding table break the quine; presumably most of them cause syntax errors). We have $o in a variable already, so all we need to do is hardcode the outside wrappers (with some small degree of compression; I didn't skip out the part of the question entirely). One trick is that we store the bulk of the encoding table in $r; we can either print it literally in order to generate the encoding table section of the inside wrapper, or concatenate some code around it and eval it in order to run the decoding process in reverse (allowing us to figure out what the encoded version of $o is, having only the decoded version available at this point).

Finally, if we were an intact copy and thus could output the entire original program, we call exit in order to prevent the other copies also trying to print the program out.

Verification script

Not very pretty, but posting it because someone asked. I ran this several times with a variety of settings (typically changing $min and $max to check for various areas of interest); it wasn't a fully automated process. It has a tendency to stop running due to heavy CPU load elsewhere; when this happened, I just changed $min to the first value of $x that wasn't fully checked and continued running the script (thus ensuring that all the programs in the range got checked eventually). I only checked deletions from the first copy of the program, because it's fairly obvious that deletions from the other copies can't do more.

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

user62131

Posted 2015-09-08T02:05:09.723

Reputation:

3The OP is a little ambiguous; but I thought the score would be (1116*1116)/3. – Greg Martin – 2016-11-23T06:19:05.283

@GregMartin: (1116*1116)/3 is 415152, so this entry would still be winning under those circumstances. However, I don't think the OP can mean that because it'd give very little incentive to be able to handle the removal of multiple characters. This quine can be less than half the length if you only need it to handle one character removal; that would give me an effective ÷ 4 on the score if we interpret it like that, which would outweigh the ÷ 3 that I get from n=3, and thus mean that the less interesting n=1 entries are actually better scoring. – None – 2016-11-23T17:26:05.433

2I made the scoring clearer. Anyways, this is absolutely incredible; didn't think anyone would get n > 1. – takra – 2016-11-25T03:18:01.963

1It says something about Perl that it's easier to write a program without letters than without punctuation – Robert Fraser – 2017-05-31T18:32:05.433

35

Befunge-98, 884, n = 14, score ≈ 2.636

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

Try it online!

This doesn't just work when you remove exactly 14 characters, but even when you remove any amount up to 14 characters.

n = 14 may seem like a very arbitrary choice, but the technique I used can in fact only be used for radiation-hardening orders from 1 to 14, but not easily beyond that (it might be possible but I have no clue how). The order-1 quine is merely 73 bytes (although it employs some golfing tricks that don't apply to larger n):

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

Explanation

When I was working on this answer I found that it's possible to set the instruction pointer's delta to (2,0) under radiation-hardened conditions with the following snippet:

20020xx

See that answer for why this works. I found this just with a bit of fiddling by hand, but this raised the question whether there are similar patterns which are robust under removal of multiple characters. So I wrote a short Mathematica script to search for these by brute force:

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

This very quickly revealed a pattern. To get a corresponding snippet which works for removal of up to n characters, you can use (m0x){n}m0 where m is n+1 and x is either m or 0. So all of the following would work for removal of up to two characters:

30030030
30030330
30330030
30330330

I'm sure it's possible to prove this, but I've simply verified for n up to 7. Of course, this only works as long as we can represent n+1 as a single digit, and the largest such digit in Befunge 98 is f which represents 15. That's why my approach is limited to n = 14. If someone finds a way to reliably set the delta to larger n+1, it would likely be possible to increase the order of this radiation-hardened quine indefinitely.

Let's look at the actual code. There are basically two parts to it. First we set the delta to (15,0) as I just mentioned:

f00f00f00f00f00f00f00f00f00f00f00f00f00f00f0xxxxxxxxxxxxxxx

And the remainder of the code has each command repeated 15 times and prints the source. If we remove the repetition, it looks like this:

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

The " is a standard 2D quining technique: it starts string mode, pushes all the characters (except itself) onto the stack and ends string mode again after wrapping around. This helps us get all the code points of the second half, but it will fail to get us anything useful from the first half, because throughout the f00f00...f0 bit, it will only record two characters (which may be either f or 0 depending on which characters are deleted). But since that part isn't made up of characters being repeated 15 times, we'll need to print it separately anyway.

More conveniently, in the unmodified quine, the length of the string before the " is -1 (mod 15). This guarantees that no matter how many characters (up to 14) we remove, that the number of characters recorded there is always 3 (one x and two of f and 0). This is actually true for any radiation order up to 14.

We now start by printing the f00f00...f0 part:

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.

The next 3k$ simply discards that 0 as well as the three characters that were pushed by " from the beginning of the program. The stack now contains only the characters after the " as well as some junk underneath from the original f00f00...f0 depending on which characters were deleted.

Now we just need to reverse the top of the stack (containing the remaining characters) and print each one of them 15 times.

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

And that's it. :)

Martin Ender

Posted 2015-09-08T02:05:09.723

Reputation: 184 808

16

JavaScript (ES6), 927 bytes, n=1, score=859329

note: don't use a REPL (like a browser console) to run this.

This was written before code length was a factor, so it isn't golfed yet.

This was very difficult, and deserves a thorough explanation. Which I will write later, after I explore this challenge a little more!

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

note: there is a trailing newline

Basically, the first line is carefully constructed to rename all 'mispellings' of setTimeout to valid functions, so that if a character is removed from one of the setTimeouts the code will not error out and the unscathed version can run. It is also written so that if any one character is removed from the first line, there will be no error and the rest of the code can run unaffected.

The second and third blocks are exactly equivalent. If one runs to completion, it sets the _ variable so that the other knows not to duplicate the quine. If one of these blocks errors out, it does not affect the other block because it was called asynchronously using setTimeout. The error will cause _ not to be set, so the other block will quine successfully. The main code is in a string, which is checked for length in each block to make sure that there were no removals.

Template strings on the next line with some comments at the end of the template strings protects the code from erroring out if one of the backticks forming the template string is removed. If the ending backtick is removed, the template string is ended by the backtick in the comment. If the starting backtick is removed, setTimeout is evaluated as an unassigned function (a no-op) and the code runs as normal, without a setTimeout. The ending backtick is nullified by another comment.


What's that you say? You want to try it out? Say no more!

Full page mode recommended.

Ignore the input box, this snippet takes no input.

Try removing any one character from the code!

Don't believe it? Normal code will still work (but it won't quine...) Try something like console.log(5)!

note: the snippet had to be modified slightly to disable the REPL feature, so I removed multiple output capabilities for this answer only.

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>

A better explanation is forthcoming. In the meantime feel free to ping me in chat @jrich with any comments/questions/criticisms!

jrich

Posted 2015-09-08T02:05:09.723

Reputation: 3 898

ah okay, whoops :| – Downgoat – 2016-02-21T18:05:31.733

4Use this instead of window. – Mama Fun Roll – 2016-02-21T23:10:54.560

3+1 for including the meaning of life in your source code – Cyoce – 2016-11-27T03:52:36.887