Fault-Tolerant Hello World (a.k.a. the Interview)

68

17

At the end of your interview, the Evil Interviewer tells you, "We make all of our applicants take a short coding test, to see if they really know what they are talking about. Don't worry; it's easy. And if you create a working program, I'll offer you the job immediately." He gestures for you to sit down at a nearby computer. "All you have to do is create a working Hello World program. But"--and he grins broadly--"there's a catch. Unfortunately the only compiler we have on this machine has a small bug. It randomly deletes one character from the source code file before compiling. Ok, see you in five minutes!" And he walks out of the room, whistling happily.

Can you guarantee that you will get the job?

The Task

Write a program that will print Hello, world! to standard output even after a single character is removed from any position in the file. Or come as close to this as possible.

The Rules

No Extraneous Output - Hello, world! must be the only substantive thing printed to standard output. It is ok to include other characters if they are naturally produced by your language of choice--such as a trailing newline or even something like [1] "Hello, world!" (for example if you were using R), but it must print the exact same thing every time. It cannot print Hello, world!Hello, world! or Hello world!" && x==1 some of the time, for example. Warnings, however, are allowed.

Testing In order to test determine your score, you have to test each possible permutation of the program: test it with each character removed, and see if it produces the correct output. I have included a simple Perl program for this purpose below, which should work for many languages. If it doesn't work for you, please create a test program and include it in your answer.

Scoring Your score is the number of times your program fails. In other words, the number of individual positions in your file where deleting a character prevents your program from working. Lowest score wins. In the event of a tie, the shortest code wins.

Trivial Solutions such as "Hello, world!" in several languages (score of 15) are acceptable, but they aren't going to win. I have at least found a Perl solution with a score of 4, which I will post eventually.

Update: The official winner will use a Turing-complete programming language and will not use any predefined mechanism that prints Hello, world!. Any external resource (other than standard libraries for your language) that is used is considered part of your program and subject to the same 1-character deletion. These requirements were stuck to the desk on a post-it note. Apologies if you didn't see them at first.

Update 2: Yes, your program has to actually accomplish the task described above in order to receive a score! Meaning it should successfully print Hello, world! at least once. This should have been obvious. Command-line switches and other settings that add functionality also count as part of your program and are subject to the single character deletion. The program must accomplish its task without any user input. A failure to compile counts in your failure count.

Happy programming, and may you get the job. But if you fail, you probably didn't want to work for that evil boss anyway.

Perl test script:

use warnings;
use strict;

my $program   = 'test.pl';
my $temp_file = 'corrupt.pl';
my $command = "perl -X $temp_file"; #Disabled warnings for cleaner output.
my $expected_result = "Hello, world!";

open my $in,'<',$program or die $!;
local $/;           #Undef the line separator   
my $code = <$in>;   #Read the entire file in.

my $fails = 0;
for my $omit_pos (0..length($code)-1)
{
    my $corrupt = $code;
    $corrupt =~ s/^.{$omit_pos}\K.//s;  #Delete a single character

    open my $out,'>',$temp_file or die $!;
    print {$out} $corrupt;  #Write the corrupt program to a file
    close $out;

    my $result = `$command`;    #Execute system command.
    if ($result ne $expected_result)
    { 
        $fails++;
        print "Failure $fails:\nResult: ($result)\n$corrupt";
    }
}

print "\n$fails failed out of " . length $code;

user7486

Posted 2013-04-17T20:10:26.347

Reputation:

1Can the deleted character result in the program not compiling? Is that still counted as not working? – lochok – 2013-04-18T07:06:48.320

@lochok, yes, that would count as a failure. Any deleted character which leads to Hello, World! not being printed is a failure. – None – 2013-04-18T07:09:54.383

@Walkerneo, thanks! I searched for similar questions and didn't find that one. I think this is significantly different, though. In particular, that question guarantees that only modifications resulting in syntactically valid code have to be handled. – None – 2013-04-18T08:33:47.440

The Perl test script should be subject to the same 1-character deletion – xDaizu – 2017-07-03T08:45:11.433

