Syntactic Confectionery Delight PerlMonks

### Camel Time

by hiseldl (Priest)
 on Aug 15, 2002 at 05:33 UTC Need Help??

This camel keeps the time! It's a JAPH, a timepiece, and a really nice drawing of our beloved dromedary all rolled into one.

```#!perl  -w
use strict;
\$_='
\$,=\$/;\$|=@_
=split//,"Justan                othe
rPerl.hacker";\$/=\'\$~=         (local  tim
e)[\$.]*(\$.==0||\$.==1?1:5);s      ubstr(\$_,eval
\\\'\$^=\$~*2*atan2(1,1)/15;int(-  cos(\$^) *(\$=+
1)+11.5)*25+int(sin(\$^)*(\$=+1)+12.5)\\\',1,
\$_[\$=+(11,4,0)[\$.]])\';do{\$_=("."x287).
"#"."."x287;for\$=(0..10){\$.=0;eval\$/;
\$=<7&&(\$.=1,e         val\$/);\$=<4&
&(\$.=2,ev             al\$/);}p
rint(p               ack("
c3  ",                27,9
1,  72                ),(/
.{  25                }/g)
,s  ca                lar(
lo   cal              time)
);}   whil            e(slee

p(1))';s/\s+//g;eval
Download and run this one in a vt102 compatible terminal for best results. I tested it in cygwin rxvt and Linux xterm and it runs smoothly.

I started this one as a challenge to myself to learn more about the power of the Perl interpreter internals. I also kept snapshots of my working scripts so that I could share my experience with you, my fellow monks. The final script above is just obfuscated enough to be a pain in the butt for a novice to understand yet still entertain the seasoned Perl hacker. Let's start with the first non-obfuscated script...

```#!perl
\$|=1;
\$w = 25;
\$h = 25;
\$hm=pack"c3",27,91,72;
\$img="."x(\$w*\$h-1);
substr(\$img,11*\$w+12,1,"#"); # in the middle
\$,="\n";
@hour=split//,"Just";
@min =split//,"another";
@sec =split//,"Perl hacker";
while(1){
@t = localtime;
\$_=\$img;
for \$hidx(0..3) {
\$newx=newx(\$t[2]%12*30+180,0,2+\$hidx)+12;
\$newy=newy(\$t[2]%12*30+180,0,2+\$hidx)+11;
\$vx = sprintf("%d", \$newx);
\$vy = sprintf("%d", \$newy);
substr(\$_,\$vy*\$w+\$vx,1,\$hour[\$hidx]);
}
for \$midx(0..\$#min) {
\$newx=newx(\$t[1]*6,0,-4-\$midx)+12;
\$newy=newy(\$t[1]*6,0,-4-\$midx)+11;
\$vx = sprintf("%d", \$newx);
\$vy = sprintf("%d", \$newy);
substr(\$_,\$vy*\$w+\$vx,1,\$min[\$midx]);
}
for \$sidx(0..\$#sec) {
\$newx=newx(\$t[0]*6,0,-1-\$sidx)+12;
\$newy=newy(\$t[0]*6,0,-1-\$sidx)+11;
\$vx = sprintf("%d", \$newx);
\$vy = sprintf("%d", \$newy);
substr(\$_,\$vy*\$w+\$vx,1,\$sec[\$sidx]);
}
print \$hm,(/.{\$w}/g),scalar localtime;
sleep 1;
}
sub newx {
my(\$ang,\$x,\$y) = @_;
my \$a=\$ang * atan2(1,1)/45;# angle in radians
return
(\$x)*cos(\$a) - (\$y)*sin(\$a);
}
sub newy {
my(\$ang,\$x,\$y) = @_;
my \$a=\$ang * atan2(1,1)/45;# angle in radians
return
(\$x)*sin(\$a) + (\$y)*cos(\$a);
}
exit 0;
```
...since I was in hack mode i did not use -w nor 'use strict.' Basically, the script creates a 25x25 matrix of dots and then calculates the positions of the clock hands based on localtime and adds them to the matrix. The vt100 codes for 'move to the home position' are printed followed by the matrix. Feel free to /msg me if you have any questions about how this works.

Ok, back to our obfuscation. My first thought after looking at this script was that this script is too long and that I should focus my effort on shortening the script as much as possible through refactoring and any Perl tricks that I could think of and apply. So after a good deal of refactoring and using eval to call my subroutines, I shortened it to the following code.

```#!perl
\$|=\$b=1;
\$,=\$/;
@B=split//,"Just another Perl hacker";
\$n='\$a=\$X*2*atan2(1,1)/15;int(-cos(\$a)*(\$i+1)+11.5)*25+int(sin(\$a)*(\$i+1)+12.5)';
\$o='\$X=(localtime)[\$r]*\$b;substr(\$_,eval \$n,1,\$B[\$i+\$c])';
do{
\$_="."x287 ."#"."."x287;
for \$i(0..10) {
\$r=0;\$c=13;\$b=1;eval \$o;
\$i<7&&(\$r=1,\$c=5 ,\$b=1,eval \$o);
\$i<4&&(\$r=2,\$c=0 ,\$b=5,eval \$o);
}
print pack("c3",27,91,72),(/.{25}/g),scalar localtime;
}while(sleep 1);
```
Nice! Now I have a short enough script to obfuscate. :) The next step is where I learned a lot about builtin variables. Making the script run with -w and use strict; without using my was a bit of a challenge. Since I wanted to use builtin variables, I needed to reduce the number of scalars that I was using because there is a limited number of read/write builtins that I could use. I removed \$b and \$c by mapping their values using \$r as an index into an array that returned the values I needed, e.g. (1,2,3)[\$r]. There are still places in the script where things could be shortened, for instance scalar localtime is there as a visual cue only and could be removed.

Preparing the script for "shape change" brought about an interesting problem; if you have escaped quotes in your eval string, you will need to add additional back-slashes to compensate for the translation. I found that using a color syntax hiliting editor such as vim helped tremendously in discovering this problem. Here is the script after substituting builtin variables for my regular variables and after preparation for drawing, e.g. wrapping it in \$_='code with spaces';s/ //g;eval allowing me to add spaces whereever I need to.

```#!perl  -w
use strict;
\$_='
\$,=\$/;\$|=@_=split//,"JustanotherPerl.hacker";\$/=\'
\$~=(localtime)[\$.]*(\$.==0||\$.==1?1:5);substr(\$_,ev
al\\\'\$^=\$~*2*atan2(1,1)/15;int(-cos(\$^)*(\$=+1)+11
.5)*25+int(sin(\$^)*(\$=+1)+12.5)\\\',1,\$_[\$=+(11,4,
0)[\$.]])\';do{\$_=("."x287)."#"."."x287;for\$=(0..10
){\$.=0;eval\$/;\$=<7&&(\$.=1,eval\$/);\$=<4&&(\$.=2,eval
\$/);}print(pack("c3",27,91,72),(/.{25}/g),scalar(l
ocaltime));}while(sleep(1))
';s/\s+//g;
eval
```
After removing all the spaces, I tested the script and ran into a few problems that parens fixed, such as "."x287."#" will flag an error because the interpreter thinks 287." is a floating point number. I put parens around the expression ("."x287)."#" to fix it.

The other issue that I found while preparing this script for a shape change is that all the backslashes and quotes that make up the escaped quote could not be separated in the drawing itself because that would terminate the beginning quote, e.g. keep \' together without spaces. Keeping that in mind, load the script in your favorite editor and move things around until you have a drawing that you like. Go back to the beginning of this article to see the final result!

There you have it! JAPH, Camel, and a timepiece, and as always TMTOWTDI!

--
hiseldl

P.S. Only the camel code at the beginning is wrapped in <code> tags to make downloading easier. That means you will have to cut/paste the rest of the code, which is there for illustrative purposes only anyway ;)

Replies are listed 'Best First'.
Re: Camel Time
by belg4mit (Prior) on Aug 16, 2002 at 02:15 UTC
Spiffy, but in your explanation you neglected to say at what time(s) the hands can be read (clockwise of course) as a sentence :-P

UPDATE: This ever so slightly tweaked version lets you see the *cough* camel *cough* as well :-D

```#!perl  -w
use strict;
\$_='
\$,=\$/;\$|=@_
=split//,"Justan                ot
+he
rPerl.hacker";\$/=\'\$~=         (local
+ tim
e)[\$.]*(\$.==0||\$.==1?1:5);s      ubstr(\$
+_,eval
\\\'\$^=\$~*2*atan2(1,1)/15;int(-  cos(\$^) *
+(\$=+
1)+11.5)*25+int(sin(\$^)*(\$=+1)+12.5)\\\',1
+,
\$_[\$=+(11,4,0)[\$.]])\';do{\$_=("."x287).
"#"."."x287;for\$=(0..10){\$.=0;eval\$/;
\$=<7&&(\$.=1,e         val\$/);\$=<4&
&(\$.=2,ev             al\$/);}p
rint(p               ack("
c3  ",                27,9
1,  72                ),(/
.{  25                }/g)
,s  ca                lar(
lo   cal              time)
);}   whil            e(slee

p(1))';print;s/\s+//g;eval

