A write-up of hacker.org’s “Super Small Hello World” challenge: the solutions that worked, the ones that almost did, and the trick that finally reached 29.
hacker.org has a little esoteric language called
SuperHack. The task sounds trivial: write a SuperHack program that prints
Hello, World!.
The catch is the score. You are scored on the area of the bounding rectangle
of your code, and smaller is better. The program also has to finish properly:
by executing the ! (halt) instruction. Crashing with a VM exception once the
text is out does not count, and neither does printing stray bytes.
The number to beat was 29.
SuperHack is a 2-D stack machine, similar in spirit to Befunge:
%) and moves right.| Instruction | Effect |
|---|---|
0-9 | push a digit |
+ - d | add, subtract, floor-divide (d by zero gives 0) |
x | duplicate the top of the stack |
n ^ | push a copy of the item n below the top (1^ copies the second) |
, | read input. When there is no input, it pushes 0 |
g | pop x, pop y, push the character code at (x, y). Outside the grid it pushes 0 |
P | pop and print as a character (low 7 bits only) |
s | skip the next cell |
? | pop; skip the next cell if the value was 0 |
@ | push the current position (and direction) onto the call stack |
$ | pop a position, jump there, and continue two cells further on |
\ | mirror: turns “moving right” into “moving down” |
! | halt |
| anything else | no-op |
Two consequences shape everything that follows.
g reads the program’s own code, so the string
Hello, World! can simply sit in the grid and be read back one character at
a time. Most of its letters (H e l o W r) are no-ops when executed. Three
are not: , pushes 0, d divides, and ! halts.@ / $ make loops, and the loop count depends on where the @s are.
$ resumes two cells after the @ it returns to. That lands on the next
@, which pushes itself again, so a few @s can replay the loop body many
times:@@@@@ give Fibonacci counts. Five of them run the body
exactly 13 times.@s spaced two apart give powers of two. Four of them allow 16
passes.Thirteen characters, thirteen passes. Keep that in mind.
Area is width times height, and 29 is prime. So a 29-cell program has to be a single row or a single column.
Hello, World!%0@s!@@@@@ 01^gP1+$
The first real solution puts the payload at the front and starts execution after it:
| part | role |
|---|---|
Hello, World! | data, never executed, because the PC starts at the % |
% | entry point |
0 | index i = 0 |
@ s ! | halt gadget: see below |
@@@@@ | five adjacent trampolines, so the body runs 13 times |
| padding (explained below) |
0 1^ g | push row 0, copy i, then fetch the character in column i |
P | print it |
1+ | i += 1 |
$ | loop |
The halt gadget. The first @ puts its own position at the bottom of the
call stack, and s jumps over the !. The Fibonacci chain then does its 13
passes. When the call stack finally unwinds down to that first @, $ resumes
two cells after it, which is exactly the !. The program halts cleanly, and
there is no end-of-string test anywhere.
The padding. With adjacent @s, returning from the last @ resumes two
cells later, which skips the first cell after the chain. That cell must
therefore be something harmless, so it is a space.
Result: Hello, World!, clean halt, area 32. It is correct but roomy: the
payload is dead weight, and the %, the padding and the 0 together cost three
cells.
1H@e@l@l@o ,1 ^WgoPr2lsds!+$
The next idea is to stop spending cells on a separate payload and weave the
string through the code. The characters of Hello, World! sit in the odd
columns (1, 3, …, 25), and the code fills the even columns in between:
1 starts the index at column 1 (the H).@s spaced two apart (columns 2, 4, 6, 8) allow up to 16 passes., (column 11) pushes the 0 that g needs as its row.2+ steps the index to the next odd column.s cells jump over the payload’s d and !, so they don’t run as code.The problem is that 16 passes is more than 13. After printing the string, the
loop keeps fetching past the end and prints $ and two NUL bytes. Then $
finds the call stack empty and the VM dies:
output: "Hello, World!$\0\0"
message: call stack underflow
It is small, but it is not a solution. It does introduce the key idea:
payload characters that happen to be useful instructions (,) do double duty as
data and code.
\
2
H
@
e
@
l
@
l
@
o
x
,
g
P
W
2
o
+
r
$
l
d
!
(One character per line. The blank lines are single spaces, which are data.)
Turning the program on its side has an unexpected benefit. In a single column,
g needs the x-coordinate to be 0 and the row to be i. The payload’s ,
supplies that 0 for free, so the fetch shrinks to x , g: duplicate i, push
0, get. The cost is one \ at the top to turn the PC downwards.
It has the same flaw as solution 2, though. The loop overshoots, prints NUL
bytes, and ends in call stack underflow. At area 27 it is the smallest program
in this post, and it doesn’t count.
The lesson from solutions 2 and 3 is that stopping cleanly costs cells. Interleaving makes the loop cheap, but it gives you 16 passes when you need exactly 13.
\
2
H
@
e
@
l
@
l
@
o
x
,
g
P
W
x
o
5
r
d
l
5
d
?
!
2
+
$
Solution 4 keeps solution 3’s vertical interleave and adds an honest end-of-string test:
| row(s) | cells | role |
|---|---|---|
| 0 | \ | turn downwards |
| 1 | 2 | i = 2 |
| 3, 5, 7, 9 | @ | four trampolines, up to 16 passes |
| 11-13 | x , g | fetch row i (the , is payload) |
| 15 | P | |
| 17-24 | x 5 d 5 d | test value floor(floor(i/5)/5) = floor(i/25) (the last d is payload) |
| 25 | ? | if the test is 0, skip the next cell… |
| 26 | ! | …which is the payload’s own ! |
| 27-28 | 2 + | i += 2 |
| 29 | $ | loop |
The index runs 2, 4, …, 26. floor(i/25) stays 0 until the very last
character (i = 26), so the ? skips the halt on every pass except the last.
On the last pass, execution falls onto the payload !, which is simultaneously
the last character printed and the instruction that stops the program.
It is a lovely piece of reuse: the payload’s ,, d and ! all serve as code.
But the test costs five cells, and the total is 30.
At this point my AI companion tried to prove that 30 was optimal. Every approach seemed to run into the same wall:
@s, and an interleave has no adjacent code cells. So you are
stuck with 16 passes and a paid-for test.? can never sit
directly in front of $. The cell between them is always a payload cell.% or a large start index), because
running ...d! as code crashes or halts.Exhaustive machine searches of the smaller areas found nothing, and searches around solution 4’s layout at area 29 found nothing either. “30 is the floor” started to look like a theorem.
It wasn’t.
9@s!@@@@@Hello, WorlÀ‘1^gP1+$
The break came from rereading the judge’s source code. Two lines matter:
case 'P':
$output .= chr($this->Pop() & 0x7F); break;
...
default:
break; // every unknown character is a no-op
P prints only the low 7 bits. A byte with its top bit set, c + 128,
prints exactly like c.Every payload character therefore has two encodings. The plain one may do
something when executed. The c | 0x80 one is guaranteed inert. That is the
“formula for the data”: store the dangerous characters as c + 128, and let P
strip the high bit for free.
Only two payload characters are dangerous: d becomes Γ€ (0xE4) and !
becomes Β‘ (0xA1). With those two changed, the whole payload can be
executed. That removes the obstacle in the third bullet above.
Now take solution 1 and move the payload inside the loop:
| cols | cells | role |
|---|---|---|
| 0 | 9 | i = 9 (the column of H) |
| 1-3 | @ s ! | halt gadget, as in solution 1 |
| 4-8 | @@@@@ | five adjacent trampolines, so the body runs exactly 13 times |
| 9-21 | Hello, WorlÀ‘ | the payload, executed on every pass |
| 22-27 | 1 ^ g P 1 + | copy i, fetch, print, increment |
| 28 | $ | loop |
Walk through one pass:
@ chain and runs through the payload. H e l l o, the space and W o r l do nothing. The payload’s , pushes the 0
that g needs as its row. Γ€ and Β‘ do nothing.1^ copies i above that 0, and g fetches column i.P prints it. For the last two characters it prints 0xE4 & 0x7F = 'd' and
0xA1 & 0x7F = '!'.1+ advances i, and $ returns into the chain.Compared with solution 1, three cells vanish:
%: the payload no longer needs protecting from execution.H, a no-op.0 before 1^g: the payload’s , supplies it on every pass.32 β 3 = 29. After the 13th pass, the call stack unwinds to the gadget and
the program stops on the ! in column 3.
Output Hello, World!, no VM message, 271 cycles, area 29.
The first submission came back with got: 'Hello, WorlC$' and a score of 31.
The web form had posted UTF-8, which turned each special character into two
bytes (Γ€ β C3 A4, Β‘ β C2 A1). Masked to 7 bits, 0xC3 prints as C and
0xA4 as $.
The fix is to make the browser send Latin-1, where each of those characters is one byte. On the challenge page, I had to open the DevTools console and run:
document.querySelectorAll('form').forEach(
f => f.acceptCharset = 'ISO-8859-1');
document.querySelector('textarea').value =
"9@s!@@@@@Hello, Worl\u00e4\u00a11^gP1+$";
Then press the normal submit button. The judge prints Hello, World!, and the
King of the Hill score is 29.
| # | program | area | ends cleanly? |
|---|---|---|---|
| 1 | Hello, World!%0@s!@@@@@ 01^gP1+$ | 32 | β |
| 2 | 1H@e@l@l@o ,1 ^WgoPr2lsds!+$ | 28 | β extra bytes and call stack underflow |
| 3 | \2H@e@l@l@ox,g PW2o+r$ld! | 27 | β extra bytes and call stack underflow |
| 4 | \2H@e@l@l@ox,g PWxo5rdl5d?!2+$ | 30 | β |
| 5 | 9@s!@@@@@Hello, WorlÀ‘1^gP1+$ | 29 | β |
, as a free push 0, its ! as the halt,
and finally the entire payload as the loop body.& 0x7F and default: break) did what hours of search couldn’t.