In May and June we ran another one of our PHP coding contest. We love to play with PHP and offer people a chance to show off their coding skills in exchange for prizes!
This time the contest revolved around creating a class that satisfies a given unit test case.
The test case contained tests for 7 mathematical operations. The contestants did not only have to guess the actual operation, but also provide a class that implements the operation in as few lines as possible.
Two contests for the prize of one
Contest 1 – Smallest solution adhering to decent coding practices
The main contest was just that; because there was some discussion on twitter regarding the ‘fewest lines as possible’ we updated the contest with a rule about ‘good coding practice’ and we ignored coding standard issues; so a brace on the same line or on the next line was considered equally good, and we accepted both regular if constructs and the ternary operator. Some contestants removed all methods from their code and replaced it by a single __call construct, but our jury decided that that is not considered a decent coding practice.
Contest 2 – Smallest possible code size
To cater to those that liked to disregard good coding practices and just create something as small as possible, we had a no-prize side contest for just fame and glory. The entries for this contest are very interesting, we’ll share the top 10 entries we received.
The winners!
As with our previous contests, we have 3 categories. From all the contestants in each category that had all the operations right and had the shortest code we randomly picked a winner. Basically all challenges could be written with one line of actual algorithm code, but you’d be surprised how many needed almost a hundred lines of code to write the class.
Here are our category winners:
- Senior category: David Frame from the United Kingdom
- Medium category: Piotr Młocek from Poland
- Junior category: Luca Lancioni from Italy
Congratulations for winning in your category!
The iPad prize was then randomly raffled amongst the three category winners, and the winner is…..
David Frame. Congrats with your iPad!
The other 2 winners get a ticket to the Dutch PHP Conference 2011.
The solution
Here is a sample class that implements all the algorithms. It’s not a shortest one but it is more clear what the algorithms were in this version:
<?php class NumberCruncher { // Simple addition public function operationA($x) { return $x+4; } // Simple multiplication public function operationB($x) { return $x*3; } // Square / 2 public function operationC($x) { return ($x*$x)/2; } // Factorial (10*9*8*7*6...) public function operationD($x) { if ($x <= 1) { return 1; } else { return $x * $this->operationD($x - 1); }; } // Fibonacci (0 1 1 2 3 5 8 13 21 34 55 89) public function operationE($x) { if ($x<=1) return $x; return $this->operationE($x-1)+$this->operationE($x-2); } // Composite of B and C public function operationF($x) { return $this->operationC($this->operationB($x)); } // Composite of D and E public function operationG($x) { return $this->operationD($x)-$this->operationE($x); } }
The composite were the most difficult ones and were the ones where most contestants went wrong. operationF was also possible without composite by just providing the simple formula it was made up of, and that’s of course fine as well. operationD and operationE have variants that do not use recursion, but the ones that used recursion were generally shorter (even though it might come at a performance penalty, but that wasn’t a criterium in this contest).
Remarkable
Andy Thompson submitted a nice implementation of operationD. The array_product is a fairly unknown function in PHP, but it’s very suitable for this kind of operation:
function operationD($d) { return array_product(range(1, max(1, $d))); }
Another interesting thing to notice is that there are multiple ways to calculate the Fibonacci sequence. Most contestants used a simple recursive formula like the one above. Matthias Steimle was one of the few contestants that used the so-called ‘Golden Ratio‘ (a seemingly magic number that helps calculate the Fibonnaci Sequence and played an important role in Dan Brown’s Da Vinci Code). The golden ratio is the square root of 5, plus 1, divided by 2, and it calculates Fibonnaci numbers in Matthias’ entry like this:
public function operationE($p) { return round((pow((1 + sqrt(5)) / 2, $p) - pow(1 - (1 + sqrt(5)) / 2, $p)) / sqrt(5)); }
The code minimization contest
It is amazing how small a piece of code can get. We didn’t look for actual solutions to the algorithms; anything /
To make it interesting, let’s walk through the top 10 entries starting with the longest. (Some linebreaks were added by yours truly to prevent horizontal scrolling).
10. Peter Lindqvist (276 bytes)
Here’s the number 10 entry:
<? class NumberCruncher{function __call($n,$a){$p=split(';','+=4;*=3;*=$r/2;=o($r);=f($r);*=$r* 4.5;=o($r)-f($r)');eval('$r=$a[0];$r'.$p[ord($n[9])-65].(is_callable('f')?';':';function f($i){return$i>1?f($i-1)+f($i-2):$i;}function o($i){return$i<2?1:$i*o($i-1);}'));return$r;}}
As you can see, minified code becomes quite unreadable, but in Peter’s entry you can still recognize the operations from the algorithm. Peter’s solution is based on using very short function names, a __call interceptor so you only need one function and a bit of eval’d code.
9. Robert Gramm (271 bytes)
<?php class NumberCruncher{function __call($n,$a){$x=$a[0];$s=substr($n,-1); return$s=='A'?$x+4:($s=='B'?$x*3:($s=='C'?$x*$x/2:($s=='D'?$x<2?1:$x*$this->D($x- 1):($s=='E'?$x<2?$x:$this->E($x-1)+$this->E($x-2):($s=='F'?$x*$x*9/2: ($s=='G'?$this->D($x)-$this->E($x):0))))));}}
Similar techniques, and slightly more efficient.
8. Olivier Garcia (225 bytes)
<?class NumberCruncher{function __call($n,$v){$b=explode(' ','v+4 v*3 v*$v/2 v>1?$v*$this->D($v-1):1 v>0?ceil(exp($v/2-1)):0 v*$v*4.5 this->D($v)-$this->E($v)');$v=$v[0]; return eval('return$'.$b[ord(substr($n,-1))-65].';');}}
7. Paul Mitchell (214 bytes)
<?class NumberCruncher{function __call($f,$a){$n=$a[0];$d="self::D($n";$e="self::E($n"; $a=split(' ',"*$n*4.5 *0+$d)-$e) +4 *3 *$n/2 ?$n*$d-1):1 <2?$n:$e-2)+$e-1)"); eval("$r=$n${a[ord(strrev($f))%7]};");return$r;}}
6. Michiel Hakvoort (210 bytes)
<?php class NumberCruncher { public function __call($a, $b) { $t=debug_backtrace(); $f=file($t[1]['file']); preg_match('#s(s*(-?d+(?:.d+)?)#', $f[$t[1]['line']-1], $r); return (float)$r[1]; } }
This entry deserves bonus points for being smaller than the previous entries, but still remain readable. This is the first entry we encounter in the top 10 that makes use of debug_backtrace to get information from the unittests, where previous entries used to still actually calculate the algorithms. So you can see things getting more dirty from here.
5. James Andres (206 bytes)
<?php eval(gzinflate('=~NÑ ~B@^PE^?Ed@ͤ´~^ÜLJ·~ BT^L^E~[`w~MTü÷v´z~Z9ç^~F)~ZL^HíÒ>ò~ RG¼Å¢*ùpWSÖOÔÒ´È~ZÆ~D÷^R:k~@C`^@Ú[~CÁ~Q¶~Eg^C*~H&^@y b~B~]^[º>9éìc^SÐq-^U%Sä~E~@>^EÉ^ØÿÝ£ÒI~U~T~H~TXlH~gAg,ç× ¥@^F «Z0À^@ºëúÆ| ^OD~[^KÉéau~YñR¶^µò~U5¦þ~E^A| ^_~Yn±qü^@'));
A completely different approach. James zipped his code and used a runtime gzinflate and eval to unzip the code and run it! This makes it possible to pass the unit test using only 206 bytes! Arpad Ray also had an approach where he used zip, but since Arpad entered another entry that was even smaller than his zip version, only James’ zip version made the top 10. (If you have trouble running the above code, I’m pretty sure WordPress will have ruined the character encoding, and I added newlines.)
4. Joris van de Sande (154 bytes)
<?class NumberCruncher{function __call($a,$b){static$i=0;preg_match_all('/(s*(.+?) s*,/',file_get_contents($_SERVER['argv'][1]),$m);return$m[1][$i++];}}
This is a debatable entry, because Joris assumes there’s a command line argument, which depending on how you run the unit test may not be present. Joris brings us to a range of entries that is more than 25% smaller than the previous one.
3. Devis Lucato (153 bytes)
<?class NumberCruncher{function __call($a,$b){$t=debug_backtrace();$l=file($t[1][file]); preg_match('#([-.d]+)s*,#',$l[$t[1][line]-1],$r);return$r[1];}}
With a gain of just 1 byte over number 4, this code again cleverly uses debug_backtrace and an efficient file/preg_match combination.
2. Tom van Looy (151 bytes)
2 more bytes down!
<?php class NumberCruncher{function __call($f,$p){return trim(strtok(end(explode('(',strstr(join(file(__CLASS__.'Test.php')),"$f($p[0])",1))),','));}}
I do have to mention that this one didn’t run out of the box because Tom’s code assumes the test class is in the same directory. If you look at the testcase you’ll see that this is not the case. Since the technique is clear however it’s still a nice entry.
1. Arpad Ray (133 bytes)
And kudos to Arpad for completely squashing the competition by using 18 bytes less than the runner up. As you can see, the winning entry even fits in a tweet! Arpad managed to perfect the simplicity of parsing the testcase using debug_backtrace and a simple split.
<?class NumberCruncher{function __call($m,$a){$b=debug_backtrace(); $f=file($b[1][file]);return+next(split('(',$f[$b[1][line]-1]));}}
Conclusion
We had great fun running this contest, we hope you enjoyed it too. Keep an eye on http://ibuildings.com/challenge or follow our Twitter account to make sure you don’t miss the next one!