--
perl -pew "s/\b;([mnst])/'\$1/g"

Re: Camel Time
by belden (Friar) on Aug 16, 2002 at 23:39 UTC
...JAPH, Camel, and a timepiece...

And also a quine!

This is very cool, your spoiling is a good read as well. ++for a great program!

blyman
setenv EXINIT 'set noai ts=2'

Re: Camel Time
by Theseus (Pilgrim) on Nov 24, 2002 at 06:12 UTC
I'd like to offer up a small conversion to make this web-based. I'm not sure how well it would perform on a low bandwidth connection, but running off my local web server it works quickly enough to completely refresh and complete the request every second. Good stuff, ++hiseldl!
```#!perl
\$|=1;
\$w = 25;
\$h = 25;
\$img="."x(\$w*\$h-1);
substr(\$img,11*\$w+12,1,"#"); # in the middle
\$,="\n";
@hour=split//,"Just";
@min =split//,"another";
@sec =split//,"Perl hacker";
print "Content-type: text/html\n\n";
print "<pre>";
@t = localtime;
\$_=\$img;
for \$hidx(0..3) {
\$newx=newx(\$t[2]%12*30+180,0,2+\$hidx)+12;
\$newy=newy(\$t[2]%12*30+180,0,2+\$hidx)+11;
\$vx = sprintf("%d", \$newx);
\$vy = sprintf("%d", \$newy);
substr(\$_,\$vy*\$w+\$vx,1,\$hour[\$hidx]);
}
for \$midx(0..\$#min) {
\$newx=newx(\$t[1]*6,0,-4-\$midx)+12;
\$newy=newy(\$t[1]*6,0,-4-\$midx)+11;
\$vx = sprintf("%d", \$newx);
\$vy = sprintf("%d", \$newy);
substr(\$_,\$vy*\$w+\$vx,1,\$min[\$midx]);
}
for \$sidx(0..\$#sec) {
\$newx=newx(\$t[0]*6,0,-1-\$sidx)+12;
\$newy=newy(\$t[0]*6,0,-1-\$sidx)+11;
\$vx = sprintf("%d", \$newx);
\$vy = sprintf("%d", \$newy);
substr(\$_,\$vy*\$w+\$vx,1,\$sec[\$sidx]);
}
print (/.{\$w}/g),scalar localtime;
print '<meta http-equiv="refresh" content="1;url=">';
sub newx {
my(\$ang,\$x,\$y) = @_;
my \$a=\$ang * atan2(1,1)/45;# angle in radians
return
(\$x)*cos(\$a) - (\$y)*sin(\$a);
}
sub newy {
my(\$ang,\$x,\$y) = @_;
my \$a=\$ang * atan2(1,1)/45;# angle in radians
return
(\$x)*sin(\$a) + (\$y)*cos(\$a);
}
exit 0;
Re: Camel Time
by drewbie (Chaplain) on Dec 11, 2002 at 20:51 UTC
I saw this node on the PerlMonks store and I have to say it's one of the cooler (and useful!) obfuscations I've seen. Excellent job! ++hiseldl

Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: obfuscated [id://190308]
Approved by virtualsue
Front-paged by jarich
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (4)
As of 2022-05-16 08:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
Do you prefer to work remotely?

Results (62 votes). Check out past polls.

Notices?