<?xml version="1.0" encoding="windows-1252"?>
<node id="761053" title="The golf course looks great, my swing feels good, I like my chances (Part II)" created="2009-04-30 03:31:19" updated="2009-04-30 03:31:19">
<type id="120">
perlmeditation</type>
<author id="176576">
eyepopslikeamosquito</author>
<data>
<field name="doctext">
&lt;P&gt;
&lt;blockquote&gt;

&lt;P&gt;
&lt;I&gt;
"To be competitive at Perl golf, you have to be a Perl expert, with years of language experience" -- Yorkey
&lt;/I&gt;
&lt;/P&gt;

&lt;P&gt;
&lt;I&gt;
"Rubbish! In TPR 3, Mickut of Finland was on the leaderboard &lt;B&gt;two hours&lt;/B&gt; after he learnt Perl! And Perl expert Petdance, despite many years of Perl experience, finished more than 80 strokes behind the leaders in the Fonality golf challenge" -- me
&lt;/I&gt;
&lt;/P&gt;

&lt;P align="right"&gt;
&lt;small&gt;
-- Yorkey and me arguing during a lunchtime walk to Kirribilli
&lt;/small&gt;
&lt;/P&gt;

&lt;/blockquote&gt;
&lt;/P&gt;

&lt;P&gt;
Were it not for this chance argument during a lunchtime walk with my workmate
and good friend, Yorkey, I probably would have given up the
[id://759963|Roman to Decimal] golf game after a few weeks.
After all, I was completely stuck at that time with my Perl solution
and had no fresh ideas to try out.
However, Yorkey's stubborn refusal to change his point of view provoked me into
proving my point to him by playing this golf in languages I hardly knew,
namely Ruby, Python and PHP.
&lt;/P&gt;

&lt;P&gt;
&lt;blockquote&gt;

&lt;P&gt;
&lt;I&gt;
That's &lt;B&gt;not&lt;/B&gt; my earring! ... All right, what's going on?
&lt;/I&gt;
&lt;/P&gt;

&lt;P align="right"&gt;
&lt;small&gt;
-- My (non-hacker) wife after finding [audreyt]'s earring sitting on our bedside table
&lt;/small&gt;
&lt;/P&gt;

&lt;/blockquote&gt;
&lt;/P&gt;

&lt;P&gt;
I decided to start with Ruby because I at least had a passing familiarity with that
language after [audreyt] stayed for a few days at my home while my wife was out of town.
During her stay, we went through the library section of
&lt;a href="http://www.pragprog.com/titles/ruby/programming-ruby"&gt;the Pickaxe book&lt;/a&gt;
together because Audrey felt it would make a great model for documenting the Perl 6 libraries.
Unfortunately, the absent-minded Audrey left one of her earrings behind on our bedside
table and I assumed it belonged to my wife, so just left it there. On her return, my wife
saw the earring sitting on her bedside table and freaked. I had previously told my wife
that Perl hacker "autrijus" was coming to stay for a few days while she was away.
Luckily, when I hastily explained that autrijus had become audrey, my wife judged it
unlikely that I would invent such a story and quickly calmed down. :-)
&lt;/P&gt;

&lt;P&gt;
After a full month of play, the Perl leader was [robin] on 60 strokes, with Ruby languishing
far behind on 73. So I naturally thought it "impossible" for Ruby to overtake Perl
in this game -- and ludicrous to suggest that I might be able to beat my Perl
solution in Ruby.
My expectations were much lower than that; I simply wanted to be competitive
in Ruby (anywhere in the 70s would be fine) so as to shut Yorkey up.
&lt;/P&gt;

&lt;readmore&gt;

&lt;P&gt;&lt;B&gt;Taking the Lead in Ruby&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
Since I'd already worked out some basic [id://600665|magic formulae] by that time,
I naturally started converting these to work with Ruby. Unfortunately,
in addition to mapping M -&gt; 1000, D -&gt; 500, C -&gt; 100, L -&gt; 50, X -&gt; 10, V -&gt; 5, I -&gt; 1,
I needed to further map the trailing newline to zero because I could
find no short way of removing it in Ruby. In Perl, the trailing newline was
easily removed via &lt;CODE&gt;/./g&lt;/CODE&gt;. This extra newline mapping invalidated
most of the Perl magic formulae I had previously found, so I had to adjust my
magic formula searcher and start searching all over again.
&lt;/P&gt;

&lt;P&gt;
&lt;blockquote&gt;

&lt;P&gt;
&lt;I&gt;
This program might contain the fastest known existing implementation of full forward crypt
&lt;/I&gt;
&lt;/P&gt;

