Some time ago I blogged about my first experience with F#. I took a course Functional Programming Principles in Scala recently and it somewhat opened my eyes on problem solving in functional style. After that I felt confident enough to do something more complex than fizzbuzz with F# so the logical next step was Bowling game kata.
Without further ado here are the tests:
let throwmany(amount, pins) = [ for n in 1..amount -> pins ]
[<TestFixture>]
type BowlingGameTests()=
[<Test>]
member x.``Bowling guttergame``() =
let throws = throwmany(20, 0)
score throws |> should equal 0
[<Test>]
member x.``Throw all ones``() =
let throws = throwmany(20, 1)
score throws |> should equal 20
[<Test>]
member x.``Throw one spare``() =
let sparethrows = [5; 5; 3]
let manythrows=throwmany (17, 0)
let throws = sparethrows @ manythrows
score throws |> should equal 16
[<Test>]
member x.``Throw one strike``() =
let throws = [10; 3; 4] @ throwmany (16, 0)
score throws |> should equal 24
[<Test>]
member x.``Perfect game``() =
let throws = throwmany (12, 10)
score throws |> should equal 300
Tests are same as in the kata slides. Throwmany-function creates a list with as many items as given in amount-parameter and each item has the value given in pins-parameter.
Being the OOP-guy that I am I originally started thinking I need a Game-class that holds the state of the game. Each throw should be registered to Game using throw-method and so on. Then I tried to start thinking in a more functional way. If I give up maintaining state, I wouldn't need any throw-method. Then I wouldn't need any Game-class at all and I could do with just a score-function that takes list of throws. This is what I ended up with:
let private calculate_strike_score (throws: int list) =
if throws.Length>3 then 10+throws.Item(1)+throws.Item(2)
else 10
let private calculate_spare_score (throws:int list) = 10+throws.Item(2)
let rec private score_throws (throws: int list) (score:int) =
match throws with
| [] -> score
| throws when throws.Head = 10
-> score_throws throws.Tail score + calculate_strike_score(throws)
| throws when (throws.Head + throws.Tail.Head = 10)
-> score_throws throws.Tail.Tail score + calculate_spare_score(throws)
| _ -> score_throws throws.Tail.Tail score + throws.Head+throws.Item(1)
let score throws = score_throws throws 0
Score-function calls recursive score_throws-function with initial score of 0. In score_throws the correct route is chosen with pattern matching. If the throws-list is empty, then everything has been done and the final score should be returned. If the first element is 10, then it's a strike. If two first elements combined is 10, then it's a spare. Otherwise it's a regular frame.
That's it. Tests are green. Time to move on to something more complex.
Software professional with a passion for quality. Likes TDD and working in agile teams. Has worked with wide-range of technologies, backend and frontend, from C++ to Javascript. Currently very interested in functional programming.