1. How I won a golf set from reg.ru
at YAPC::EU 2013
Brian McCauley
(nobull)
Discussion at http://perlmonks.org/?node_id=1049290
2. Who am I
• Just Another Perl Hacker ™
• I am the one Matt Trout warned you about
• Don’t to much Perl in my $dayjob
• Still come to YAPC::EU for the community
3. Perl Golf
• Solve a defined problem
• Minimal number of characters
• No points for best practice
• No points for runtime efficiency
• No points for originality
• No points for obfuscation
4. The reg.ru challenge
• Based on the ancient game of Go
• 2 players
• Square grid
• Place black/white stones
• Capture opponent’s stones by surrounding
5. The reg.ru challenge
• 9 x 9 game-state on STDIN
• Enumerate capturing moves on STDOUT
1 2 3 4 5 6 7 8 9
1 ¶
2 x ¶
3 b w b ¶
4 b w w b ¶
5 b b ¶
6 ¶
7 x x ¶
8 w b b w ¶
9 w b b w w ¶
2 4¶
7 9¶
7. $b=++$/x11 .<>;$b = ++$/ x 11 . <>;
Read in the board
• Set $/ to a character that won’t appear
• Slurp STDIN including “n”
– (1,9) not adjacent (2,1) etc.
– Each row 10 characters
• Padding so cell (3,5) is in linear position 35 etc.
$/='';
$b = '11111111111' . <STDIN>;
8. Consider each vacant space
• Consider, in turn, a copy board with “x” in
each empty cell (“ ”)
• Record location of “x” in $i
for $i ( 9 .. 99 ) {
if( ($x = $b) =~ s/^(.{$i}) /$1x/s ) {
# Do stuff with $x
}
}
for my $i ( 11 .. 99 ) {
if( ( my $x = $b) =~ s/^(.{$i}) /$1x/s ) {
# Do stuff with $x
}
}
9. Consider each white stone
• Consider, in turn, a copy board with one “w”
highlighted as “W”
• Don’t need index this time
while( $x =~ /w/g ) {
$_ = "$`W$'";
# Do stuff with $_
}
}
10. Flood fill white stones
• Replace “w” adjacent “W” with “W”
• Repeat until no more
1 while
s/w((?<=W.{10})|(?<=W.)|(?=.{9}W|W))/W/s;
while ( # Repeat until fails
s/
w # Find 'w'
((?<=W.{10}) | # with a 'W' above
(?<=W.) | # or a 'W' to left
(?=W) | # or a 'W' to right
(?=.{9}W) ) # or a 'W' below
/W/xgs # Replace with 'W'
) { }
11. See if we’ve captured
• Find highlighted white stone (“W”) adjacent
an empty cell (“ ”).
• If none then we’ve captured some stones.
/W((?<= .{10})|(?<= .)|(?=.{9} | ))/s ||
print "Hoorah! We captured some tiles";
unless(/W((?<= .{10})|(?<= .)|(?=.{9} | ))/) {
print "Hoorah! We captured some tiles";
}
12. Output the move
• Print the row and column derived from $i .
• Padding means these are div and mod 10
• Exit the “consider each white stone” loop.
• Row & col are also 1st and 2nd characters.
$i=~/./ +
( print "$& $'n" ) +
last
print int($i/10)," ",$i%10,"n";
last;
13. All together now
#!perl
$b = ++$/ x 11 . <>;
for $i ( 9 .. 99 ) {
if( ( $x = $b ) =~ s/^(.{$i}) /$1x/s ) {
while( $x =~ /w/g ) {
$_ = "$`W$'";
1 while
s/w((?<=W.{10})|(?<=W.)|(?=.{9}W|W))/W/s;
/W((?<= .{10})|(?<= .)|(?=.{9} | ))/ ||
$i=~/./ +
(print "$& $'n") +
last
}
}
}
14. TIMTOWDI
#!perl
@g=( d..n, map /./gs, <> );
sub n {
my($i,$r)=@_;
map{
$_=1 and $r=n($i+1)+n($i-1)+
n($i+10)+n($i-10)==1 if /w/;
$_=$r=1 and $0=$i if/ /
} $g[$i];
$r
}
map{
$0=~/./ + print "$& $'n" if $g[$_] eq 'w' && n $_
} 0 .. 99
15. Bonus post-YAPC slide
• Now 175! (stole ideas Sergei and Timur)
#!perl -ln0
map {
$i=$-[0]+11;
{
map {
1 while
s/w((?<=W.{10})|(?<=W.)|(?=.{9}W|W))/W/s;
/W((?<= .{10})|(?<= .)|(?=.{9} | ))/s ||
$i=~/./ +
print("$& $'") +
last
} "$`W$'" while/w/g
}
} "$`x$'" while/ /g
Hinweis der Redaktion
I am Just Another Perl Hacker – 10 years ago quite active in the community but I am the person Matt warned you about…
On the first day recall reg.ru announced a Perl Golf competition.So what is this Perl Golf anyhow? I’ve never done this before and BooK is not here so I thought I’d have a go.
place pieces black and white alternately on a board capture opponent’s pieces turning them your colour by surrounding them.
Test suite provided by reg.ru as you’d expect in Perl.
Now let’s break that down
WARNING: 2 transitions!Need the \n so there are 10 char/line also as a border so we don’t see 1,9 adjacent 2,1No strictures andno whitespace except the space between 11 and .
Consider all the places I could place my next black stone. Actually this considers even characters in the leading padding and the newlines but these will never be spaces.
NO TRANSITIONSMark theMention that assigning $_ and using $` and $’ are bad practice. Considering every white stone is very wasteful and against my instincts but there are no points for speed in this game.
OK this is the guts of the solution. Flood fill “W” over “w”. Use look-ahead/behind assertions find “W” 10 characters back (above), 1 character back (left), 9 forward (below), imediately after (right). 9 v 10 because regex cursor is now after the character in question Need the /s because I need . to match \n. Need two look behind clauses because Perl does not support variable width look-behind.
This is the same regex you’ve just seen LHS of s/// except the “w” is now “W” and “W” is “ “
Use regex not div and mod for brevity. Use + not ; so we don’t need do{} block. Unfortunately need () around print but still a saving. next(LABEL) would be more idiomatic but this is Golf!
All but 2 of the whitespace can be removed.Up until 01:50 (217)– woke up repeatedly with small optimisations like using $i=~/./.
Sergi’s solution is a more elegant approach using arrays and recursion. At the submission deadline it passed all the tests but was a bit longer than mine. With a bit of fine tuning it’s now 179 (v 205) but I found a couple of bugs that the tests missed. Is 26 enough to fix it.
ApparentlySergei Mozhaiskybeat me but missed the deadline – actually there was a bug in his solution.Borrowed the idea of using map{} with single element LIST and a statement modifier from Sergei Mozhaisky’s solution.Also borrowed a better way of printing the output from TimurNozadze’s solution meaning I did not need the padding and could use #! flags to slurp the input. I got my solution down from 205 to 175. There was a rumour that BooK beat me but he denied it.