&lt;P align="right"&gt;
&lt;small&gt;
-- &lt;a href="http://www.mail-archive.com/golf@perl.org/msg01568.html"&gt;Ton Hospel&lt;/a&gt; sensationally wins a &lt;B&gt;Perl&lt;/B&gt; golf tournament by writing the fastest &lt;B&gt;C&lt;/B&gt; program
&lt;/small&gt;
&lt;/P&gt;

&lt;/blockquote&gt;
&lt;/P&gt;

&lt;P&gt;
I won't bore you here with the gory details of my magic formula searcher, written
in C, for speed.
To get a feel for what these searching programs look like, take a look at my
simple one, described in [id://600665], or
&lt;a href="http://www.xs4all.nl/~thospel/ASIS/zlet5.tar.gz"&gt;Ton's much more complex one&lt;/a&gt;.
&lt;/P&gt;

&lt;P&gt;
My new and improved search program found a Ruby-friendly magic formula easily enough,
and I was flabbergasted when my first Ruby approach, despite using the &lt;I&gt;nine&lt;/I&gt; character
&lt;CODE&gt;each_byte&lt;/CODE&gt; method, was equal leader on 73 strokes!
&lt;CODE&gt;
p=t=0
$&lt;.each_byte{|c|n=10**(238%c%19%4)&gt;&gt;-c%29%2;p&lt;n&amp;&amp;t-=2*p;t+=p=n}
p t
&lt;/CODE&gt;
As you can see, this is just a straightforward translation of the original
algorithm I was using in Perl, albeit with a magic formula replacing the
Perl regex-based lookup table. As I was quick to point out to Yorkey, I didn't
need to be a Ruby expert to do this, just needed to know the core parts
of the language and, more importantly, &lt;I&gt;find a good algorithm&lt;/I&gt;.
&lt;/P&gt;

&lt;P&gt;
I started with &lt;CODE&gt;each_byte&lt;/CODE&gt; only because I couldn't get the shorter
&lt;CODE&gt;getc&lt;/CODE&gt; function to work. For example, this attempt:
&lt;CODE&gt;
n=t=0
t+=n-n*(n&lt;(n=10**(238%c%19%4)&gt;&gt;-c%29%2)?2:0)while c=getc
p t
&lt;/CODE&gt;
failed to compile with "undefined local variable or method `c'".
Following Eugene's advice of "Can't possibly work, try it anyway", I
changed &lt;CODE&gt;c&lt;/CODE&gt; to &lt;CODE&gt;C&lt;/CODE&gt; (uppercase variables are
constants in Ruby):
&lt;CODE&gt;
n=t=0
t+=n-n*(n&lt;(n=10**(238%C%19%4)&gt;&gt;-C%29%2)?2:0)while C=getc
p t
&lt;/CODE&gt;
and it worked! The screen was littered with "warning: already initialized constant C"
messages (written to stderr), but these don't matter to codegolf, which only cares
about what is written to stdout. Combining with a well-known Ruby golfing
trick of replacing the three-char &lt;CODE&gt;238&lt;/CODE&gt; with the two-char
&lt;CODE&gt;?ascii-char-with-ord-of-238&lt;/CODE&gt;, shortened my solution to 65.
As you might expect, I felt elated at leading the Ruby experts by eight strokes!
And, more importantly, forcing Yorkey to eat his words.
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Choking on my breakfast cereal&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
Complacency is dangerous in golf and I had become complacent.
If anyone had told me at this time that I could reduce my Ruby solution
from 65 strokes all the way down to 53, I would have declared them insane.
&lt;/P&gt;

&lt;P&gt;
After basking for months in my newly acquired Ruby fame, I almost choked
on my breakfast cereal when I checked the codegolf leaderboard one morning and noticed that
&lt;a href="http://codegolf.com/boards/conversation/view/201"&gt;Python golfing god, Mark Byers&lt;/a&gt;,
had posted a 59 stroke Ruby solution.
This was intolerable! Back to work.
&lt;/P&gt;

&lt;P&gt;
After experimenting some more with Ruby's evaluation order, I came up with
a weird spaceship operator 60 stroke solution:
&lt;CODE&gt;
n=t=0;t+=n.*1+n&lt;=&gt;n=10**(238%C%19%4)&gt;&gt;-C%29%2while C=getc;p t
&lt;/CODE&gt;
I've left the &lt;CODE&gt;238&lt;/CODE&gt; above for readability, but my submitted solution
naturally used the &lt;CODE&gt;?ascii-char-with-ord-of-238&lt;/CODE&gt; dirty trick mentioned earlier.
This solution introduces another dirty Ruby golfing trick, namely using
a &lt;CODE&gt;.*&lt;/CODE&gt; "method call" for "multiplies" rather than &lt;CODE&gt;*(...)&lt;/CODE&gt;, thus
saving a stroke by eliminating the parens. You can try this trick routinely
when golfing in Ruby whenever you need to change operator precedence -- though
it doesn't always work, Ruby's parsing being pretty quirky, in my experience.
By the way, it was this Ruby solution that inspired my weird 62 stroke Perl spaceship operator
solution, mentioned in the previous article, an example of transferring ideas from
one language to another. Often the hard part in golf is generating new ideas to try,
and using multiple languages is a fertile source of fresh ideas.
&lt;/P&gt;

&lt;P&gt;
Alas, I couldn't improve this solution further, so switched to Python, hoping to
take revenge on the "Python golfing god" there.
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Python Baby Steps&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
As you might expect by now, my first Python attempt was the same ol' same ol':
&lt;CODE&gt;
t=p=0
for c in map(ord,raw_input()):n=10**(84169%c%5)&gt;&gt;-c%29%2;t+=n-2*p*(p&lt;n);p=n
print t
&lt;/CODE&gt;
89 strokes! This solution bears a close resemblance to the earlier Ruby ones.
Notice that Python, like Perl, but unlike Ruby, does not need to map the
trailing newline because the Python &lt;CODE&gt;raw_input&lt;/CODE&gt; function removes it.
&lt;/P&gt;

&lt;P&gt;
Two further strokes were whittled easily enough with:
&lt;CODE&gt;
t=p=0
for c in raw_input():x=84169%ord(c);n=10**(x%5)&gt;&gt;x/4%2;t+=n-2*p*(p&lt;n);p=n
print t
&lt;/CODE&gt;
Notice too that in Python, alone among the four languages, assignment is not an operator. This proved a chronic nuisance in this game because I couldn't see any opportunity
to exploit evaluation order to eliminate the "previous value" variable (&lt;CODE&gt;p&lt;/CODE&gt;
in the Python solution above).
&lt;/P&gt;

&lt;P&gt;
Another generally applicable golfing tip is to study every single built-in function the
language has to offer, especially the short ones. When I did that, the Python
&lt;CODE&gt;hash&lt;/CODE&gt; function caught my eye. I wonder if it could be used in
a magic formula? Well, it seems to have better properties for this
purpose than &lt;CODE&gt;ord&lt;/CODE&gt; and is only one stroke longer.
Definitely worth a try. It did indeed improve things:
&lt;CODE&gt;
t=p=0
for c in map(hash,raw_input()):n=10**(c/619%4)&gt;&gt;c%8/5;t+=n-2*p*(p&lt;n);p=n
print t
&lt;/CODE&gt;
... but only by one stroke.
86 strokes now, but still a gaping eight strokes behind the Python golfing god.
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Going for the Outright Lead&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
&lt;blockquote&gt;
&lt;I&gt;
Necessity is the mother of invention
&lt;/I&gt;
&lt;/blockquote&gt;
&lt;/P&gt;

&lt;P&gt;
The Python solutions are different to the Ruby and Perl ones in that
you have to either &lt;CODE&gt;map&lt;/CODE&gt; the &lt;CODE&gt;hash/ord&lt;/CODE&gt; functions,
or assign them to a variable, as in &lt;CODE&gt;x=84169%ord(c)&lt;/CODE&gt;, because
all the magic formulae seen so far use the character &lt;I&gt;twice&lt;/I&gt;.
It occurred to me therefore, that if I could find a magic formula that
used each character in the input string &lt;I&gt;once only&lt;/I&gt; that would be a big
saving in Python. How to find such a formula? I have no idea, but I
played around one afternoon, just trying stuff, and stumbled on a gem:
&lt;CODE&gt;
t=p=0
for r in raw_input():n=10**(205558%ord(r)%7)%9995;t+=n-2*p%n;p=n
print t
&lt;/CODE&gt;
&lt;/P&gt;

&lt;P&gt;
By way of explanation, notice that the magic
formula &lt;CODE&gt;205558%ord(r)%7&lt;/CODE&gt; maps
M -&gt; 3, D -&gt; 6, C -&gt; 2, L -&gt; 5, X -&gt; 1, V -&gt; 4, I -&gt; 0
as shown in the following table:
&lt;/P&gt;

&lt;P&gt;
&lt;table border="1"&gt;
 &lt;tr&gt;&lt;th&gt;Roman&lt;/th&gt;&lt;th&gt;m&lt;/th&gt;&lt;th&gt;10**m&lt;/th&gt;&lt;th&gt;10**m%9995&lt;/th&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;M&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;1000&lt;/td&gt;&lt;td&gt;1000&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;D&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;1000000&lt;/td&gt;&lt;td&gt;500&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;C&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;100&lt;/td&gt;&lt;td&gt;100&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;L&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;100000&lt;/td&gt;&lt;td&gt;50&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;V&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;10000&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;/P&gt;

&lt;P&gt;
Generally, formulae that map M -&gt; 3, C-&gt; 2, X -&gt; 1 and I -&gt; 0 are
highly effective because applying "%NNNN", where NNNN &gt; 1000, does
not mangle the already matching 10**m, so instead of requiring seven
lucky hits, you now need only three (D, L and V).
&lt;/P&gt;

&lt;P&gt;
Combining this new formula with the same modulo trick I used to move my Perl
solution from 62 to 60 strokes reduced my Python
solution to 78 strokes and tied for the lead with Mark Byers!
&lt;/P&gt;

&lt;P&gt;
&lt;blockquote&gt;
&lt;I&gt;
Code Golf is 10% strategy, 90% tactics
&lt;/I&gt;
&lt;/blockquote&gt;
&lt;/P&gt;

&lt;P&gt;
Actually, I've found many different 78 stroke Python solutions, but none shorter.
Here are some more variations in the middle line:
&lt;CODE&gt;
n=10**(hash(r+"*N_")%9)%2857;t+=n-2*p%n;p=n
n=10**(hash(r+"@M4")%7)%9995;t+=n-2*p%n;p=n
n=10**(hash(r*37509)%7)%9995;t+=n-2*p%n;p=n
n=10**"IXCMVLD".find(r)%9995;t+=n-2*p%n;p=n
n=10**(494254%ord(r)/9)%4999;t+=n/2-p%n;p=n
&lt;/CODE&gt;
The last one is noteworthy in that it uses a different mapping, namely
M -&gt; 2000, D -&gt; 1000, C -&gt; 200, L -&gt; 100, X -&gt; 20, V -&gt; 10, I -&gt; 2.
Also noteworthy is that, because it divides by two (&lt;CODE&gt;n/2&lt;/CODE&gt;),
it also works with a:
&lt;CODE&gt;
t=p=1
&lt;/CODE&gt;
initialization. This observation will allow us later to exploit a Ruby built-in variable (&lt;CODE&gt;$.&lt;/CODE&gt;),
which is initialized to one.
Note that this second alternative mapping is available, without penalty, in Ruby and Python,
but not Perl and PHP, for various complicated tactical reasons.
These are the sort of tactical tricks that are crucial when fighting for the lead in golf.
&lt;/P&gt;

&lt;P&gt;
Incredibly, applying what I learnt in my Python diversion to Ruby, plus yet another
dirty Ruby trick (using the Perl-inspired Ruby built-in variable &lt;CODE&gt;$.&lt;/CODE&gt; to
eliminate the &lt;CODE&gt;t=0&lt;/CODE&gt;), enabled me to reduce my Ruby solution from 60 strokes
all the way down to 53 and so steal the outright lead from "primo":
&lt;CODE&gt;
n=1;$.+=n/2-n%n=10**(494254%C/9)%4999while C=getc;p$.
&lt;/CODE&gt;
&lt;/P&gt;

&lt;P&gt;
&lt;blockquote&gt;
&lt;I&gt;
Success is never final -- Winston Churchill
&lt;/I&gt;
&lt;/blockquote&gt;
&lt;/P&gt;

&lt;P&gt;
Of course, I can't prove that I've found the optimal magic formula.
It's also likely that further language or algorithmic golfing improvements will be found,
especially given my relative inexperience in Ruby and Python.
&lt;/P&gt;

&lt;P&gt;
In the next installment of this series, I'll show off my PHP solutions.
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Leaderboards, end of April 2009&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
&lt;B&gt;All languages&lt;/B&gt; (281 entries):
&lt;CODE&gt;
  1st  53  eyepopslikeamosquito  Ruby
  2nd  55  primo                 Ruby
  3rd  56  flagitious            Ruby
  4th  58  ySas                  Perl
  5th  58  leonid                Ruby
  6th  59  bearstearns           Perl
  7th  59  Mark Byers            Ruby
  8th  59  kounoike              Perl
  9th  60  robin                 Perl
 10th  61  arpad                 Perl
&lt;/CODE&gt;
&lt;/P&gt;

&lt;P&gt;
&lt;B&gt;Perl&lt;/B&gt; (69 entries):
&lt;CODE&gt;
  1st  55  eyepopslikeamosquito
  2nd  58  ySas
  3rd  59  bearstearns
  4th  59  kounoike
  5th  60  robin
  6th  61  arpad
  7th  61  shinh
  8th  62  0xF
  9th  66  ersagun
 10th  66  redneval
 11th  66  o0lit3
 12th  66  Aidy
 13th  67  szeryf
 14th  70  ott
 15th  73  jojo
 16th  73  yvl
 17th  73  acura
 18th  77  Jasper
 19th  78  agenticarus
 20th  79  tybalt89
 21st  82  yojeb
 22nd  82  grizzley
 23rd  85  twice11
 24th  86  yanick-walloper
 25th  87  Ciaran
 26th  87  olivier
 27th  88  justin
 28th  91  SubStack
 29th  93  wendelscardua
 30th  94  Trinary
 31st  95  tripa
 32nd  95  dropbear
 33rd  95  sprimmer
 34th  98  yanick
 35th  99  chargrill
 36th  99  kjan
 37th  99  Jocelyn
 38th  100 k12u
 39th  102 duranain
 40th  104 sphx95
&lt;/CODE&gt;
&lt;/P&gt;

&lt;P&gt;
&lt;B&gt;Ruby&lt;/B&gt; (86 entries):
&lt;CODE&gt;
  1st  53  eyepopslikeamosquito
  2nd  55  primo
  3rd  56  flagitious
  4th  58  leonid
  5th  59  Mark Byers
  6th  71  shinh
  7th  71  tryeng
  8th  73  yvl
  9th  73  bitsweat
 10th  76  ozy4dm
&lt;/CODE&gt;
&lt;/P&gt;

&lt;P&gt;
&lt;B&gt;Python&lt;/B&gt; (87 entries):
&lt;CODE&gt;
  1st  78  Mark Byers
  2nd  78  eyepopslikeamosquito
  3rd  79  tryeng
  4th  79  hallvabo
  5th  80  tha
  6th  80  primo
  7th  81  BjarkeEbert
  8th  85  hiro.suzuki
  9th  91  mick
 10th  91  gtalpo
&lt;/CODE&gt;
&lt;/P&gt;

&lt;P&gt;
&lt;B&gt;PHP&lt;/B&gt; (62 entries):
&lt;CODE&gt;
  1st  70  eyepopslikeamosquito
  2nd  89  hiro.suzuki
  3rd  89  Methedrine
  4th  89  W
  5th  89  Trinary
  6th  90  arpad
  7th  90  angpoo
  8th  91  rollercoaster375
  9th  92  El Hombre Gris
 10th  92  morten
&lt;/CODE&gt;
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;Leaderboard Update&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
One month later, the leaderboard changes are:
&lt;CODE&gt;
hallvabo              Python    79 -&gt;  72
hendrik               Python    94 -&gt;  90
d3m3vilurr            Python   108 -&gt; 101
yvl                   Perl      73 -&gt;  61
falsetru              Python   216 -&gt;  95
Leinad                Perl         -&gt; 152
eyepopslikeamosquito  Python    78 -&gt;  72
eyepopslikeamosquito  PHP       70 -&gt;  68
Kalindor              PHP          -&gt;  93
tomatoring            Python       -&gt; 295
&lt;/CODE&gt;
&lt;/P&gt;

&lt;P&gt;&lt;B&gt;References&lt;/B&gt;&lt;/P&gt;

&lt;P&gt;
&lt;ul&gt;
  &lt;li&gt; [id://759963]
  &lt;li&gt; [id://762180]
  &lt;li&gt; [id://763105]
  &lt;li&gt; [id://811919]
  &lt;li&gt; [id://814900]
  &lt;li&gt;&lt;a href="http://codegolf.com/"&gt;Golf competitions in Perl, Ruby, Python or PHP&lt;/a&gt;
  &lt;li&gt; [id://600665]
  &lt;li&gt; &lt;a href="http://www.therubygame.com/challenges/5/submissions?order=shortest"&gt;therubygame: Roman numerals. What are they good IV?&lt;/a&gt;
  &lt;li&gt; &lt;a href="https://gist.github.com/1712932"&gt;therubygame deconstruct by czetter&lt;/a&gt;
&lt;/ul&gt;
&lt;/P&gt;

&lt;/readmore&gt;
</field>
</data>
</node>