Could you give an example of "command-line switches and other settings that add functionality"? Do you mean switches and settings given to the program itself, or using switches and settings within the build environment? – CasaDeRobison – 2014-02-18T07:25:38.407

Answers

135

Befunge, Score 0

I think I cracked it - no single character deletion will change the output.
Deleting any character from line 1 changes nothing - it still goes down at the same place.
Lines 2 and 3 are redundant. Normally line 2 is executed, but if you delete a character from it, the < is missed, and line 3 takes charge.
Deleting newlines doesn't break it either (it did break my previous version).
No test program, sorry.

EDIT: simplified a lot.

                              vv
@,,,,,,,,,,,,,"Hello, world!"<<
@,,,,,,,,,,,,,"Hello, world!"<<

A short explanation of the flow:

  1. Befunge starts executing from top-left, moving right. Spaces do nothing.
  2. v turns the execution flow downward, so it goes down one line.
  3. < turns the execution flow left, so it reads line 2 in reversed order.
  4. "Hello, world!" pushes the string to the stack. It's pushed in reversed order, because we're executing right to left.
  5. , pops a character and prints it. The last character pushed is printed first, which reverses the string once more.
  6. @ terminates the program.

ugoren

Posted 2013-04-17T20:10:26.347

Reputation: 16 527

2+1, great solution. I don't think a test program is needed, because it is obvious that there is only a small number of potentially significant deletions. – None – 2013-04-18T12:14:05.057

4So easy to verify and no discussion involved. Genius! – Karma Fusebox – 2013-04-19T22:51:27.647

Congratulations on an excellent solution. – None – 2013-04-29T07:39:55.790

2

I know this isn't [tag:code-golf], but here's a version in 66 bytes.

– MD XF – 2018-02-28T00:26:19.723

45

Perl, Score 0

(147 characters)

Here is my solution, which I managed to get from 4 down to 0:

eval +qq(;\$_="Hello, world!";;*a=print()if length==13or!m/./#)||
+eval +qq(;\$_="Hello, world!";;print()if*a!~/1/||!m/./#)##)||
print"Hello, world!"

It must appear all on one line to work; line breaks are for "readability" only.

It benefits from Perl's pathologically permissive syntax. Some highlights:

  • Barewords that are not recognized are treated as strings. So when eval becomes evl, that is not an error if a string is permissible at that point.
  • Unary + operator, which does nothing other than disambiguate syntax in certain situations. This is useful with the above, because function +argument (where + is unary) becomes string + argument (addition) when a function name is mangled and becomes a string.
  • Many ways to declare strings: a double quoted string qq( ) can become a single-quoted string q(); a string delimited by parentheses qq(; ... ) can become a string delimited by a semicolon qq; ... ;. # inside strings can eliminate balancing issues by converting things to comments.

The length of this can probably be reduced somewhat, though I doubt ugoren's solution can be beaten.

user7486

Posted 2013-04-17T20:10:26.347

Reputation:

13+1 for line breaks are for readability only ... – Bakuriu – 2013-04-24T14:47:28.660

42

HQ9+

This will never fail to produce the intended result when a character is deleted so gets a score of zero.

HH

When do I start?

skeevey

Posted 2013-04-17T20:10:26.347

Reputation: 4 139

1It produces "hello, world" though, not "Hello, world!" as specified. – Paul R – 2013-04-17T21:17:52.677

16Nonsense, the language was just incorrectly specified on the esolang wiki ;) – skeevey – 2013-04-17T21:20:39.103

11Good for you if they have an HQ9+ compiler on the interview computer... :) – None – 2013-04-18T04:41:47.897

2

@mbomb007 This interpreter prints Hello, world! for the H command.

– Dennis – 2016-12-14T01:28:30.760

You could write your own buggy compiler. – PyRulez – 2014-02-19T20:50:10.057

14

Befunge-98, score 0, 45 bytes

20020xx""!!ddllrrooww  ,,oolllleeHH""cckk,,@@

Try it online!

