require "cam.pm" ;

{ $projects -> Christmas_PPC ; }

$? about cam.pm
$* the cam.pm list
$@ cam.pm meetings
$_ cam.pm projects
  home

This was not the easiest of templates to fit things in, in particular, there were all sorts of holes in horrid places to work with (or work around, if you prefer). The runs, such as they were were often not long enough to do useful things with them, however, entries were produced, and they did do interesting things.

The entries as submitted were from:
Matthew Byng-Maddick (non-entry)
Phil Kendall [2]
Andrew Savige [2] [3] [4] [5] [6]
Pete Clay

The winner, though it's a close one between Andrew's snowscape and Pete's single entry, is the PPM snowflake.

The entries can all be downloaded from here as a .tar.gz, including the test programme that was used to verify the entries.

Matthew Byng-Maddick

Back to top

Matthew B-M opened the competition with this single snowscape, in the hope of getting people to enter and produce interesting solutions. This was an idea that was brought up at a london.pm social meeting just before the competition was posted. This does, of course, not count as a real entry, due to him having judged the competition.

Requirements: ANSI Terminal

            sub                          l{$
          r[-1]=~                      s/./-/g
         ;a(   2,$d  );a(4,"/$e\\")  ;a(3   ,$f
          );a(5,$u.$e.    "".$    u."",1);for(
          @t){$p="(.{"    .$_.    "}";$r[-5]=~
         s,  $p.)   .,$    1.    "."   ,ex;  $r
        [0-    $_]   =~s;  $p  )...   ;$1    .""
       ."/$u"   .qq/\\/;xe ,1 for(2..4)}   $_=$z.
      ""     .join"\n",@r;print;sleep$|}sub     r{
  int(rand$_[0])}$|=1;$u=$";$e="_"x5;$a=$u."o";$b="o";
 ;sub  a{;substr  $r[-$_[0    ]],$h,7,  $_[1]}sub  t{t(
 01)    if!@_;$    k=$a;$      a=$b;$    b=$k;$r    [$_
 -01]  =~s/$a/r(  012)?$a:    $b.""/eg  for(1..$r  )}$b
  .=$u;$z="\e[H\e[J";($r,$c)=split/\s/,`stty${u}size`;
      $v     =$"x$c;$h=r($c-7);@t=map{r($c-     3)
       }(0..r   (9));$k=$b ;$ k=~s/\s//;   for(01
        ..$    r){   ($r[  $_  -1]=   $v)    =~#
         s,  \s,r   (30    )?    $":   $k#"  o"
          ,xeg}($f=$d=    "|".    "$u#$u+$u|")
          =~y/#/+/;for    (0..    $r){l;t;@r=(
         $v,   @r);  pop@r}$r[-6]="  Let"   .$"
          ."it$u"                      ."snow"
            ;l;                          $p;
 

This prints a snowscape with a house and some trees. The snow falls gently, occasionally being pertured from side to side as it goes. It's a fairly simple program once you see it having been reformatted and spaced out sensibly. It calls `stty size` to find out how big it's supposed to be, and then fills the screen with snow, finally overlaying the other bits.

# This function is better called "render"
sub l {
    # set out the base.
    $r[-1] =~ s/./-/g;
    ;

    # set out the house
    # at this point, $d is "| # + |"
    #                $f is "| + + |"
    #                $e is  "_____"
    a(2, $d);
    a(4, "/$e\\");
    a(3, $f);
    a(5, $u . $e . "" . $u . "", 1);

    # set out the trees
    for(@t) {
        $p="(.{".$_."}";
        $r[-5] =~ s,$p.).,$1.".",ex;
        $r[0-$_] =~ s;$p)...;$1.""."/$u".qq/\\/;xe,1 for(2..4)
    }

    # $_ contains the screen, including the "clear screen" code
    $_=$z . "" . join "\n", @r;

    print;

    # $| is set to 1 so this is a "sleep 1"
    sleep $|
}

# r() returns a random integer between 0 and <arg>-1
sub r {
    int(rand $_[0])
}

# set autoflush
$|=1;

# $u and $" both contain ' ' by default.
$u=$";

# $e now contains "_____"
$e="_" x 5;

# $a will contain " o"
$a=$u . "o";

# $b contains "o"
$b="o";
;

# this is a replace specifically for drawing the house.
# it does a substitution for the line <arg1> from the bottom
# position $h for 7 characters with <arg2>
sub a {
    ;
    substr $r[-$_[0]], $h, 7, $_[1]
}

# this function is better called perturb. It occasionally swaps " o" for "o "
# and vice versa. It does this recursively, by calling itself with an
# argument.
sub t {
    # recurse if we haven't already
    t(01) if ! @_;
    # swap $a and $b
    $k=$a;
    $a=$b;
    $b=$k;
    # do the swap across all the rows for whatever is in $a with $a or $b
    # on a 1 in 10 chance.
    $r[$_-01] =~ s/$a/r(012) ? $a : $b . ""/eg for(1..$r)
}

# $b="o ";
$b.=$u;

# this is the clear screen code
$z="\e[H\e[J";

# get the number of rows and columns rows => $r and cols => $c
($r,$c) = split/\s/, `stty${u}size`;

# $v contains a blank line as wide as the screen.
$v=$" x $c;

# $h is the left-hand position of the house
$h=r($c-7);

# @t contains the left hand positions of the trees there are between 1 and 9
# of them and they're each random
@t=map {r($c-3)} (0..r(9));

# set up $k="o "
$k=$b;
# $k="o"
$k=~s/\s//;

# for each row on the screen, set it to a blank line, and then randomly
# (1 in 30 chance) put snow in each position instead.
for(01..$r) {
    ($r[$_-1]=$v) =~ s,\s,r(30) ? $" : $k,xeg
}

# $f is "| + + |"
# $d is "| # + |"
($f=$d="|" . "$u#$u+$u|") =~ y/#/+/;

# run the frames
for(0..$r) {
    # draw the frame
    l;

    # perturb the snow
    t;

    # get the next frame, by putting a blank line at the top, and
    # removing the bottom line
    @r=($v, @r);
    pop @r
}

