Hack.lu 2015 Writeups

From YobiWiki
Jump to navigation Jump to search

2015

2015 Perl golf

Goal: Write a Perl program that takes a parameter as input and outputs a filtered version with alternating upper/lower case letters of the (english) alphabet. Non-letter characters have to be printed but otherwise ignored.
Example:

Input	Hello World! Hallo Welt!
Output	HeLlO wOrLd! HaLlO wElT!

Rules:

  • You have 1 second.
  • You have 45 (ASCII) chars.
  • Do not flood the server.

The first thing is to find how the server will use your script.
Input isn't read from stdin but is expected as the first argument, so you can try locally with

perl -e 'print @ARGV[0]' 'Hello World! Hallo Welt!'
Hello World! Hallo Welt!

Then come with a strategy to convert letters as asked and squeeze it.
Here is our solution: (37 chars)

$_=pop;s/\pL.*?(\pL|$)/\u\L$&/g;print

But here is actually how Fluxfingers designed this challenge: they tried internally, kept the best one-liner they could come with, which was 38 chars long, and extended the limit to 45 to give some room to the participants.
The best solution Fluxfingers had in mind: (38 chars)

$_=pop;s/\pL/uc$&^$"^($x^=$")/ge/print 

So we were one char shorter \o/

A little word of explanation of our solution:

$_=pop;s/\pL.*?(\pL|$)/\u\L$&/g;print
$_=                                    assign to the Perl "current var", so we don't have to mention it later
   pop;                                we need @ARGV[0], pop is shorter
       s/             /      /g;       search and replace, g=repeat
         \pL.*  \pL                    We want one letter followed optionally by some stuff followed by one letter
                                          \w captures too much (also [0-9_]) so we rely on that Unicode magic \pL
                                          So first captured letter needs to be in uppercase and second captured letter needs to be lowercase
            .*?                        Problem with .* is that it captures too much, including letters.
                                          One could write [^a-zA-Z]* but that's not golfing anymore
                                          Adding the "?" reduces the "greediness" of the expression to capture as few chars as possible
               (\pL|$)                 An issue wit \pL only is that it fails if there is an odd number of letters in the string
                                          So here we say one letter, or end of string, to capture that corner case.
                       \u\L$&          $& is what was captured by the regex, \u means first char uppercase, \L means lowercase for the remaining letters
                                print  Should I explain?

After the CTF, we got it even shorter, now 36 chars!:

$_=uc pop;s/\pL.*?\pL/\u\L$&/g;print
   uc                                  Set whole input as uppercase
                  \pL                  No more dirty (\pL|$) trick here

The trick is to avoid the corner case of odd number of letters by setting the whole input uppercase first

Alternative solution of 36 chars, optimizing what was discussed here:

$_=lc pop;s/\pL/$&^($u^=$")/ge;print
$_=                                    assign to the Perl "current var", so we don't have to mention it later
   lc                                  Set whole input as lowercase
      pop;                             we need @ARGV[0], pop is shorter
          s/   /           /ge         search and replace, g=repeat, e=evaluate again the result
            \pL                        We want one letter: \w captures too much (also [0-9_]) so we rely on that Unicode magic \pL
               
                $&                     $& is what was captured by the regex
                  ^     $"             xor it with $" which evaluates to " ". The effect is that a letter XORed by 0x20 toggles its case.
                  ^($u^=$")            Actually that's to be done only half of the time so we xor with a variable that is constantly xored with " "
                                         so $u value is successively 0x20, 0x00, 0x20, 0x00 etc
                                print  Should I explain?

UPDATE: someone managed to get down to 34 chars!:

print pop=~s/\pL/lc$&^($u^=$")/ger
                                 r      perform non-destructive substitution and return the new value

Actually we can go even further for some of those solutions if we tolerate that the script doesn't always work.
One can simply reload multiple times the script till the server provides a compatible input to get the flag.
E.g. 29 chars and 28 chars:

$_=pop;s/\w.*?\w/\u$&/g;print
print pop=~s/\w.*?\w/\u$&/gr

worked after a few reloads on the sentence "body. want Law the help of and, being. a, graduates"

2015 PHP Golf

Same as for Perl Golf, but now we've 62 chars max.
The lazy solution: (57 chars)

<?=`echo '$argv[1]'|perl -pe 's/\pL.*?(\pL|$)/\u\L$&/g'`;

Apparently Fluxfingers solution was:

<?=preg_filter("/\pL/e",'($0|" ")^chr($m^=32)',$argv[1]);