Although an optimal solution has already been found (and there's no tie breaker), I thought I'd show that this can be simplified considerably with Befunge 98.

Explanation

The 20020xx reliably sets the delta (the steps of the instruction pointer between ticks) to (2,0) so that starting at the first x, only every other command is executed. See this answer for a detailed explanation of why this works. Afterwards, the code is merely:

"!dlrow ,olleH"ck,@

We first push all the relevant character codes onto the stack with "!dlrow ,olleH". Then ck, means print the top of the stack (,), 13 (c plus 1) times (k). @ terminates the program.

Martin Ender

Posted 2013-04-17T20:10:26.347

Reputation: 184 808

11

J, 7 points

Selecting every letter with odd position:

   _2{.\'HHeellllo,,  wwoorrlldd!!'
Hello, world!

randomra

Posted 2013-04-17T20:10:26.347

Reputation: 19 909

5The same thing in golfscript would be 4 points: 'HHeelllloo,, wwoorrlldd!!'2% – cardboard_box – 2013-04-18T00:07:32.320

@cardboard_box Feel free to submit it. :) – randomra – 2013-04-18T10:04:26.227

Upvoting because I understand it, and it doesn't feel like cheating. – Michael Stern – 2014-04-17T16:43:10.103

3

Befunge-93, Score 0 (63 bytes)

I know this isn't a code-golf challenge, but I thought it would be interesting to see if the existing Befunge-93 solution could be improved upon in terms of size. I had originally developed this technique for use in the similar Error 404 challenge, but the need for a wrapping payload in that case made the 3 line solution more optimal.

This isn't quite as good as Martin's Befunge-98 answer, but it's still a fairly significant reduction on the winning Befunge-93 solution.

<>>  "!dlrow ,olleH">:#,_@  vv
  ^^@_,#!>#:<"Hello, world!"<<>#

Try it online!

Explanation

There are two versions of the payload. For an unaltered program, the first < causes the program to execute right to left, wrapping around to the end of the line, until it reaches a v directing it down to the second line, and a < directing it left into the left-to-right version of the payload.

An error on the second line causes the final < to be shifted left and replaced with a > directing the flow right instead. The # (bridge) command has nothing to jump over, so the code just continues until it wraps around and reaches a ^ at the start of the line, directing it up to the first line, and then a > directing it right into the right-to-left payload.

Most errors on the first line just cause the final v commands to shift across by one, but that doesn't alter the main flow of the code. Deleting the first < is slightly different, though - in that case the execution path just flows directly into the left-to-right payload on the first line.

The other special case is the removal of the line break. When the code wraps around to the end of the line, in this case it's now the end of the what used to be the second line. When it encounters the # command from the right, this jumps over the > and thus continues directly into the right-to-left payload.

In case there's any doubt, I've also tested with the perl script and it confirmed that "0 failed out of 63".

James Holderness

Posted 2013-04-17T20:10:26.347

Reputation: 8 298

3

Unary, score 0, 74817134662624094502265949627214372599829 bytes

The code is not included due to post length limitations, but it consists of 74817134662624094502265949627214372599829 zeros.

If any of these zeros is removed, the resulting brainfuck program is KSab's hello world program.

Sadly, I didn't get the job because my machine had only 500 GB of hard disk space.

Black Owl Kai

Posted 2013-04-17T20:10:26.347

Reputation: 980

0

Gol><>, score 0, 38 bytes

<<H"Hello, world!"/
H"Hello, world!" <

The language is released after the challenge.

jimmy23013

Posted 2013-04-17T20:10:26.347

Reputation: 34 042

0

8086 machine code (MS-DOS .COM), 71 bytes, score = 0

Slight variation on previous answers on the same theme.

In binary form:

00000000 : EB 28 28 E8 00 00 5A 83 C2 09 B4 09 CD 21 C3 48 : .((...Z......!.H
00000010 : 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 24 90 90 90 : ello, World!$...
00000020 : 90 90 90 90 90 90 90 90 90 90 EB D7 D7 E8 00 00 : ................
00000030 : 5A 83 C2 09 B4 09 CD 21 C3 48 65 6C 6C 6F 2C 20 : Z......!.Hello, 
00000040 : 57 6F 72 6C 64 21 24                            : World!$

In readable form (NASM syntax):

cpu 8086
org 0x100

%macro CODEPART 0
    call near .nextline
.nextline:
    pop dx
    add dx, 0x09
    mov ah, 0x09
    int 0x21
    ret

.msg        db "Hello, World!$"
%endmacro

jump1:
    jmp jump2
    db 0x28

part1:
    CODEPART

filler:
    times 13    db 0x90

jump2:
    jmp part1
    db 0xd7

part2:
    CODEPART

Rundown

The active part is duplicated so that there is always one untouched by radiation. We select the healthy version by way of jumps. Each jump is a short jump, and so is only two bytes long, where the second byte is the displacement (i.e. distance to jump, with sign determining direction).

We can divide the code into four parts which could be irradiated: jump 1, code 1, jump 2, and code 2. The idea is to make sure a clean code part is always used. If one of the code parts is irradiated, the other must be chosen, but if one of the jumps is irradiated, both code parts will be clean, so it will not matter which one is chosen.

The reason for having two jump parts is to detect irradiation in the first part by jumping over it. If the first code part is irradiated, it means we will arrive one byte off the mark. If we make sure that such a botched landing selects code 2, and a proper landing selects code 1, we're golden.

For both jumps, we duplicate the displacement byte, making each jump part 3 bytes long. This ensures that irradiation in one of the two last bytes will still make the jump valid. Irradiation in the first byte will stop the jump from happening at all, since the last two bytes will form a completely different instruction.

Take the first jump:

EB 28 28        jmp +0x28 / db 0x28

If either of the 0x28 bytes are removed, it will still jump to the same place. If the 0xEB byte is removed, we will instead end up with

28 28           sub [bx + si], ch

which is a benign instruction on MS-DOS (other flavours might disagree), and then we fall through to code 1, which must be clean, since the damage was in jump 1.

If the jump is taken, we land at the second jump:

EB D7 D7        jmp -0x29 / db 0xd7

If this byte sequence is intact, and we land right on the mark, that means that code 1 was clean, and this instruction jumps back to that part. The duplicated displacement byte guarantees this, even if it is one of these displacement bytes that were damaged. If we either land one byte off (because of a damaged code 1 or jump 1) or the 0xEB byte is the damaged one, the two remaining bytes will also here be benign:

D7 D7           xlatb / xlatb

Whichever the case, if we end up executing those two instructions, we know that either jump 1, code 1, or jump 2 were irradiated, which makes a fall-through to code 2 safe.

Testing

The following program takes the COM filename as an argument, and creates all variants of it, along with a file called tester.bat. When run in DOS, this BAT file will run each of the created binaries, redirecting the outputs into one text file per program.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    FILE *fin, *fout, *fbat;
    int fsize;
    char *data;

    if (!(fin = fopen(argv[1], "rb")))
    {
        fprintf(stderr, "Could not open input file \"%s\".\n", argv[1]);
        exit(1);
    }

    if (!(fbat = fopen("tester.bat", "w")))
    {
        fprintf(stderr, "Could not create BAT test file.\n");
        exit(2);
    }

    fseek(fin, 0L, SEEK_END);
    fsize = ftell(fin);
    fseek(fin, 0L, SEEK_SET);

    if (!(data = malloc(fsize)))
    {
        fprintf(stderr, "Could not allocate memory.\n");
        exit(3);
    }

    fread(data, 1, fsize, fin);

    fprintf(fbat, "@echo off\n");

    for (int i = 0; i < fsize; i++)
    {
        char fname[512];

        sprintf(fname, "%02d.com", i);
        fprintf(fbat, "%s > %02d.txt\n", fname, i);

        fout = fopen(fname, "wb");

        fwrite(data, 1, i, fout);
        fwrite(data + i + 1, 1, fsize - i - 1, fout);

        fclose(fout);
    }

    free(data);
    fclose(fin);
    fclose(fbat);
}

DOSBox, where I tested this, does not have the fc command, so another BAT file was created to run under Windows. It compares each text file with the first one to make sure they are all identical:

@echo off

for %%x in (*.txt) do (
    fc %%x 00.txt > nul
    if ERRORLEVEL 1 echo Failed: %%x
)

Ocular inspection of one of the text files is still needed.

gastropner

Posted 2013-04-17T20:10:26.347

Reputation: 3 264