# set $r[-6] to "  Let it snow"
$r[-6]="  Let" . $" . "it$u" . "snow";

# render the frame
l;

# space filler.
$p;

Phil Kendall

Back to top

Phil Kendall was the first to submit a real entry, and also, in fact the last, for his second, revised version. His program was based on checking the snowflakes for compliance to the rules of the competition (there was a test programme applied to all entries, but this one was more helpful for development). It stores its own copy of the template, encoded, of course, and produces an output that is basically a character (rather than line) diff of the program in its argument against the template.

Version 1

Requirements: filename as argument

            for                          (()
          ,split+                      '',('C'
         .''   .'3'  .'Q3!A7M7!93'.  '3'.   '4'
          .'2E2433!A'.    'C4'    .'44C!AC444'
          .'C!922433'.    '42'    .'433422!8'.
         ''  .''.   ''.    ''    .''   .''.  ''
        .''    .''   .''.  ''  .''.   '3'    .''
       .'433'   .'422243'. '' .'343!763'   .'A1'.
      ''     .'21A36!625U52!2k!1429284829'.     ''
  .'24!134746664743')){$t.=$/,next,if"!"eq$_;$t.=($|--
 ?'#'  :chr(32))  x(ord$_>    57?(-55+  ord$_):$_  );};
 @l=    ((split    "\n",(      $t)));    $nn=@l;    ();
 push  @l,$l[$nn  -$_]for(    2..@l);;  undef($/)  ;@p=
  map{s/[^\s]/#/g;$_}split"\n",<>;foreach(0..(@l>@p?@l
      -1     :$#p)){@a=split'',$l[$_]||'';;     @b
       =split   '',$p[$_." "] ||"";for(0   ..(@a>
        @b?    $#a   :$#b  ))  {$c=   $a[    $_]
         ||  chr(   32)    ;;    ;$d   =$b[  $_
          ]||chr(32);;    ;;;;    print+($c)eq
          $d?$c:chr(32    )eq(    $c)?"-":"+"}
         s;#   ;;g;  print"\n"}#cam  -pm.   '02
          #.Xmas.                      Tourney
            #by                          PAK
 

Phil's entry has been discussed above, in essence it's pretty simple, though there are obviously some slight nasties, anyone who has done any golfing will be familiar with the $|-- trick, which is that the return value from that expression will always be 1 or 0, and it will flip. Phil is also fond of the ?: ternary operator, which he seems to use an awful lot.

You should run the program as:

perl pak.pl /path/to/filename/to/test.pl

If all is well, then you will see the snowflake as printed in the original template, if not then there will be +s and -s where non-whitespace needs to be added or removed.

# for each character in:
# C3Q3!A7M7!93342E2433!AC444C!AC444C!92243342433422!83433422243343!763
# A121A36!625U52!2k!142928482924!134746664743
# do:
for ((), split +'', ('C' . '' . '3' . 'Q3!A7M7!93' . '3' . '4' . '2E2433!A' .
                     'C4' . '44C!AC444' . 'C!922433' . '42' . '433422!8' .
                     '' . '' .  '' . '' . '' . '' . '' . '' . '' . '' . ''
                     . '' . '3' . '' . '433' . '422243' . '' . '343!763' .
                     'A1' . '' . '21A36!625U52!2k!1429284829' . '' .
                     '24!134746664743'
                     )) {
# if the character is a "!" then print a newline
$t.=$/ , next , if "!" eq $_;
# otherwise, it's a space or hash (switching) for
# (if it's a number, that number, otherwise the ascii value - 55)
# characters
$t.=( $|-- ? '#' : chr(32) ) x ( ord $_ > 57 ? ( -55 + ord $_ ) : $_ );
}

# we now have a representation of the top half of the snowflake in #'s

;

# @l contains the lines of our now expanded template.
@l=((split "\n", ($t)));

# $nn contains the number of lines
$nn=@l;

();

# mirror all but the last line on the end of the array, going outwards.
push  @l, $l[$nn-$_] for(2..@l);

# at this point, join("\n",@l) would have a full snowflake layout.

;

# set our record separator to undef, so when we do <> it will read the
# entire file.
undef($/);

# get each line of the file into @p, converting all the non-whitespace to
# "#"
@p=map {s/[^\s]/#/g ; $_} split "\n", <>;

# generate numbers to the bigger of @l and @p
foreach (0..(@l>@p?@l-1:$#p)) {
    # split the iteration line of the template into characters, making sure
    # to cope in the case where it's undefined.
    @a=split '', $l[$_] || '';

    ;

    # do the same for the line of the input. the $_." " is a pointless
    # stringification and then back again, but it uses up characters and
    # whitespace.
    @b=split '', $p[$_." "] || "";

    # for the bigger of a and b (note that we have these loaded and the
    # $_ is localised. iterate for that number of characters.
    for(0 .. (@a>@b ? $#a : $#b )) {
        # $c will contain the value at this point of the template, or if
        # it's not there then a space - this copes with trailing whitespace.
        $c=$a[$_] || chr(32);

        ;;

        # similarly for $d and the input
        $d=$b[$_] || chr(32);

        ;;;;;

        # print the appropriate character, the value from the template if
        # they are the same, a "-" if $c thinks there should be whitespace
        # and a "+" if $c thinks there should be text.
        print +($c) eq $d ? $c : chr(32) eq ($c) ? "-" : "+"
    }
    # this is a useless substitution, since $_ is a number at this point.
    s;#   ;;g;

    # we've reached the end of the line, so print the newline.
    print "\n"
}

Back to top

Version 2

Requirements: filename as argument

            $t=                          '3'
          .'*A*'.                      '!1.='.
         '.'   .'!'  .'0**+)5)+**'.  '!'.   '1'
          .'3+++3!13'.    '++'    .'+3!0))+**'
          .'+)+**+))'.    '!/'    .'*+**+)))+'
         .+  '*'.   '*'    .+    '+'   .'*'  .+
        '!'    .~~   '.-'  .+  '*1'   .~~    '('
       .')(1'   .'*-!-),E' .+ ',)!)[!(+'   .')0)'
      .+     '/+/)0)+!(*+.+---+.+*!';$w=chr     32
  ;$t=~s;.;$&=~/!/?$/:($|--?'#':$w)x(-39+ord$&);eg;$n=
 +$/;  $/=$1;@p=  grep{~y~    ~#~c+23}  split+$n,  <>;;
 @l=    split~~    $n,$t;      push@l    ,$l[10-    $_]
 for(  -0..012);  for(0...    (23>@p?+  23:$#p)){  +@a=
  split'',$l[$_]||'';@b=split'',$p[$_]||'';for(0...(@a
      >+     @b?$#a:$#b)){$c=$a[$_]||$w;$d=     $b
       [$_]||   $w;$s.=+$c eq $d?$c:($w)   eq$c?+
        '-'    :~~   '+'}  $s  .=$n   }$m    =$s
         .+  'E'.   'r'    .+    'r'   .'o'  .+
          "rs:".$s=~+y    .+-.    ..$n;print$m
          #Have.Golfed    Code    TooMuch.Must
         ###   Fill  WithComment.Oh  Well   ###
          #Happy#                      NewYear
            ###                          PAK
 

This is pretty similar, though the encoding string is different, and shorter, the chr(32)s have been replaced by a variable, and it's a slightly neater programme. It is run in the same way as the one above, and reports the number of errors found.

# + and ~~ are noops in this (~~ because you get "bitwise complement
# of bitwise complement")
$t = '3' . '*A*' . '!1.=' . '.' . '!' . '0**+)5)+**' . '!' . '1' .
     '3+++3!13' . '++' . '+3!0))+**' . '+)+**+))' . '!/' .
     '*+**+)))+' . +'*' . '*' . +'+' . '*' . +'!' . ~~'.-' . +'*1' .
     ~~'(' . ')(1' . '*-!-),E' . +',)!)[!(+' . ')0)' .
     +'/+/)0)+!(*+.+---+.+*!';

# $w is " "
$w=chr 32;

# in the above string substitute every character for either
#   \n if it is a "!"
#   run of alternate "#" and " " to the ascii value of the character
#     minus 39
# $t therefore contains the top half of template after this.
$t =~ s;.; $& =~ /!/ ? $/ : ($|--?'#':$w) x (-39 + ord $&) ;eg;

# $n is "\n"
$n= +$/;

# set $/=undef
$/=$1;

# <> will be the whole file ($/==undef)
# split it per line
# translate all of the nonwhitespace characters into "#", returning
# all of the lines (I can't see a situation where ~y~    ~#~c+23 is
# false)
@p=grep{~y~    ~#~c+23} split+$n, <>;

;

# @l is the template version of the above, being also split on \n
#    (~~$n eq "\n")
@l = split ~~$n , $t;

# reflect the template about its midpoint
push @l, $l[10-$_] for (-0..012);

# $s is going to be our output
# So for each line or the whole of the input (whichever is greater)
for(0...( 23 > @p ? +23 : $#p ) ) {
    # @a contains the characters on this line from the template
    +@a = split '', $l[$_] || '';

    # @b contains the characters on this line from the input
    @b = split '', $p[$_] || '';

    # for each character on the longest line.
    for(0...(@a > +@b ? $#a : $#b)) {
        # $c is the character from the template on this line, or
        #    a whitespace if it doesn't exist
        $c = $a[$_] || $w;

        # $d is the character from the input on this line, or
        #    a whitespace if it doesn't exist
        $d = $b[$_] || $w;

        # The above code makes sure that trailing whitespaces work.

        # This code puts the template character if both are the same
        # (ie " " or "#", and a "-" if they're different and the template
        # says whitespace, or a "+" if they're different and the template
        # has a "#"
        $s .= +$c eq $d ?
                          $c
                        : 
                          ($w) eq $c ?
                                       +'-'
                                     :
                                       ~~'+'
    }
    # add a newline to our output before processing the next line.
    $s .= $n
}

# $m is the output then "Errors:" and a count of the number of "+" and "-"
# characters in $s and then a newline.
$m = $s . +'E' . 'r' . +'r' . 'o' . +"rs:" . $s =~ +y.+-.    . . $n;
# print it.
print $m

Andrew Savige

Back to top

So, the man who made his name with Acme::EyeDrops, and as Santa in one of the first big FWP golf tournaments submitted no fewer than 6 entries to this. Andrew Savige is either insane or a genius. It seemed reasonable to allow the EyeDrops-based entry, given that he was also the author of the automated code generation tool.

Requirements: large terminal, programme in a file

            '?'                          =~(
          '(?{'.(                      '`'|'%'
         ).(   '['^  '-').('`'|'!')  .''.   (((
          '`'))|"\,").    '"'.    ('`'|"\/").(
          '['^('+')).(    '`'|    '%').(('`')|
         ((  '.')   )).    ((    (((   '\\'  ))
        )))    .((   '$')  ).  '%;'   .((    (((
       '\\'))   ))).'@~='. +( '`'|'-').(   ('`')|
      ((     '!'))).('['^'+').'\\{'.(('[')^     ((
  '"'))).';!-~;.;;\\$_\\}<'.('^'^('`'|'.')).'>;'.('`'|
 '-')  .('`'|'!'  ).("\["^    '+').''.  '\\{`'.+(  '`'|
 '#'    ).('`'|    ',').(      ('`')|    "\%").(    '`'
 |'!'  ).(('[')^  ')').'>'    .('&').(  '^'^('`'|  ',')
  ).'`;'.('{'^'[').('['^',').('`'|'!').('['^')').('`'|
      ((     '.'))).'\\$/'.('['^'#').'(\\$'     .+
       '_+\\'   .'$_),\\@' .+ '~;'.('{'^   '[').(
        '['    ^((   '(')  ))  .''.   (((    '`'
         ))  |','   ).(    ((    '`'   ))|+  ((
          '%'))).('`'|    '%')    .('['^"\+").
          '!\\$%\\}\\'    .'$'    .'%'.(('{')^
         '['   ).((  '.')).'.'.('{'  ^'['   ).(
          ';'&'='                      ).'"}'.
            ')'                          );;
 

Andrew's first programme produces a falling snowflake by opening $0 and substituting a "." for every non-whitespace character. It adds newlines at the top, and then clears the screen and prints a frame each time. I cheated with this, and read the source using B::Deparse.

# STAGE 1 : run through Deparse
#
# '?' =~ m[(?{eval"open\$%;\@~=map\{y;!-~;.;;\$_\}<0>;map\{`clear>&2`; warn\$/x(\$_+\$_),\@~; sleep!\$%\}\$% .. 9"})];
#
# STAGE 2 : reduce by getting rid of the eval (it's the only thing that
#           is run by the regexp eval, and doesn't return anything)
# this is a function of the way that EyeDropper works. In effect, it's
# a null regexp and an eval within the regexp. The string you see above
# and below is formed by doing bitwise operations on constant strings.
'?' =~ m[(?{
    # the following line is equivalent to:
    #  open 0, $0;
    open $%;

    # map yourself ($0) translating all the characters to "." as we go into
    # the array @~
    @~ = map { y;!-~;.; ; $_ } <0>;

    # equivalent to a "for (0..9) {"
    map {
        # clear the screen
        `clear >&2` ;

        # print out 2n "\n"s (the value of $/) and then our translated
        # snowflake
        warn $/ x ($_+$_), @~;

        # sleep 1 (1 is the !0 and $%==0)
        sleep !$%
    } $%..9

    # end our eval
    })];

Back to top

Then came the second entry

Requirements: large terminal

            $_=                          '33
          01Z2101                      0701Z27
         010   901Z  16010401090112  01Z3   901
          Z13010601070    1040    110010401Z29
          02Z160106010    6030    301040105010
         30  1Z11   011    80    5Z1   8011  00
        811    01Z   2301  04  0903   010    401
       Z16011   109Z290602 01 0304Z22010   401040
      10     508Z32010211Z2916Z2422Z2026Z20     26
  Z2025Z2025Z2026Z1927Z1828Z1826Z1826Z2123Z2123Z2123Z2
 123Z  2123Z2123  Z2123Z21    23Z2123Z  03011723Z  0009
 122    3080105    01Z021      306230    2010410    Z03
 1701  230317Z03  02033602    20Z09020  2120335Z1  5040
  41601030102030105010501Z160109060105Z2214Z1818Z1618Z
      18     0302030303Z';s/\s//g;$f='*';;;     ;;
       print+   map((($f^= $/ )x(($_))),   /../g)
        ,$/    for   ((((  ((  ((((   (((    (((
         ((  ((((   (((    ((    (((   ((((  ((
          split+Z)))))    ))))    ))))))))))))
          ))))))))))))    )))#    ############
         ###   ####  ##############  ####   ###
          #######                      #######
            ###                          ###
 

The second entry produces a static picture of a candle, the data for which is run-length encoded in pairs in the data at the top. I, personally, thought it was a pretty picture, though this is just the first version of the it, and it definitely gets better. The trick of using "*"^"\n" eq " " is very clever, and all the following programmes use it.

# Set up $_ to be our data string. This means that all of the // operations
# will work sensibly.

# The data has been reformatted as the next operation removes all \s
# characters. I feel this is ok to aid the readability of this program.
$_='3301Z
    21010701Z
    27010901Z
    1601040109011201Z
    3901Z
    130106010701040110010401Z
    2902Z
    1601060106030301040105010301Z
    11011805Z
    180110081101Z
    2301040903010401Z
    16011109Z
    290602010304Z
    2201040104010508Z
    32010211Z
    2916Z
    2422Z
    2026Z
    2026Z
    2025Z
    2025Z
    2026Z
    1927Z
    1828Z
    1826Z
    1826Z
    2123Z
    2123Z
    2123Z
    2123Z
    2123Z
    2123Z
    2123Z
    2123Z
    2123Z
    03011723Z
    0009122308010501Z
    0213062302010410Z
    031701230317Z
    030203360220Z
    090202120335Z
    1504041601030102030105010501Z
    160109060105Z
    2214Z
    1818Z
    1618Z
    180302030303Z';

# get rid of the spaces in the data.
s/\s//g;

# set $f to be "*". Worth noting at this point that "*" ^ "\n" = " ".
$f='*';

;;;;

# take off two characters from the stream (so we get double figures)
# and use them to alternately print spaces and "*"s (because of the
# XOR property above. Do this for each of the parts in between split/Z/
# Note that there are 3 different uses of $_ here. In the outside, the
# full data string, then the part in the for() loop that's split by the
# "Z" characters, then the current 2 numbers in the map. This is fine
# because they're implicitly localised at each level.
print +map ( ( ( $f ^= $/ ) x (($_)) ), /../g), $/
    for ((((((((((((((((((((((((((((((((((((
        # worth noting at this point that "+" stringifies the bareword
        # so this is morally equivalent to ``split/Z/''
        split+Z
        ))))))))))))))))))))))))))))))))))))

Back to top

And a third

Requirements: large terminal

            $:=                          q^A
          "#,Z>1Z                      97Z5;Z5
         ;Z5   :Z5:  Z5;Z4<Z3=Z3;Z3  ;Z68   Z68
          Z68Z68Z68Z68    Z68Z    68Z68Z$"28Z!
          *-8)"&"Z#.'8    #"%+    Z$2"8$2Z$#$E
         #5  Z*##   -$D    Z0    %%1   "$"#  $"
        &"&    "Z1   "*'"  &Z  7/Z3   3Z1    3Z3
       $#$$$Z   ^;$;=q^B"Z 6" ("Z<"*"Z1"   %"*"-"
      ZH     "Z."'"("%"+"%"Z>#Z1"'"'$$"%"&"     $"
  Z,"3&Z3"+),"Z8"%*$"%"Z1",*Z>'#"$%Z7"%"%"&)Z^;$^=q^@"
 Z8"*  "Z="*"Z2"  &"*"*"ZF    "Z/"%")"  &")"%"Z>#  Z2"'
 "&$    %"%"$"$    "Z-"2&      Z4"*)+    "Z7"&*%    "#"
 Z0"-  *Z>'$"#%Z  6"%"&"&)    Z^;;;sub  _{s}\s}}g  ;$.=
  '*';system$^O=~Win?CLS:'clear';print+map((($.^=$/)x(
      ((     ord))-33)),/./g),$/for+split+Z     ;;
       sleep!   $%}_($_=$; .$ :),_$_=$^.   $:,for
        !$%    ..4   ;$_=  'M  erry   60C    hri
         st  mas6   0to    60    a60   snow  bo
          und60cam.pm6    0fro    m60a60Sydney
          60sunbather!    ';$\    =$/;s;\s;;g;
         ;;;   s;$=  ;$";gsex;print  ;s;k   ara
          te;chop                      ;eggs;;
            #by                          /-\
 

This was an evolution on the first entry. The data encoding was single characters, giving a lot more space, and there were two heads to the candle used in alternation, to make it flicker. The end prints "Merry Christmas to a snowbound cam.pm from a Sydney sunbather!", which was only stifled by the fact that for once, it didn't snow in Cambridge. Even better, this version of Andrew's programme will work under Windows too.

#           111111111122222222223333333333
# 0123456789012345678901234567890123456789
# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGH
# 
# $: contains the data for the bottom half of the candle, the bit that doesn't
# move.
$: = q^
       A"#,Z
       >1Z
       97Z
       5;Z
       5;Z
       5:Z
       5:Z
       5;Z
       4<Z
       3=Z
       3;Z
       3;Z
       68Z
       68Z
       68Z
       68Z
       68Z
       68Z
       68Z
       68Z
       68Z
       $"28Z
       !*-8)"&"Z
       #.'8#"%+Z
       $2"8$2Z
       $#$E#5Z
       *##-$DZ
       0%%1"$"#$"&"&"Z
       1"*'"&Z
       7/Z
       33Z
       13Z
       3$#$$$Z
      ^;

# $; and $^ contain the two alternate top halves.
$; = q^
       B"Z
       6"("Z
       <"*"Z
       1"%"*"-"Z
       H"Z
       ."'"("%"+"%"Z
       >#Z
       1"'"'$$"%"&"$"Z
       ,"3&Z
       3"+),"Z
       8"%*$"%"Z
       1",*Z
       >'#"$%Z
       7"%"%"&)Z
      ^;

$^ = q^
       @"Z
       8"*"Z
       ="*"Z
       2"&"*"*"Z
       F"Z
       /"%")"&")"%"Z
       >#Z
       2"'"&$%"%"$"$"Z
       -"2&Z
       4"*)+"Z
       7"&*%"#"Z
       0"-*Z
       >'$"#%Z
       6"%"&"&)Z
      ^;
;;

# define a sub "_"
sub  _ {

    # which gets rid of spaces in $_
    s}\s}}g;

    # $. is a "*", this is useful because:
    # $. ^ $/ eq " "
    # and of course, $. ^ $/ ^ $/ eq "*"
    $. = '*';

    # run "CLS" or "clear" depending on what system we're on.
    system
        $^O =~ Win ?
                 CLS : 
                 'clear';

    # note 3 different uses of $_ here:
    # 1. the global data
    # 2. that data split by "Z" from the for() below
    # 3. each character mapped to be the multiplier, when passed
    #    through ord() and then having 33 subtracted.
    #
    # so this prints alternate " " and "*" for each character in the data,
    # with "Z" marking line splits
    print + map ((( $. ^= $/ ) x ( ((ord)) -33) ) , /./g ), $/
        for + split +Z;

    ;

    # sleep "not 0" == 1
    sleep !$%
}

# this is a fake, the argument isn't used, but it's evaluated before the
# function is called. So, we call &_ with ($_ eq $; . $:), and then with
# ($_ eq $^ . $:), which we do 4 times each.
_ ( $_ = $; . $: ) , _ $_ = $^ . $: , for !$% .. 4;

# define a new string to print.
$_ = 'Merry60Christmas60to60a60snowbound60cam.pm60from60a60Sydney60sunbather!';

# make sure that print will automatically put a newline on the end.
$\ = $/;

# get rid of spaces in the above string
s;\s;;g;

;;;

# $= is our current format page length (defaults to 60). $" defaults to a
# space. The evaluation doesn't really matter, because it would get
# substituted anyway...
s;$=;$";gsex;

# print out the string
print;

# random filler noop substitution
s;k   ara
          te;chop                      ;eggs;;

Back to top

He didn't stop there, though. I swear the man didn't do anything else over christmas.

Requirements: large terminal

            $;=                          q]A
          "#,Z>1Z                      97Z5;Z5
         ;Z5   :Z5:  Z5;Z4<Z3=Z3;Z3  ;Z68   Z68
          Z68Z68Z68Z68    Z68Z    68Z$"28Z!*-8
          )"&"Z#.'8(+Z    $1#8    $2Z&^Z*##SZ0
         %$  6-"Z   ;-Z    7/    Z33   Z13Z  3$
        $$]    ;$~   ='Qi  vv  }$Gl   vmw    xqe
       w';sub   _{s}\s}}g; $. ='*';print   join""
      ,(     !!system$^O=~Win3?CLS:'clear')     ,(
  map{map((($.^=$/)x(-33+ord)),m).)g),$/}split+Z),@,;;
 push  (+@,,$~=~  /.*?\$/g    ?map{chr  (-4+ord)}  $&=~
 (((    m).)g))    ):$/);      sleep!    $%}$:=q    ^B"
 Z6"(  "Z<"*"Z1"  %"*"-"ZH    "Z."'"("  %"+"%"Z>#  Z1"'
  "'$$"%"&"$"Z,"3&Z3"+),"Z8"%*$"%"Z1",*Z>'#"$%Z7"%"%"&
      )Z     ^;$^=q^@"Z8"*"Z="*"Z2"&"*"*"ZF     "Z
       /"%")"   &")"%"Z>#Z 2" '"&$%"%"$"   $"Z-"2
        &Z4    "*)   +"Z7  "&  *%"#   "Z0    "-*
         Z>  '$"#   %Z6    "%    "&"   &)Z^  ;;
          ;$~.='$xs$e$    wrs{    fsyrh$geq2tq
          $jvsq$e$W}hr    i}$w    yrfexliv%$';
         $|=   1;$~  =~s~\s~~g;_($_  =$:.   $;)
          ,_$_=$^                      .$;,for
            !$%                          ..6
 

