in reply to camel code

And at the risk of spoiling the fun by dissecting the camel, here's my attempt at describing what's going on here, because it's a pretty neat trick, and may encourage other ObWriters to experiment on their own. (People who don't want the fun spoiled should tune out now...)

The program basically reads its own source code, and prints out a smaller version of the Camel picture, by reducing the image by 50%. It does this by splitting the code up into 2*2 blocks of characters, figuring out which characters in this block are non-whitespace, and printing out a corresponding character to display the picture. Characters range from a " " (for a 4x4 block of spaces) to "X" (for a 4x4 block of non-whitespace).

The picture is created, stored in an array, and printed out twice (the strings are reversed during the first print, so that the 2nd set of camels are facing the other way). After the picture printout, the O'Reilly disclaimer is printed out.

Here's my annotated version of the code. Note that, since the formatting has been removed and comments added, this code will not work the same as the original. It's just meant for a study aid.

#!/usr/bin/perl -w # camel code use strict; $_=' eval("seek\040DATA,0,0;"); # clever way of seeking to beginning + of program file! foreach (1..3) { # skip the first 3 lines (shebang, use str +ict, and a blank line) <DATA>; } my @camel1hump; # variable def's my $camel; my $Camel; # for each line read, grab the following line (if there is another # one to read), and go through the individual characters, detectin +g # whitespace and nonwhitespace characters. # # If you think of the image as being divided into 2x2 character # blocks, then each char in the block will be assigned a power of # 2 thusly: # # +---+---+ # | 8 | 4 | # +---+---+ # | 2 | 1 | # +---+---+ # # For each block, this while block will add up the corresponding # numbers for each nonwhiteapce char, resulting in a number stored # in $camel1hump from 0 to 15. This number will then be translated # to a ASCII picture character from ' ' (for a value of 0, # indicating 4 whitespace characters) to 'X' (for a value of 15, # indicating 4 nonwhitespace chars). These picture chars will be # stored in the string $camel, to be printed out in a fancy way # below. while (<DATA>) { $_ = sprintf("%-69s", $_); # format string in 69 columns my @dromedary1 = split(//); # split $_ into individual char +s if (defined($_ = <DATA>)) { # get next line and split, @camel1hump=split(//); # if it exists } while (@dromedary1) { # for each char my $camel1hump = 0; # this var will hold the bits my $CAMEL = 3; # this var is the bit # if (defined($_ = shift(@dromedary1)) && /\S/) { # if the n +ext char is a nonspace, $camel1hump += 1<<$CAMEL; # add 1<<3 (8) to our + bit counter } $CAMEL--; # next bit... if (defined($_ = shift(@dromedary1)) && /\S/) { # if th +e next char is a nonspace, $camel1hump += 1<<$CAMEL; # add 1<<2 (4) to our + bit counter } $CAMEL--; # next bit... if(defined($_=shift(@camel1hump)) && /\S/) { # if the n +ext char is a nonspace, $camel1hump += 1<<$CAMEL; # add 1<<1 (2) to our + bit counter } $CAMEL--; # next bit... if(defined($_ = shift(@camel1hump)) && /\S/) { # if the + next char is a nonspace, $camel1hump+=1<<$CAMEL; # add 1<<0 (1) to o +ur bit counter ; } # pick out the proper corresponding text format char for t +he # added-up bits. This could be non-obfuscated as a substr( +) # call. $camel .= (split(//,"\040..m`{/J\047\134}L^7FX"))[$camel1h +ump]; } $camel .= "\n"; } @camel1hump = split(/\n/,$camel); # split the picture into indi +vidual lines # Now we print the picture out. The same block of code is included # twice in the program. The reason the second set of cames is reve +rsed # is because the reverse() function call actually does reverse the + # string in the @camel1hump array, so the second time the strings +are # printed, we get a flipped picture. foreach (@camel1hump) { chomp; $Camel=$_; # a bit of obfuscation which just translates 8 of the formatti +ng chars # to the digits "1" through "8", and then back again. y/LJF7\173\175`\047/\061\062\063\064\065\066\067\070/; y/12345678/JL7F\175\173\047`/; $_=reverse; # reverse the string in $_ (which reverses +it in the @camel1hump array) print"$_\040$Camel\n"; # and print out reversed string, alo +ng with the $Camel copy. } # and do basically the same thing again. Since the strings were re +versed in the preceding # block, they're printed out as flipped images of the camel. foreach (@camel1hump) { chomp; $Camel=$_; y/LJF7\173\175`\047/12345678/; y/12345678/JL7F\175\173\0 47`/; $_=reverse; print "\040$_$Camel\n"; } '; # and that's the end of $_. Whew. ; s/\s*//g; # remove all spacing from $_ ; eval; # display the camels eval("seek\040DATA,0,0;"); # seek back to the beginning of program undef$/; $_=<DATA>; # slurp in all the program into $_ s/\s*//g; # remove space ( ); ; s;^.*_;;; # remove all characters until the final "_", which will b +e the last underscore in "__DATA__" # Now print out the O'Reilly disclaimer. The chars in __DATA__ # are just the ASCII values, coded in octal. (And you thought # the picture data was there!) map { # this map statement eval "print\"$_\""; # prints out each octal character } # that it finds /.{4}/g; # using this pattern match. __DATA__ \124\150\145\040\165\163\145\040\157\146 \040\141\040\143\141\155\145\154\040\151 \155\141\147\145\040\151\156\040\141\163 \163\157\143\151\141\164\151\157\156\040 \167\151\164\150\040\120\145\162\154\040 \151\163\040\141\040\164\162\141\144\145 \155\141\162\153\040\157\146\040\117\047 \122\145\151\154\154\171\040\046\012\101 \163\163\157\143\151\141\164\145\163\054 \040\111\156\143\056\040\125\163\145\144 \040\167\151\164\150\040\160\145\162\155 \151\163\163\151\157\156\056
Whew. Is it lunchtime yet?

Update: Thanks to Erudil for pointing out that the y/// lines, which I thought were purely for obfuscatory (ooh!) purposes, actually serve a purpose. Namely, to translate non-symmetrical characters to their mirror image. "L" and "J" are exchanged, "F" and "7" are exchanged, etc. (Take a close look at the ordering of the characters in those two y/// lines.)

As an example of why this needs to be done, look at the camels' heads:

  .mm.           .mm.
 .XXX^XLmm   mmJX^XXX.

See those "L"s and "J"s? If they hadn't been switched with the y/// code, the picture wouldn't have the dark portions in the right areas, and the picture wouldn't be a mirror image:

  .mm.           .mm.
 .XXX^XLmm   mmLX^XXX.