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 7773 bytes (although it employs some golfing tricks that don't apply to larger n
):
200 20 xx""''dd22**xx""''ÈÈ..aa22**..33kk$$00{{''##uu''''!!uu''!!11++''xx##;;::!!kk@@::,,,,;;
Explanation
When I guess I'll startwas working on an explanation thenthis 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. :)