Yet another evolution, this one managed to encode the "Merry Christmas" message, and display it, one word at a time as the frame advanced. The maps that were used made my brain hurt, and some clever techniques to make it hard to read were evident.

# The data for the bottom of the candle
$; = q]
       A"#,Z
       >1Z
       97Z
       5;Z
       5;Z
       5:Z
       5:Z
       5;Z
       4<Z
       3=Z
       3;Z
       3;Z
       68Z
       68Z
       68Z
       68Z
       68Z
       68Z
       68Z
       68Z
       $"28Z
       !*-8)"&"Z
       #.'8(+Z
       $1#8$2Z
       &^Z
       *##SZ
       0%$6-"Z
       ;-Z
       7/Z
       33Z
       13Z
       3$$$
      ];
$~ = 'Qivv}$Glvmwxqew';

sub _ {
    # get rid of the spaces in $_
    s}\s}}g;

    $. = '*';

    print join"", (!!system $^O =~ Win3 ? CLS : 'clear'),
                  (
                     # this map splits per line
                     map { 
                           # this one splits per character, and signals
                           # the run length
                           map ( ( ($.^=$/) x (-33+ord) ), m).)g ), $/
                         }
                         split+Z
                  ),
                  # @, contains the text for the Happy Christmas message
                  @,;
    ;

    # the /g makes sure that the next time, we retain our current position
    # so each time this is called, we push a new word, at the end, we push
    # a newline. The words are taken from $~
    push( +@, , $~ =~ /.*?\$/g ?
                                 # the word themselves need to have 4
                                 # subtracted from the ascii value of each
                                 # character.
                                 map { chr(-4 + ord) } $&=~ ((( m).)g )))
                               :
                                 $/
        );

    # sleep 1
    sleep !$%
}

