zsh, 603 594 566 561 548 440 415 399 378 370 bytes
ec
ho \\n;ca t<<<$'\x20';exi t
d$c -e8BC6P
d0c -eKp
$'\172\163\150' $'\055\143' $'\146\157\162 v \151\156 \173\043\056\056\134\175\175\073\173 \146\147\162\145\160 \055\161 $\166 '$0$'\174\174\074\074\074$\166\073\175'
$'\145v\141\154' $':\073\072\046\046\145\170\151\164';#%&()*+,/9=>?@ADEFGHIJLMNOQRSTUVWXYZ[]^_`jklmsuwy
0# $#;for b in {$..z};{ fgrep -q $b $0||<<<$b;}
Depends on coreutils + dc
.
Try it online!
That was... a journey.
This answer has three parts. The first 4 lines handle certain special cases to simplify the code that follows. The next 2 lines and the last line both accomplish essentially the same thing, but exactly one is run with any given character removal. They are written with mostly complementary character sets, so that removing any character breaks only one at most, allowing the other to continue to function.
Looking at the first part, we first handle
- newline removal with
ec\nho \\n
- space removal with
ca t<<<$'\x20'
(followed by exi t
to avoid running later code, which would result in extraneous output)
$
removal with d$c -e8BC6P
(8BC6
= 9226
is 36*256 + 10
, and 36 and 10 are the byte values of the $
and newline characters respectively; we use hex digits in decimal to avoid having to include them in the large comment in line 6)
0
removal with d0c -eKp
(K
gets the decimal precision, which is 0
by default)
In the next part, the only characters used (aside from the garbage at the end of the second line) are $'\01234567v;
, space, and newline. Of these, four have been accounted for, so the remainder ('\1234567v
) cannot occur in the last line. Expanding the octal escapes ($'\123'
represents the ASCII character with value 1238), we get:
zsh -c 'for v in {#..\}};{ fgrep -q $v '$0'||<<<$v;}'
eval ':;:&&exit'
The first line loops through all characters used in the program and searches for each one in its own source code ($0
is the filename of the script being run), printing any character that is not found.
The second line looks a little strange, and appears to do the same thing as exit
with a bunch of nops. However, encoding exit
as octal directly results in $'\145\170\151\164'
, which does not contain 2
or 3
. We actually need to make this less resilient to removals. This is because if any of '\014567v
are removed, breaking the first line, the second line also breaks, allowing the remainder of the code to execute. However, we need it to also break if 2
or 3
are removed so that lines 3 and 4 can run. This is accomplished by shoehorning in :
and ;
, which have a 2 and 3 in their octal representation respectively.
The junk at the end of line 2 is simply there to ensure every printable ASCII character appears at least once, as the way the checking is done by looping through each one requires this.
If exit
was not called in the first section (i.e. it was mangled by the removal of one of '\01234567v
), we move on to the second, in which we must accomplish the same thing without using any of these characters. The last line is similar to the decoded first line, except that we can contract the range of the loop to save a few bytes, because we already know that all characters except for '\01234567v
have been covered. It also has 0# $#
before it, which comments it out and prevents it from producing extraneous output if 0
or $
were removed.
4Since this challenge isn't tagged quine, may we read our own source code? – Dennis – 2018-09-05T22:56:51.970
1@Dennis Sure. Be my guest – Post Rock Garf Hunter – 2018-09-05T23:12:12.537
2If all the bytes in our program represent digits, may we output via exit code? – Mr. Xcoder – 2018-09-06T06:04:54.103
15I think this would be better as a code challenge where you have to maximise the number of discrete characters used. – Notts90 supports Monica – 2018-09-06T11:07:09.913
@tsh that’d be pretty interesting! – Notts90 supports Monica – 2018-09-06T11:27:55.723
2Should've specified more than 1 byte instead of non-empty :P. Or what Notts90 said. – Magic Octopus Urn – 2018-09-06T13:54:30.207
1@Notts90 Probably. The radiation hardening already makes the non-trivial answers quite hard. – user202729 – 2018-09-06T14:17:22.563
1@MagicOctopusUrn The votes agree with you... this is a case of code-golf with the 548 byte answer getting the lion's share of upvotes! – JayCe – 2018-09-06T15:38:23.033
1@MagicOctopusUrn More than 1 byte would mean that all the one byte answers would just be two bytes, e.g.
11
for retina. Unless you mean more than one distinct byte. Which would probably have improved the challenge. – Post Rock Garf Hunter – 2018-09-06T20:25:01.330Is output with a trailing newline acceptable? (If so, would the output when the newline character is removed have to be two newlines?) – Doorknob – 2018-09-06T23:07:06.133
There is no clear meta consensus on this, and in any case whether two newlines would be required if a newline was removed is a challenge-specific question (as it essentially amounts to "is it okay to only sometimes add a trailing newline). – Doorknob – 2018-09-06T23:37:03.910
@Doorknob Ok you can add an extra new line. – Post Rock Garf Hunter – 2018-09-06T23:39:22.247
Would the output when a newline is removed have to be two newlines, then, or would a single newline be acceptable (when all other outputs have trailing newlines)? – Doorknob – 2018-09-06T23:51:23.207
@Doorknob It should probably be 2. – Post Rock Garf Hunter – 2018-09-06T23:54:13.760