This is a special JAPH. It's special because it was the first one I ever made. It's essentially a variant on the first two, but I'm still proud of it. Let's check it out.
@;=split //,'0;oP2S@!\0cP4D9&V@hA#a;(n Just another Perl hacke ' ;foreach (@;) {pop @;;print chr ((@;. (100 +ord ))% 86+32)}
Right away, your eyes are drawn to-
wait a minute
Now that I've said I made it, it sounds like I'm being really smug about how great it is. Just try to forget that it's mine.
Anyway, right away we see the JAPH text, but something's wrong - it's missing an r. So we might want to treat this as just a red herring, meant to throw us off the real trail.
Let's try rearranging the code, keeping in mind that ;
ends a line and .
concatenates strings (don't be confused by @;
, it's just an array named ;
).
@; =split //,'0;oP2S@!\0cP4D9&V@hA#a;(n Just another Perl Hacke' ;foreach (@;) {pop @;;print chr ((@; . (100 +ord ))%86 + 32) }
That's much cleaner, but now the output is:
j?=>@+89>2/<@p/<6@2+-5/<
Yeesh. What went wrong? Well, when we rearranged the code, we accidentally removed a character -- the newline after line 2. Somehow, that dramatically altered the output. Let's look at what's actually going on here.
The first line uses
, which splits a string into an array using a specified delimiter. Here, there is no delimiter specified between the slashes, so the string is simply being converted to an array.
Next we have a foreach loop that operates on the array
operates on arrays, applying the loop statements to successive elements. There is no loop variable specified, so $_
is used by default. $_
will be set to sequential values of the array @;
as the loop iterates (i.e.
,
,
, etc.).
The first statement of the for loop is
, which returns the last value of an array and then removes it. So each time we go through the loop, the array becomes smaller. This reveals quite a bit about the nature of the JAPH. If we examine the initial string, we see that it is 50 characters long (don't forget the newline), so only 25 characters will be processed before the loop ends. We also see that our suspected red herring is in the latter half of the string, so it will be popped off before it reaches the loop.
Finally we get to the real meat of the JAPH. The
function is relatively straightforward; it converts ASCII values to their corresponding characters. Inside the parentheses, @;
is being appended to 100 +
. In a string context, @;
simply returns its own size, so as the loop iterates it will equal 50, 49, 48, etc.
is the inverse of
; it returns the ASCII value of a character. I don't think I have to explain what variable it's operating on. The result of the concatenation is then modulated by 86 and added to 32, and the final number is converted to a character and printed.
These numbers may seem arbitrary, but they were all chosen for a reason. 32 serves as the lower bound because it was the lowest ASCII value required (a space). Similarly, 86 + 32 was the upper bound because I knew the highest value would be 118 (u
). And 100 had to be added to
because of a problem introduced by the concatenation: after c, the ASCII values become 3 digits instead of 2, so the result of the concatenation jumps from 5099 to 50100. This meant that some target values were unreachable, i.e. there was no character that could be used in the string that would result in the proper output. Preemptively adding 100 meant there there was never a large jump.
I'll be honest: after setting up the formula, I essentially sat down and brute-forced my way through creating the gibberish string. There are worse ways to spend an hour.