# the data for one of the tops

$: = q^
      B"Z
      6"("Z
      <"*"Z
      1"%"*"-"Z
      H"Z."'"("%"+"%"Z
      >#Z
      1"'"'$$"%"&"$"Z
      ,"3&Z
      3"+),"Z
      8"%*$"%"Z
      1",*Z
      >'#"$%Z
      7"%"%"&)Z
     ^;

# and the other

$^ = q^
       @"Z
       8"*"Z
       ="*"Z
       2"&"*"*"Z
       F"Z
       /"%")"&")"%"Z
       >#Z
       2"'"&$%"%"$"$"Z
       -"2&Z
       4"*)+"Z
       7"&*%"#"Z
       0"-*Z
       >'$"#%Z
       6"%"&"&)Z
      ^;
;;

$~ .= '$xs$e$wrs{fsyrh$geq2tq$jvsq$e$W}hri}$wyrfexliv%$';

# set autoflush
$|=1;

# get rid of the spaces in $~
$~ =~ s~\s~~g;

# run each individual top 6 times.
_( $_ = $: . $; ), _ $_ = $^ . $; , for !$% .. 6

Back to top

This programme was the fifth submitted, and took some ideas from the example at the top

Requirements: large terminal

            $_=                          q~v
          ZvZ&%('                      $&"'"&(
         &"&   $&"'  "&$Z$#$$$#$%$&  "'"&   (&#
          %$&"'"&#Z#$$    $#%#    %$%$%$%(%%%#
          %$%$%#Z"%*#$    %$%$    %$%(%%%#%$%$
         %#  Z"%,   ($%    $%    $%(   %%%#  %$
        %$%    #Z"   %*%"  %$  %$%$   %(%    %%#
       %$%$%#   Z#%%"#%#%$ %$ %$%$##&#%$   %$%$%#
      Z$     &""$%"&$%$%$%#%"%"&%%$%$%#Z%&%     &#
  %"'"'"'###%*'"'"'"ZT%?ZT%?ZS'>Zv~;s;\s;;g;$;='@,=map
 {$.=  $";join""  ,map((($    .^=O)x(-  33+ord)),  /./g
 ),$    /}split    +Z;s/.      /(rand    )<.2?"o    ":$
 "/eg  for@;=((5  x84).$/)    x30;map{  system$^O  =~W?
  CLS:"clear";print@;;splice@;,-$_,2,pop@,;@;=($/,@;);
      sl     eep!$%}2..17';$;=~s;\s;;g;eval     $;
       ######   ########## ## ##########   ######
        ###    ###   ####  ##  ####   ###    ###
         ##  ####   ###    ##    ###   ####  ##
          ############    ####    ############
          ############    ####    ############
         ###   ####  ##############  ####   ###
          #######                      #######
            #by                          /-\
 

