Remember Logo? If you have fond memories of turtles crawling around with pens, drawing all sorts of designs that only a sick kid with a spirograph and shrooms could have thought up, you'll like this JAPH.

I call this creation Tortise.

Tortise is a little program that reads a script inspired by Logo (but different and far less flexible, due to the fact that there are no subs or grouping). As the script is parsed, the tortise crawls around drawing ASCII art. Because it is ASCII art, you need a relatively standard terminal, with at least 24 lines to view the complete sketch.

Here's the ugly, cryptic version of the Tortise script.

$_="DE3W1S3SW1NW1UN3E5DS3SE1E1NE1N3UE4DW1SW1SE1E1SE1SW1W1UN4E4DE3W2S4 US6W16DN3NE1E1SE1S3N2W2UE4S2DN4SE1S1SE1SE1N4UE3DE1SE1S2SW1W1NW1N2UN1E 5DE3W2S4UN4E4DS4N2E3S2N4UE5DW3S2E1SW1S1E3UE2DN4E2SE1SW1W1E1SE1S1US6W3 3DN4E2SE1SW1W1UE7N2DW3S2E1SW1S1E3UE2DN4E2SE1SW1W1E1SE1S1UE5DW3N4US6W1 5DS4N2E3N2S4UE2DN3NE1E1SE1S1W2E2S2UN1E5DSW1W1NW1N2NE1E1SE1UE2N1DS4N1N E3SW2SE2UE5DW3N2E1NW1N1E3US4E2DN4E2SE1SW1W1E1SE1S1";s/\s//g;%d=(N,[-1 ],NE,[-1,1],E,[0,1],SE,[1,1],S,[1],SW,[1,-1],W,[0,-1],NW,[-1,-1]);$c= "[NESW]+";map{if(/U|D/){$s=$_;$s eq"D"?$g[$n][$m]="#":0;}else{($v,$r) =/($c)(\d+)/}while($r){$n+=$d{$v}[0];$m+=$d{$v}[1];$z=\$g[$n][$m];$$z =$s eq"D"?"#":$$z;$r--}}/[UD]|$c\d+/g;for(;$a<@g;$a++){!defined@{$g[$ a]}?do{print$/;next}:0;for($z=0;$z<@{$g[$a]};$z++){print$g[$a][$z]?$g [$a][$z]:$"}print$/}

Ok, so it's longer than four lines. It's not easy creating tortises and teaching them to spell.

If you would like to see the de-obfuscated version of Tortise, and learn how you too can train your very own tortise, you may read more.


The scripting is simple:

And as promised, here is the de-obfuscated version:

#!/usr/bin perl # ASCII Tortise: # Tortise parses the string contained in $script, and moves # a pen around inside of a two-dimensional array according to # the directions in $script. # Whitespace has no significance between complete command # chunks within the tortise script. # Therefore you may break up complex designs into simpler # subdesigns, represented as seperate lines of script. # To make it a little easer, stick to my here-document # format for scripting. # The script commands are as follows: # U - Pen up (don't draw). # D - Pen down (draw). # N, NE, E, SE, S, SW, W, NW, followed by number of # repititions. In other words, E5 would draw an # eastward (rightward) line, five pixels long. # The repitition number is required for all directional # commands, even if there is only one repitition. # For the most part, anything that doesn't match the # described script format will just confuse the # parser. As mentioned before, whitespace is noncritical, # and the parser ignores it. The parser ignores a lot # of other things too, but play it safe by only using # whitespace as delimiters. Delimiters are not required. use strict; use warnings; my $script = <<END_SCRIPT; DE3W1S3SW1NW1UN3E5 DS3SE1E1NE1N3UE4 DW1SW1SE1E1SE1SW1W1UN4E4 DE3W2S4US6W16 DN3NE1E1SE1S3N2W2UE4S2 DN4SE1S1SE1SE1N4UE3 DE1SE1S2SW1W1NW1N2UN1E5 DE3W2S4UN4E4 DS4N2E3S2N4UE5 DW3S2E1SW1S1E3UE2 DN4E2SE1SW1W1E1SE1S1US6W33 DN4E2SE1SW1W1UE7N2 DW3S2E1SW1S1E3UE2 DN4E2SE1SW1W1E1SE1S1UE5 DW3N4US6W15 DS4N2E3N2S4UE2 DN3NE1E1SE1S1W2E2S2UN1E5 DSW1W1NW1N2NE1E1SE1UE2N1 DS4N1NE3SW2SE2UE5 DW3N2E1NW1N1E3US4E2 DN4E2SE1SW1W1E1SE1S1 END_SCRIPT # @screen becomes a 2d array holding tortise's poop trail. my @screen; # %delta contains definitions of how direction commands # affect tortise's Y and X location in the grid. my %delta = ( "N" =>[-1, 0 ], "NE"=>[-1, 1 ], "E" =>[ 0, 1 ], "SE"=>[ 1, 1 ], "S" =>[ 1, 0 ], "SW"=>[ 1,-1 ], "W" =>[ 0,-1 ], "NW"=>[-1,-1 ] ); # %pen is cool. %pen holds the tortise's location, # his most recent direction command, his pen state, # how many moves he has to make before receiving his # next command. my %pen = ( "State" => "U", "Moves" => 0 , "Dir" => "" , "X" => 0 , "Y" => 0 ); # $dot and $blank just define what colors Tortise uses. my $dot = "#"; my $blank = " "; # Read the script, and pass commands on to Tortise. foreach ( $script =~ /[UD]|[NESW]+[0-9]+/g ) { if ( /U|D/ ) { $pen{State} = $_; if ( $pen{State} eq "D" ) { $screen[$pen{Y}][$pen{X}] = $dot; } } else { ( $pen{Dir}, $pen{Moves} ) = /([NSEW]+)([0-9]+)/; } # Keep moving until Tortise runs out of moves. # Then he sits and waits for the next command chunk. while ( $pen{Moves} ) { $pen{Y} += $delta{$pen{Dir}}[0]; $pen{X} += $delta{$pen{Dir}}[1]; if ( $pen{State} eq "D" ) { $screen[$pen{Y}][$pen{X}] = $dot; } $pen{Moves}--; } } # Now we can show the world Tortise's poop trail. for ( my $y = 0; $y < @screen; $y++ ) { if (!defined @{$screen[$y]}) { print "\n"; next; } for ( my $x = 0; $x < @{$screen[$y]}; $x++ ) { if ( $screen[$y][$x] ) { print $screen[$y][$x]; } else { print $blank; } } print "\n"; }

Dave

"If I had my life to do over again, I'd be a plumber." -- Albert Einstein