Scala Bowling … more function cowbell
My critiXXXXX advisors expected a more function-oriented solution. Here's something a bit better, perhaps ...
I was at a loss as to how to deal with a function that scores one part of an input list and consumes a different part. Finally I got the notion of a function of two parameters, a total and a list, which adds to the total based on the scoring part and calls itself recursively with the appropriate list remainder.
The result is that I have dropped the Framer class, and added the recursive function to the Game class, which now looks like this:
class Game(rolls: List[Int]) {
def score = scoreReduce(0,rolls)
def scoreReduce(total: Int, rolls: List[Int]):Int = {
rolls match {
case Nil => total
case 10::second::third::Nil
=> total+10+second+third
case first::second::third::Nil if (first+second==10)
=> total+first+second+third
case 10::second::third::theRest
=> scoreReduce(total+10+second+third, second::third::theRest)
case first::second::third::theRest if (first + second == 10)
=> scoreReduce(total+first+second+third, third::theRest)
case first::second::theRest
=> scoreReduce(total+first+second, theRest)
}
}
}
</code>
I started with the four black cases. Those pass all my tests but for the perfect game, which was scored 320 rather than 300. What happened was that on the last strike, we score up the 300, but then we return the rest of the rolls (the bonus rolls) for one last go, as if they were a frame. In bowling, the bonus rolls only provide a bonus for a mark in the final frame: they do not constitute a frame of their own.
The two red cases differ from the black ones in that they are explicitly talking about the end of the list: first::second::third::Nil and so on. Instead of recurring one more time, they just return the final value.
So … I feel this is more in the direction of a functional solution. I can imagine going further, and perhaps I will. We have the fact of a two-part argument, the total and the rolls. We might be able to put the total in front, or on the back, and update it there. Front will be easier, as Scala prefers us to work at the head. Let’s see …
Hmm, that turned out to be easier than I thought. I just put the total on the front, and adjusted all the patterns. The result now looks like this:
class Game(rolls: List[Int]) { def score = scoreReduce(0::rolls) def scoreReduce(rolls: List[Int]):Int = { rolls match { case total::Nil => total case total::10::second::third::Nil => total+10+second+third case total::first::second::third::Nil if (first+second==10) => total+first+second+third case total::10::second::third::theRest => scoreReduce(total+10+second+third::second::third::theRest) case total::first::second::third::theRest if (first + second == 10) => scoreReduce(total+first+second+third::third::theRest) case total::first::second::theRest => scoreReduce(total+first+second::theRest) } } }
</code>
I think that is in some sense better. All the work is being done in the patterns, which is perhaps appropriate. The patterns are a bit opaque because of the necessity to embed the two or three rolls of interest into the middle of the list (total, rolls of interest, …). It might be possible to improve that aspect with some typographic tricks.
Anyway, I think it is more functional than it was and I look forward to comments. It has been a long time since I worked in a functional style. (No smart remarks now … :) )