# Bowling Kata

Our team’s been doing Katas to get the hang of TDD. One such kata (calculating bowling scores) struck me as insanely simple with pattern matching in F#:

let rec score acc = function
| 10 :: (a :: b :: _ as t) -> score (acc + 10 + a + b) t // strike
| 10 :: t -> score acc t // final frames following strike
| a :: b :: c :: [] when a + b = 10 -> acc + 10 + c // final frame spare
| a :: b :: (c :: _ as t) when a + b = 10 -> score (acc + 10 + c) t // spare
| h :: t -> score (acc + h) t // normal roll
| [] –> acc

// Tests

let
test game expected =
let result = score 0 game
if result <> expected then
printfn "%A = %i (expected %i)" game result expected

test [0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0] 0 // gutter game
test [1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1] 20 // all ones
test [5;5;3] 16 // one spare (wrong!)
test [5;5; 3;0; 0;0; 0;0; 0;0; 0;0; 0;0; 0;0; 0;0; 0;0] 16 // one spare
test [0;0; 0;0; 0;0; 0;0; 0;0; 0;0; 0;0; 0;0; 0;0; 5;5;3] 13 // one spare

test [10;3;4;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0] 24
// one strike
test [10;10;10;10;10;10;10;10;10;10;10;10] 300 // perfect game
test [1;4; 4;5; 6;4; 5;5; 10; 0;1; 7;3; 6;4; 10; 2;8;6] 133

### Oops!

Thanks to Brian Friesen (below in the comments) for pointing out a bug. This wasn't correctly handling spares in the final frame! One line fix (highlighted).

It brings up another issue in one of the test cases as well. [5;5;3] should produce a different score depending on whether it's at the start or end of a complete game. Added two cases for that.

Tags

1. Joel says:

I'm pretty sure that both cases of "_ as t" can be replaced with simply "t".

2. @Joel: I don't believe so. The 't' is naming the whole parenthesized expression, not just the underscore. For example:

let a :: (b :: c :: _ as t) = [1; 2; 3; 4]

val t : int list = [2; 3; 4]

val c : int = 3

val b : int = 2

val a : int = 1

Notice that 't' is the whole original tail while 'b', and 'c' allow "peeking" ahead. I think this is the very thing that makes this solution so simple.

3. Scott Seely says:

The one strike line should total 17. Strike equals the 10 + the next two balls. In your case, this is 17.

4. Scott Seely says:

Crap- nevermind/disregard. Did the math again, gotta add the 3 +4 again.

5. Joel says:

Ah, I see what you mean. Very clever.

6. Brian Friesen says:

It doesn't seem to handle the final frame correctly.

test [1;4; 4;5; 6;4; 5;5; 10; 0;1; 7;3; 6;4; 10; 2;8;6] 133

results in:

[1; 4; 4; 5; 6; 4; 5; 5; 10; 0; 1; 7; 3; 6; 4; 10; 2; 8; 6] = 139 (expected 133)

Example taken from Uncle Bob:

butunclebob.com/…/Bowling%20Game%20Kata.ppt

7. @Brian You're right! I just fixed it and added an "Oops!" section above. Thanks for catching!