This was a different programme, and used something similar to my snowscape to fall on the ground to spell out cam.pm in a block. I liked this entry, and typical to Andrew's golfing history, he had nearly half the space just in comments. Most of the code was evalled, and it's pretty amazing that there didn't need to be whitespace in any of that bit.

$_ = q~
       vZ
       vZ
       &%('$&"'"&(&"&$&"'"&$Z
       $#$$$#$%$&"'"&(&#%$&"'"&#Z
       #$$$#%#%$%$%$%(%%%#%$%$%#Z
       "%*#$%$%$%$%(%%%#%$%$%#Z
       "%,($%$%$%(%%%#%$%$%#Z
       "%*%"%$%$%$%(%%%#%$%$%#Z
       #%%"#%#%$%$%$%$##&#%$%$%$%#Z
       $&""$%"&$%$%$%#%"%"&%%$%$%#Z
       %&%&#%"'"'"'###%*'"'"'"Z
       T%?Z
       T%?Z
       S'>Z
       v
      ~;
s;\s;;g;

# the following bit of code is basically:
#  @, ends up containing the cam.pm text, which is extracted from the RLE
#     data in $_
#  @; is filled with snow on 30 rows
#  each frame is printed in the map, which prints the current "@;" (screen)
#  the splice then takes off a row of snow and turns it into the cam.pm
#     body.
#  finally a newline is pushed on at the top.
$; = '
      @, = map { $.=$" ; join "" , map((($.^=O)x(-33+ord)),/./g) , $/ }
                  split +Z;

      s/./(rand)<.2 ? "o" : $"/eg for @; = ((5x84).$/)x30;

      map { system $^O =~ W ? CLS :"clear" ;
            print @;
            ;
            splice @; , -$_ , 2 , pop @, ;
            @; = ( $/ , @; );
            sleep !$%
          } 2..17
     ';

# eval the above code with no spaces in it.
$; =~ s;\s;;g;
eval $;

Back to top

This last programme came hot on the tail of the one above, and is an evolution of it. It adds a snowman that appears on the last frame.

Requirements: large terminal

            $_=                          q~v
          ZvZ&%('                      $&"'"&(
         &"&   $&"'  "&$Z$#$$$#$%$&  "'"&   (&#
          %$&"'"&#Z#$$    $#%#    %$%$%$%(%%%#
          %$%$%#Z"%*#$    %$%$    %$%(%%%#%$%$
         %#  Z"%,   ($%    $%    $%(   %%%#  %$
        %$%    #Z"   %*%"  %$  %$%$   %(%    %%#
       %$%$%#   Z#%%"#%#%$ %$ %$%$##&#%$   %$%$%#
      Z$     &""$%"&$%$%$%#%"%"&%%$%$%#Z%&%     &#
  %"'"'"'###%*'"'"'"ZT%?ZT%?ZS'>Zv~;s;\s;;g;$~=q~ZZZJ_
 #_ZH  /'\\ZG|#o  #o#|ZG|$    <%|ZH\\"  \\!_!_!/"  /ZG/
 )\\    ZF/+\\Z    E|-|ZE      |-|ZE|    -|ZF\\+    /ZG
 \\)/  ~;;@x=@,=  +map{$.=    $";;join  "",map(((  $.^=
  O)x(-33+ord)),/./g)}split+Z;$~=~s~\s~~g;;s/./(rand)<
      .2     ?"o":$"/egxfor@;=(5x84)x30;map     {#
       system   $^O=~W?CLS :+ "clear";;;   ;print
        $_.    $/,   ,for  $_  -18?   @;:    ###
         ((  map{   $|=    1;    ;;;   join  ""
          ,map($|--?$"    x(-3    *11+ord):$_,
          /./g)}split+    Z,$~    ),@x);splice
         @;,   -$_,  2,pop@,;@;=(""  ,@;)   ;;;
          ;sleep!                      $%}+2..
            18#                          /-\
 

