Ah, Christmas! What could possibly be better than sitting around the table with your friends and family and playing code golf! … Wait, what?
Oh, right, it’s not Christmas yet. But you probably want to prepare yourself for it anyway!
If you haven’t noticed already, there’s a great website for playing code golf: https://code-golf.io/. The cool thing about it is that it’s not just for perl 6! At the time of writing, 6 other langs are supported. Hmmm…
Anyway, as I’ve got some nice scores there, I thought I’d share some of the nicest bits from my solutions. All the trickety-hackety, unicode-cheatery and mind-blowety. While we are at it, maybe we’ll even see that perl 6 is quite concise and readable even in code golf. That is, if you have a hard time putting your Christmas wishes on a card, maybe a line of perl 6 code will do.
I won’t give full solutions to not spoil your Christmas fun, but I’ll give enough hints for you to come up with competitive solutions.
All I want for Christmas is for you to have some fun. So get yourself rakudo to make sure you can follow along. Later we’ll have some pumpkin pie and we’ll do some caroling. If you have any problems running perl 6, perhaps join #perl6 channel on freenode to get some help. That being said, https://code-golf.io/ itself gives you a nice editor to write and eval your code, so there should be no problem.
Some basic examples
Let’s take Pascal’s Triangle task as an example. I hear ya, I hear! Math before Christmas, that’s cruel. Cruel, but necessary.
There’s just one basic trick you have to know. If you take any row from the Pascal’s Triangle, shift it by one element and zip-sum the result with the original row, you’ll get the next row!
So if you had a row like:
1 3 3 1 |
All you do is just shift it to the right:
0 1 3 3 1 |
And sum it with the original row:
1 3 3 1 | |
+ + + + | |
0 1 3 3 1 | |
= | |
1 4 6 4 1 |
As simple as that! So let’s write that in code:
for ^16 { put (+combinations($^row,$_) for 0..$row) } |
You see! Easy!
… oh… Wait, that’s a completely different solution. OK, let’s see:
.put for 1, { |$_,0 Z+ 0,|$_ } … 16 |
Output:
1 | |
1 1 | |
1 2 1 | |
1 3 3 1 | |
1 4 6 4 1 | |
1 5 10 10 5 1 | |
1 6 15 20 15 6 1 | |
1 7 21 35 35 21 7 1 | |
1 8 28 56 70 56 28 8 1 | |
1 9 36 84 126 126 84 36 9 1 | |
1 10 45 120 210 252 210 120 45 10 1 | |
1 11 55 165 330 462 462 330 165 55 11 1 | |
1 12 66 220 495 792 924 792 495 220 66 12 1 | |
1 13 78 286 715 1287 1716 1716 1287 715 286 78 13 1 | |
1 14 91 364 1001 2002 3003 3432 3003 2002 1001 364 91 14 1 | |
1 15 105 455 1365 3003 5005 6435 6435 5005 3003 1365 455 105 15 1 |
Ah-ha! There we go. So what happened there? Well, in perl 6 you can create sequences with a very simple syntax: 2, 4, 8 … ∞
. Normally you’ll let it figure out the sequence by itself, but you can also provide a code block to calculate the values. This is awesome! In other languages you’d often need to have a loop with a state variable, and here it does all that for you! This feature alone probably needs an article or 𝍪.
The rest is just a for loop and a put call. The only trick here is to understand that it is working with lists, so when you specify the endpoint for the sequence, it is actually checking for the number of elements. Also, you need to flatten the list with |
.
If you remove whitespace and apply all tricks mentioned in this article, this should get you to 26 characters. That’s rather competitive.
Similarly, other tasks often have rather straightforward solutions. For example, for Evil Numbers you can write something like this:
.base(2).comb(~1) %% 2 && .say for ^50 |
Remove some whitespace, apply some tricks, and you’ll be almost there.
Let’s take another example: Pangram Grep. Here we can use set operators:
‘a’..‘z’ ⊆ .lc.comb && .say for @*ARGS |
Basically, almost all perl 6 solutions look like real code. It’s the extra -1 character oomph that demands extra eye pain, but you didn’t come here to listen about conciseness, right? It’s time to get dirty.
Numbers
Let’s talk numbers! 1 ² ③ ٤ ⅴ ߆… *cough*. You see, in perl 6 any numeric character (that has a corresponding numeric value property) can be used in the source code. The feature was intended to allow us to have some goodies like ½ and other neat things, but this means that instead of writing 50
you can write ㊿
. Some golfing platforms will count the number of bytes when encoded in UTF-8, so it may seem like you’re not winning anything. But what about 1000000000000
and 𖭡
? In any case, code-golf.io is unicode-aware, so the length of any of these characters will be 1.
So you may wonder, which numbers can you write in that manner? There you go:
-0.5 0.00625 0.025 0.0375 0.05 0.0625 0.083333 0.1 | |
0.111111 0.125 0.142857 0.15 0.166667 0.1875 0.2 | |
0.25 0.333333 0.375 0.4 0.416667 0.5 0.583333 0.6 | |
0.625 0.666667 0.75 0.8 0.833333 0.875 0.916667 1 | |
1.5 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 8 8.5 9 10 | |
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
45 46 47 48 49 50 60 70 80 90 100 200 300 400 500 | |
600 700 800 900 1000 2000 3000 4000 5000 6000 7000 | |
8000 9000 10000 20000 30000 40000 50000 60000 70000 | |
80000 90000 100000 200000 216000 300000 400000 | |
432000 500000 600000 700000 800000 900000 1000000 | |
100000000 10000000000 1000000000000 |
This means, for example, that in some cases you can save 1 character when you need to negate the result. There are many ways you can use this, and I’ll only mention one particular case. The rest you figure out yourself, as well as how to find the actual character that can be used for any particular value (hint: loop all 0x10FFFF characters and check their .unival
s).
For example, when golfing you want to get rid of unnecessary whitespace, so maybe you’ll want to write something like:
say 5max3 # ERROR |
It does not work, of course, and we can’t really blame the compiler for not untangling that mess. However, check this out:
say ⑤max③ # OUTPUT: «5» |
Woohoo! This will work in many other cases.
Conditionals
If there is a good golfing language, that’s not Perl 6. I mean, just look at this:
puts 10<30?1:2 # ruby |
say 10 <30??1!!2 # perl 6 |
Not only TWO more characters are needed for the ternary, but also some obligatory whitespace around <
operator! What’s wrong with them, right? How dare they design a language with no code golf in mind⁉
Well, there are some ways we can work around it. One of them is operator chaining. For example:
say 5>3>say(42) |
If 5 is ≤ than 3, then there’s no need to do the other comparison, so it won’t run it. This way we can save at least one character. On a slightly related note, remember that junctions may also come in handy:
say ‘yes!’ if 5==3|5 |
And of course, don’t forget about unicode operators: ≥
, ≤
, ≠
.
Typing is hard, let’s use some of the predefined strings!
You wouldn’t believe how useful this is sometimes. Want to print the names of all chess pieces? OK:
say (‘♔’…‘♙’)».uniname».words»[2] | |
# KING QUEEN ROOK BISHOP KNIGHT PAWN |
This saves just a few characters, but there are cases when it can halve the size of your solution. But don’t stop there, think of error messages, method names, etc. What else can you salvage?
Base 16? Base 36? Nah, Base 0x10FFFF!
One of the tasks tells us to print φ to the first 1000 decimal places. Well, that’s very easy!
say ‘1.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374847540880753868917521266338622235369317931800607667263544333890865959395829056383226613199282902678806752087668925017116962070322210432162695486262963136144381497587012203408058879544547492461856953648644492410443207713449470495658467885098743394422125448770664780915884607499887124007652170575179788341662562494075890697040002812104276217711177780531531714101170466659914669798731761356006708748071013179523689427521948435305678300228785699782977834784587822891109762500302696156170025046433824377648610283831268330372429267526311653392473167111211588186385133162038400522216579128667529465490681131715993432359734949850904094762132229810172610705961164562990981629055520852479035240602017279974717534277759277862561943208275051312181562855122248093947123414517022373580577278616008688382952304592647878017889921990270776903895321968198615143780314997411069260886742962267575605231727775203536139362’ |
Yes!!!
Okay, that takes a bit more than 1000 characters… Of course, we can try to calculate it, but that is not exactly in the Christmas spirit. We want to cheat.
If we look at the docs about polymod, there’s a little hint:
my @digits-in-base37 = 9123607.polymod(37 xx *); # Base conversion |
Hmmm… so that gives us digits for any arbitrary base. How high can we go? Well, it depends on what form we would like to store the number in. Given that code-golf.io counts codepoints, we can use base 0x10FFFF (i.e. using all available codepoints). Or, in this case we will go with base 0x10FFFE, because:
☠☠☠⚠⚠⚠ WARNING! WARNING! WARNING! ⚠⚠⚠☠☠☠ | |
THIS WILL MAKE YOUR COMPUTER IMPLODE! | |
UNICODE STRINGS ARE SUBJECT TO NORMALIZATION SO YOUR | |
DATA WILL NOT BE PRESERVED. HIDE YOUR KIDS, HIDE YOUR | |
WIFE. HIDE YOUR KIDS, HIDE YOUR WIFE. HIDE YOUR KIDS, | |
HIDE YOUR WIFE. AND HIDE YOUR HUSBAND. | |
☠☠☠⚠⚠⚠ WARNING! WARNING! WARNING! ⚠⚠⚠☠☠☠ |
When applied to our constant, it should give something like this:
𢍟𪱷𢕍𭙪𝧒𘝞뎛磪끝寡𥵑𱫞𛰑𗋨䨀𰌡𧮁𦅂嗟𧽘𱀉𫍡𪏸𐂇ந𰑕𨬎𦔏o |
How do we reverse the operation? During one of the squashathons I found a ticket about a feature that I didn’t know about previously. Basically, the ticket says that Rakudo is doing stuff that it shouldn’t, which is of course something we will abuse next time. But for now we’re within the limits of relative sanity:
say ‘1.’,:1114110[‘o𦔏𨬎𰑕ந𐂇𪏸𫍡𱀉𧽘嗟𦅂𧮁𰌡䨀𗋨𛰑𱫞𥵑寡끝磪뎛𘝞𝧒𭙪𢕍𪱷𢍟’.ords] |
Note that the string has to be in reverse. Other than that it looks very nice. 192 characters including the decoder.
This isn’t a great idea for printing constants that are otherwise computable, but given the length of the decoder and relatively dense packing rate of the data, this comes handy in other tasks.
All good things must come to an end; horrible things – more so
That’s about it for the article. For more code golf tips I’ve started this repository: https://github.com/AlexDaniel/6lang-golf-cheatsheet
Hoping to see you around on https://code-golf.io/! Whether using perl 6 or not, I’d love to see all of my submissions beaten.
🥧♫