Here is the end result of my implementation of the Bowling Game Kata in Scala. You can see all steps including the tests on CodersDojo.org.
class BowlingGame { var rolls: List[Int] = List() def roll(pins: Int): Unit = { rolls = rolls :+ pins } def score: Int = { scoreRecursive(0, 1, rolls) } def scoreRecursive(currentScore: Int, frame: Int, rolls: List[Int]): Int = { frame match { case 11 => currentScore case f => rolls match { case 10 :: rollsTail // strike => scoreRecursive(currentScore + (rolls take (3) sum), f + 1, rollsTail) case first :: second :: rollsTail if (first + second == 10) // spare => scoreRecursive(currentScore + (rolls take (3) sum), f + 1, rollsTail) case first :: second :: rollsTail // normal => scoreRecursive(currentScore + (rolls take (2) sum), f + 1, rollsTail) case _ // incomplete game => throw new Exception("Only complete games can be scored.") } } } }
Comparing this piece of code with the Java implementation from Uncle Bob (who, no doubt, knows how to write clean Java code) I would conclude that it is more expressive and concise. The pattern matching is a) shorter and b) makes the algorithm stand out more clearly. I do, however, appreciate that this might have to do with personal taste. But, what this implementation with pattern matching does make trivial is to also score partial games. It just requires two more lines of code that seem almost obvious.
class BowlingGame { var rolls: List[Int] = List() def roll(pins: Int): Unit = { rolls = rolls :+ pins } def score: Int = { scoreRecursive(0, 1, rolls) } def scoreRecursive(currentScore: Int, frame: Int, rolls: List[Int]): Int = { frame match { case 11 => currentScore case f => rolls match { case 10 :: rollsTail // strike => scoreRecursive(currentScore + (rolls take (3) sum), f + 1, rollsTail) case first :: second :: rollsTail if (first + second == 10) // spare => scoreRecursive(currentScore + (rolls take (3) sum), f + 1, rollsTail) case first :: second :: rollsTail // normal => scoreRecursive(currentScore + (rolls take (2) sum), f + 1, rollsTail) case last :: rollsTail // partial frame => currentScore + last case nil // partial game => currentScore } } } }
As I described in my previous post, the variable rolls might indicate that the above code could be made more functional by replacing it with some other construct like list comprehension or recursion. However, thinking about it, it sort of makes sense since the rolls are indeed what changes as a game progresses. I couldn’t come up with a more functional implementation that is not less expressive. Can you?
Lukasz took me up on the challenge and got rid of the nested match. I like it. I like it a lot. It makes the code both more expressive and concise.
class BowlingGameTailRecursion { var rolls: List[Int] = List() def roll(pins : Int) = rolls = rolls :+ pins def score = scoreRecursive(0, 1, rolls) def scoreRecursive(score: Int, frame: Int, rolls: List[Int]) : Int = { (frame, rolls) match { case (11, _) => score case (_, 10 :: tail) => scoreRecursive(score + 10 + tail.take(2).sum, frame + 1, tail) case (_, first :: second :: tail) if first + second == 10 => scoreRecursive(score + 10 + tail.head, frame + 1, tail) case (_, first :: second :: tail) => scoreRecursive(score + first + second, frame + 1, tail) case (_, first :: tail) => scoreRecursive(score + first, frame + 1, tail) case _ => score } } }