This got rid of the horrid eval above, and had some more brain-wrenching mapping. $_ contains the cam.pm block, and $~ the snowman

$_ = q~
       vZ
       vZ
       &%('$&"'"&(&"&$&"'"&$Z
       $#$$$#$%$&"'"&(&#%$&"'"&#Z
       #$$$#%#%$%$%$%(%%%#%$%$%#Z
       "%*#$%$%$%$%(%%%#%$%$%#Z
       "%,($%$%$%(%%%#%$%$%#Z
       "%*%"%$%$%$%(%%%#%$%$%#Z
       #%%"#%#%$%$%$%$##&#%$%$%$%#Z
       $&""$%"&$%$%$%#%"%"&%%$%$%#Z
       %&%&#%"'"'"'###%*'"'"'"Z
       T%?Z
       T%?Z
       S'>Z
       v
      ~;

# get rid of the spaces in $_
s;\s;;g;

$~=q~
     Z
     Z
     Z
     J_#_Z
     H/'\\Z
     G|#o#o#|Z
     G|$<%|Z
     H\\"\\!_!_!/"/Z
     G/)\\Z
     F/+\\Z
     E|-|Z
     E|-|Z
     E|-|Z
     F\\+/Z
     G\\)/
    ~;
;

# Fill @x and @, with the RLE encoded "cam.pm" block from $_
@x = @, = +map {
                $. = $";
                ;
                join "", map( ( ( $. ^= O ) x (-33+ord) ), /./g )
               } split +Z;

