Binary Golf Grand Prix (BGGP) is a yearly competition aimed to create the smallest file that accomplishes a specific task. This year's goal was to create the smallest self-replicating file which fulfills the following requirements:
This article describes my approach for solving this year's competition (BGGP4) with Perl.
My initial goal was to replicate BGGP4's example with Perl and hopefully reduce its size a little bit. The example provided was a shell script that used the cp and echo commands:
#!/bin/sh cp $0 4 echo 4
I quickly found the first obstacle: the use of cp. There is no built-in function in Perl for copying files. There is, however, a standard module called File::Copy which provides the function copy for copying files or filehandles and cp as a short alias for it. Good, but this brought a second obstacle: importing a module would add extra bytes to the size of the script (by adding use File::Copy "cp"). Then I noticed the following line in BGGP4's submission template:
Target Environment (How do we run the file?):
Great, that means we can specify how to run the script, or in other words, how to call the perl interpreter. This give us the option to import File::Copy using the -M switch. An initial script looked like this (14 bytes):
cp$0,4;print 4 (to run: perl -MFile::Copy=cp bggp4.pl)
Notice how we got rid of unnecessary whitespaces and the shebang. Can we do better? I looked into other perl switches that might be useful and I found the -l[octnum] switch which "assigns $\ (the output record separator) to have the value of octnum so that any print statements will have that separator added back on". This means we can specify a value that will get appended to the print function each time we use it. After a few attempts I reduced the initial script to the following code (12 bytes):
cp$0,4;print (to run: perl -l064 -MFile::Copy=cp bggp4.pl)
In this case the octal value 064 is the equivalent of 52 decimal, which is the ASCII value of the character '4'. Then when the print function is called without arguments the character '4' gets printed automatically. This was my first submission.
I kept thinking on how to make use of the perl interpreter to further reduce the size of the script. The idea of passing the source code of the script as a command line argument came to mind. That would have required a combination of eval and $ARGV which was no better than the original approach. Then I went back to perl switches and I read about the -s switch that converts switches (and their values) to variables in the main program. Awesome. This means we can specify a switch with the contents of the inital script and execute it with eval (8 bytes):
eval($a) (to run: perl -MFile::Copy=cp -s bggp4.pl -a="cp\$0,4;print 4")
Can we do better? Yes. By default some Perl functions (including eval) use the value of the special variable $_ when no arguments are supplied. We can then use the -_ switch and call eval with no arguments (4 bytes):
eval (to run: perl -MFile::Copy=cp -s bggp4.pl -_="cp\$0,4;print 4")
This was my second and final submission. To be honest, I'm not sure if the (ab)use of the perl interpreter fits in the original goal of BGGP4 but I surely had a good time learning new stuff about Perl.