Code Golf

September 29th, 2006

Many years ago, I didn’t program just for money. I actually did it because I loved the problem solving aspect. I kind of miss that - I spend way too much time formatting reports and generating graphs these days, not enough time just messing around for the hell of it.

The other day, I discovered CodeGolf.com as a new hobby/competitive sport. The idea is simplicity itself: take one of the challenges, produce working code in your interepreted language of choice (Perl, Python, PHP or my favourte, Ruby) that pumps out the right output and then get it working in the smallest number of bytes possible. It’s an exercise in obfuscation and confusion, sure, but it’s great fun for somebody like me.

The challenge that drew me in was Oblongular Number Spirals which looks deceptively simple. Let’s just say that after about an hour I realised it wasn’t - it’s actually very, very hard. I ended up with a solution that uses a really bad edge detection algorithm as I walk across a virtual grid (actually a multi-dimensional array) making sure I don’t bump into other rows and the edges.

A working solution must have taken me a total of six hours coding time - which shows how out of practice I am with this pure algorithm work. Reducing it down to the smallest number of bytes was the easiest bit, but falling asleep last night I realised that there were ways I could easily reduce it further from my poor show of 426 bytes. Anyway, it was great fun, and for the masochists amongst you, here is my slightly longer (line breaks are in this one) script:

s,t=gets.split.map!{|s|s.to_i};b=[];
k,d,x,y,e,f,g,h=s*t,0,0,0,-1,-1,s,t;o=k.to_s;
(1..k).each{|i|b[y]=[]if b[y].nil?;b[y][x]=i;
case d;
when 0;if x+1==g;d=1;f=y;y+=1;else;x+=1;end;
when 1;if y+1==h;d=2;g=x;x-=1;else;y+=1;end;
when 2;if x-1==e;d=3;h=y;y-=1;else;x-=1;end;
when 3;if y-1==f;d=0;e=x;x+=1;else;
y-=1;end;end};
(0...t).each{|r|m="";
(0...s).each{|q|z=b[r][q].to_s;m<<" "*(o.length-z.length);
m<<" "unless q==0;
m<< z;};puts m;}

Isn’t that awful? Taking a language as beautiful as Ruby and making it look like that! I should be ashamed of myself. For some reason, in some browsers, the last line of the above block gets chopped but should read ‘m << z;};puts m;}'

Here's a slightly less painful version:

s, t = gets.split.map!{ |s| s.to_i }
b=[]
k, d, x, y, e, f, g, h = s*t, 0, 0, 0, -1, -1, s, t
o = k.to_s

(1..k).each{ |i|
  b[y] = []
  if b[y].nil?
    b[y][x] = i   
    case d
    when 0
      if x + 1 == g
        d = 1; f = y; y += 1
      else
        x += 1
      end
    when 1
      if y + 1 == h
        d = 2; g = x; x -= 1
      else
        y += 1
      end
    when 2
      if x - 1 == e
        d = 3; h = y; y -= 1
      else
        x -= 1
      end
    when 3
      if y - 1 == f
        d = 0; e = x; x += 1
      else
        y -= 1
      end
    end
}

(0...t).each{ |r|
  m = ""
  (0...s).each{ |q|
    z = b[r][q].to_s
    m << " " * (o.length - z.length)
    m << " " unless q == 0
    m << z
  }
  puts m
}

That produces output that passes the codegolf tests, so if somebody wants to take it and make the obvious space-saving optimisations (yes, I spelt that word incorrectly the last time I used it), that will get it down to less than 200 bytes, feel free.

And to any potential clients reading this right now, let me promise you, hand on heart, I would never dream of putting code that awful into your production environments. The code you buy would be elegant, commented, and use meaningful variable names. Honest. ;-)