Hi killerhippo,
This Perl code has been deliberately structured to be difficult for a
human to read and comprehend, commonly known as obfuscated code.
obfuscate: To make so confused or opaque as to be difficult to
perceive or understand.
- The American Heritage Dictionary
http://dictionary.reference.com/search?q=obfuscated
Usually, writing obfuscated code is a bad practice, since it makes it
more difficult for code to be understood, debugged, and reused.
However, some programmers enjoy writing small programs of such
hard-to-read code as both a challenge to themselves and those reading
the code, and simply for fun.
The first obvious thing to do is run the code, and see what it does!
It prints the message "perl owns me" in a nifty way, spinning the
letters around the screen in a spiral pattern that lines them up twice
to show the message.
The best way to work through this code is to convert it into a more
readable form. The first step I took was to redo the line breaks to
match the actual lines of code:
#!/usr/bin/perl
## Perl Owns Me
$e="\e[";
for$r(0..62){
for(0..11){
($c,$y,$x)=(31&(620,5586,14053,496)
[$_/3]>>5*($_%3),(7-($r/5))*($_%3-1),(11-($r/3))*(int($_/3)-2));
print $_?"":$e."2J",$e,10-int(sin(3-$r/10)*$x-cos(3-$r/10)*$y),";",
40-int(cos(3-$r/10)*$x+sin(3-$r/10)*$y),H=>chr$c+($c?96:10)
}
select'','','',(30-$r)?.1:1
}
This gives us an assignment statement and two nesting for loops with
two very ugly looking lines inside. The first line is another
variable assignment, but is actually assigning values to three
different variables. So we can convert this:
($c,$y,$x)=(31&(620,5586,14053,496)
[$_/3]>>5*($_%3),(7-($r/5))*($_%3-1),(11-($r/3))*(int($_/3)-2));
to this:
$c = 31 & (620,5586,14053,496) [$_/3]>>5*($_%3);
$y = (7-($r/5)) * ($_%3-1);
$x = (11-($r/3)) * (int($_/3)-2);
and the code works in the exact same way. Our first guess, which
seems to be supported by the later print statement, is that $y and $x
are the screen co-ordinates where the current letter will be printed,
and $c represents what letter is being printed. The functions for $y
and $x depend on the variable $r, which is incrementing as part of the
outermost for loop.
Note that the variable $_ which is used is actually a special global
variable, which in this case was assigned by the innermost for loop.
Since the for loop didn't specify a variable, the loop value was
assigned to $_ by default.
So, glancing at the formulae for $y and $x, we can see how the
character positions change depending on both the current $r value
(which is basically a time value as the program steps forward through
the $r loop) and on which character is being printed (defined by the
innermost loop, assigned to the variable $_ ). Note that the
int($_/3) function within the $x formula simply converts the value of
$_ divided by three into an integer.
Looking at the assignment statement for $c, the value is generated by
doing a bitwise AND with 31 and a value from the list
(620,5586,14053,496). The list value used is indexed by [$_/3], and
then bitwise shifted right five times, and then multiplied by ($_%3).
Note that % is the modulo operator, not a division.
Somehow, this arcane formula of bit-shifting and arithmetic seems to
be generating values which will become the ASCII values for the
letters "perl owns me" in the print statement later. I don't have
time to step through the math and watch it work. Since the program
actually runs, I'm going to use the experimental evidence to support
this analysis. ;)
The print statement itself is a nightmare!
print $_?"":$e."2J",$e,10-int(sin(3-$r/10)*$x-cos(3-$r/10)*$y),";",
40-int(cos(3-$r/10)*$x+sin(3-$r/10)*$y),H=>chr$c+($c?96:10)
The print function takes a comma-separated list of things to be
printed to the screen, so let's visually split this statement apart by
the commas:
print $_?"":$e."2J",
$e,
10-int(sin(3-$r/10)*$x-cos(3-$r/10)*$y),
";",
40-int(cos(3-$r/10) * $x+sin(3-$r/10)*$y),
H=>chr$c+($c?96:10)
Recall that $e was assigned to a string value earlier. While this
still looks a bit cryptic, the quick explanation is that this print
command is using ANSI control characters to do cursor movement on the
screen, and then print the appropriate character. The $x and $y
values affect the cursor movement, and $c is used to generate the
character itself (using the chr function).
I don't have time to look up further details and pull apart the exact
workings of the last couple statements - if you still have any
specific (brief) questions on the code, you could mention it in a
clarification.
Thanks,
- josh_g-ga |