In November I got together with Tom Moertel and Chris Winters at a coffee shop in Mt. Lebanon (Aldo, sorry, you were closed). The premise was to solve the Bowling Game Kata in Haskell. Tom has already completed this on his own, but we sat down to give it a shot together. Our attempt together follows:
module Bowling where type FrameScore = Int type FrameRolls = [Int] type FrameNumber = Int data FrameType = Strike | Spare | Normal | Ten deriving (Show, Eq) data FrameInfo = Frame FrameScore FrameRolls FrameNumber FrameType deriving (Show) score :: [Int] -> Int score = scoreFrames . parseFrames 1 parseFrames :: Int -> [Int] -> [FrameInfo] parseFrames n (x:y:zs) | n == 10 = Frame (sum (x:y:zs)) (x:y:zs) n Ten : [] | x == 10 = Frame 10 (x:[]) n Strike : parseFrames (n + 1) (y:zs) | (x+y) == 10 = Frame 10 (x:y:[]) n Spare : parseFrames (n + 1) zs | otherwise = Frame (x+y) (x:y:[]) n Normal : parseFrames (n + 1) zs getTwo :: [FrameInfo] -> [Int] getTwo = take 2 . unrollFrames getOne :: [FrameInfo] -> [Int] getOne = take 1 . unrollFrames unrollFrames :: [FrameInfo] -> [Int] unrollFrames [] = [] unrollFrames ((Frame _ (xs) _ _) : ys) = xs ++ unrollFrames ys scoreFrames :: [FrameInfo] -> Int scoreFrames ((Frame s _ n t) : xs) | n == 10 = s | t == Strike = sum (s : getTwo xs ++ scoreFrames xs : []) | t == Spare = sum (s : getOne xs ++ scoreFrames xs : []) | otherwise = sum (s : scoreFrames xs : [])
That’s rather long winded, especially compared to Tom’s version which is short and elegant. I went home that night and created an alternate short and elegant version:
module TinyBowling where score = c 1 a n rs = sum $ take n rs c 10 rs = a 3 rs c f rs = s (f+1) rs s f (10:rs) = a 3 rs + c f rs s f (x:y:rs) | (x+y)==10 = a 3 rs + c f rs | otherwise = a 2 rs + c f rs
I was reviewing this code tonight and discovered that I’d really like to create a short and elegant Perl version. I gave it a shot but I’m not pleased at all. In this round of Golf I think Haskell wins on elegance, hands down:
package Bowling;
sub score { c(1, 0, @_) }
sub c {
my ($f, $s, @rs) = @_;
my ($x, $y, @zs) = @rs;
return $s if $f > 10;
return _c($f, $s, 2, \@rs, [@zs] ) if !($x && $y);
return _c($f, $s, 3, \@rs, [$y,@zs]) if $x == 10;
return _c($f, $s, 3, \@rs, [\@zs] ) if $x + $y == 10;
}
sub _c {
my ($f, $s, $n, $rs, $zs) = @_;
c($f+1, eval(join '+',$s,map $_||0, @{$rs}[0..$n-1]), @{$zs});
}
Yeah, we close earlier in the winter. Too bad we missed you, as I (Rich) have been reading Ross’s blog for a couple of years now - what a freaking amazing guy with vision.
Now if he’d only invest a bit in Lithuania (half Lit here) instead of Estonia
at 10:27 am on December 20th, 2006Small World, Rich. Sometime in 2007 - before the new school year - I’ll be executing Socialtext vision from your place a few times a month.
I think we’ve actually considered hiring some European support folks out of Lithuania. I’m not sure what the status is, but we aren’t ruling out Lithuania.
Maybe you know somebody that should be working for us.
at 10:46 am on December 20th, 2006That reminds me, I should put up a flier in the coffee shop…
labis labis
at 2:35 pm on December 20th, 2006