Tuesday, July 14, 2015

Coding a C# version of Threesus AI into Perl5

I mentioned back in March one of my "big" projects was to convert the Threesus AI from C# to Perl5. Many reasons for this other than sheer stupidity :). I know Perl5 well and can think in Perl. I don't know C# and I have no idea if some C# code is brilliant or simply a mistake.

Another reason is that Perl5 is much more portable than C#, which is largely Win-based. Another reason  is that RPerl  is starting to be usable but requires that I use 'vanilla' Perl. (Actually my code tends to be pretty plain. perltidy doesn't complain much :)

Moo

So my first attempt involved modelling all the C# classes with roughly equivalent Moo classes. I'm keeping the code in a Github repo, Games-Threesus.

vec vs bits

I quickly decided that all the C# bit-wrangling was not for me so I started using an unknown (to me) Perl operator called vec. It allows me to code a 4x4 Threes! board into an 8-byte string (16 cells requiring 4 bits to encode its value = 64 bits) but each nibble is individually addressable. Turns out we only need 16 values to describe each card/cell which fits into 4 bits nicely.

Slowwwww

Well I knew it was going to be slow. The C# code compiles down to very efficient binary code and also takes advantage of multi-CPUs if they are present. The Perl code is big and slow. The recursive calling of the major methods adds hugely to the memory consumption.

First move!

Despite all the huge overhead involved it actually gave me a move command ('SWIPE Up') in a reasonable time. But at this stage there are so many warnings and error messages that I don't know if it worked or not. Single-stepping through the code allowed me to verify that the game simulation is accurate.

Time to rip out the plumbing

I used Moo to model the C# classes because I have a basic knowledge of its use. However it became apparent while single-stepping the code that I don't have enough knowledge of Moo's internals and I wasn't able to follow large swathes of Moo utility code. 

However when I stand back a bit and look at what I'm using Moo classes and methods for, it's not actually very much and the down side is a lot of overhead code which I don't understand. With an eye to RPerl, replacing Moo with 'bless'ed objects will allow me to pinpoint bottlenecks quicker.

Also, I realised I've been using 'constant' for no good reason. Simply using upper-case names is all I really need for the constants in this code.