# get rid of the spaces in $~ (the snowman)
$~ =~ s~\s~~g;
;

# fill @; with snow
s/./(rand)<.2?"o":$"/egx for @; =(5x84)x30;

# run our frames
map {
     # clear the screen
     system $^O =~ W ? CLS : +"clear";
     ;;;

     # print "$_\n"
     print $_ . $/,, for $_-18 ?
                                 # the snowscape
                                 @;
                               :
                                 ((
                                 # this map draws the snowman
                                 map {
                                     $| = 1;
                                     ;;;
                                     join "",
                                       # alternate characters are RLE number
                                       # of spaces, or the character itself
                                       map( $|-- ? $" x (-3*11+ord) : $_, /./g)
                                     } split +Z, $~),
                                       # @x contains the block
                                       @x);
     # advance the snowscape pushing the snow and block
     splice @; , -$_ , 2 , pop @, ;
     @; = ( "" , @; );
     ;;;
     # sleep
     sleep !$%
    } +2..18

Pete Clay

Back to top

So, this competition's Colin Watson came in at the 11th hour and used nice vector maths and quasi-Affine Transformations to produce a snowflake but as a PPM file.

            $w=                          100
          ;$w*=5;                      ;$W=$w*
         $w;   q--;  $p=atan2(1,0)*  2;3;   @P=
          ("\000"x3)x(    $W);    BEGIN{$";sub
          S{$_.='fhoq'    }sub    K{$_.='s'.((
         $n  ++))   .((    ''    .''   .'{'  ))
        ;};    sub   Y{$_  .=  $_[0   ]};    S;K
       ;Y(''.   '$k/=2;$'. '' .'l/=2}');   S;K;0;
      Y(     '$k/=3;$l/=3;$k+=$j/4}fhoqh{'.     ''
  .'cevq"P6\a$jq$j\a255\a",q@P}');S;K;Y('$g=$c/3;e}');
 S;K;  Y(q.$g=$.  .'c/-3;'    .'e}');S  ;K;Y('$'.  'g'.
 '='    .'$c*2'    .'/3}'      );;s/v    /vag/x;    sub
 p{$P  [$x+$w/2+  int($y)*    $w+$W/2]  ="\377"x3  }sub
  r{$z=$x*cos($t)+$y*sin($t);$y=$x*sin($t)-$y*cos($t);
      $x     =$z};tr/a-z/n-za-m/;s/d/$"/gx;     $x
       ++;$y=   4;eval$_}; s/ /japh/;sub   T{;$a= 
        $_[    0];   ;$b=  ((  $a&(   ($a    |(+
         $a  <<1)   )<<    1)    )>>   2)|(  $a
          >>3);if($b){    $_[0    ]=""}else{$_
          [0]="f$a;p;"    }};;    for(1..20000
         *10   ){$_  =int(rand(20))  ;T$_   ;0;
          eval$_}                      u#byPJC
            #JA                          PH#
 

Pete's entry was the only one which used no data at all. The data that is there is code that gets rot-13ed and evalled. The code itself confuses Deparse in a couple of places, and contains some real nasties. The circuit reduction for the effective: $b=$a>4 within T is, erm, interesting. The fact that it produces the output it does is also impressive, but is a function of the way such transformations work, and doing the iteration that many times makes it likely to work.

Pete's recommendation was to run this programme as:

perl pjc.pl | xloadimage stdin

though any PPM (Portable PixMap) viewer should work

The output should look something like that below:

[snowflake]

# $w=500 - The width and height of our canvas
$w = 100;
$w *= 5;
;

# $W=250000 = number of pixels
$W = $w*$w;

# ''
q--;

# $p = \pi
$p = atan2(1,0)*2;
3;

# fill each pixel with 0x000000
@P=("\000"x3) x ($W);

BEGIN {
    $";

    # $_ is going to be (lc only) rot-13'ed, and "d" is used as a space
    # separator so:

    # S adds "sub " to the code we're building
    sub S {
        $_ .= 'fhoq'
    }

    # K keeps track of a number, and adds "f<n>{" to the stream, where
    # <n> is replaced by an incrementing counter
    sub K {
        $_ .= 's' . (( $n++ )) . (('' . '' . '{' ));
    }

    ;

    # Y adds its argument onto the stream.
    sub Y {
        $_ .= $_ [0]
    }

    ;

    # sub f0{$x/=2;$y/=2}
    S;K;
    Y ( '' . '$k/=2;$' . '' . 'l/=2}');

    # sub f1{$x/=3;$y/=3;$x+=$w/4}sub u{pri "P6\n$w $w\n255\a", @P}
    # note that below the "i" gets expanded to "int"
    S;K;
    0;
    Y( '$k/=3;$l/=3;$k+=$j/4}fhoqh{' . '' . 'cevq"P6\a$jq$j\a255\a",q@P}');

    # sub f2{$t=$p/3;r}
    S;K;
    Y('$g=$c/3;e}');

    # sub f3{$t=$p/-3;r}
    S;K;
    Y(q.$g=$.  .'c/-3;'    .'e}');

    # sub f4{$t=$p*2/3}
    S;K;
    Y( '$' . 'g'. '=' . '$c*2' . '/3}');
    ;

    # turn "i" into "int"
    s/v/vag/x;

    # p turns a pixel ($x-$w/2 , $y-$w/2) to white
    # ie, the origin for x and y is in the middle
    sub p {
        $P[ $x + $w/2 + int($y)*$w + $W/2 ] = "\377" x 3
    }

    # r rotates the cursor vector by $t
    sub r {
        $z = $x * cos($t) + $y * sin($t);
        $y = $x * sin($t) - $y * cos($t);
        $x = $z
    }

    ;

    # rot13
    tr/a-z/n-za-m/;

    # substitute d (q before the rot) with " "
    s/d/$"/gx;

    $x++;
    $y=4;
    
    # the finished code that will be run is:
    #   # move the cursor closer in by a factor of 2
    #   sub f0 {
    #       $x /= 2;
    #       $y /= 2
    #   }
    #
    #   # f1 moves the cursor inwards by a factor of 3 (to the centre)
    #   # and then move it out by a quarter of the canvas along the x-axis
    #   # this gives a lot of the filament structure
    #   sub f1 {
    #       $x /= 3;
    #       $y /= 3;
    #       $x += $w/4
    #   }
    #
    #   # print out our canvas as a PPM
    #   sub u {
    #       print "P6\n$w $w\n255\n", @P
    #   }
    #
    #   # Theta = pi/3 ; rotate
    #   sub f2 {
    #       $t=$p/3;
    #       r
    #   }
    #
    #   # Theta = -pi/3 ; rotate
    #   sub f3 {
    #       $t = $p/-3;
    #       r
    #   }
    #
    #   # This does nothing
    #   sub f4 {
    #       $t = $p*2/3
    #   }

    eval $_
}

;
s/ /japh/;

sub T {
    ;
    # $a is our argument
    $a = $_[0];
    ;
    # $b > 0 if $a not element of [0,4]
    # I did this by doing:
    # perl -e 'map { $b=(($_ & (($_|($_<<1))<<1))>>2)|($_>>3); print "\$a=$_ \$b=$b\n" } (0..20)'

    $b=(( $a & (( $a | (+$a<<1) ) <<1 )) >> 2 ) | ( $a>>3 );

    if($b) {
        # note this is a reassign back to our argument
        # does nothing
        $_[0] = ""
    }
    else {
        # note this is a reassign back to our argument
        # calls f0,f1,f2,f3,f4 as appropriate, and then puts the pixel
        $_[0] = "f$a;p;"
    }
}

;;

# apply lots of potential transformations using T.
for(1 .. 20000*10) {
    $_ = int(rand(20));
    T $_;
    0;
    # since T reassigns back in its argument, we eval that
    eval $_
}

# u prints out the ppm
u

$Id: christmasppc-summary.shtml,v 1.2 2003/01/03 00:08:50 mbm Exp $