Jon Bettinger has found a failing test! Excellent!

In a comment on an earlier article, Jon points out that the special check for a final bonus frame can trigger on the 9th frame, not just the 10th. The game can end 10-x-y with a strike in the 9th frame and an open frame in the 10th! A test that succeeds and one that fails are:

    it("should count not double count tenth frame bonus rolls" ) {
    	expect(11)(Game.score(List(0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 10,1,0)))
    }

    // fails !!!
    it("should count non-mark tenth frame after strike in ninth" ) {
    	expect(12)(Game.score(List(0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 10, 1,0)))
    }

</code>

My initial–but still thoughtful–reaction to this is that the current tail-recursive approach is doomed to fail without frame counting. I have a fairly simple counting fix in hand. And I suspect that a two-phase approach, parsing frames from the left and then summing, might be made to work without counting. Stylistic arguments aside, I think these count-free solutions are getting hard to understand.

I’d like to see one that shows up as clean and yet in full functional style.

I’ll show my current solution below. But first, some thoughts on helping.

The Code in My Head Always Works

It is fun, when someone is having trouble with a program, to suggest a “solution” that is significantly different from what they are doing, and it is tempting to declare it to work, and to be better. Recent experience here in the comments on these articles confirms the experience of my entire lifetime:

The code in my head always works. The code in the computer … not so much. What does this tell us about how to help some poor fool, like me, from a distance? Jon’s test is a perfect way to help, in that it’s just about the code and the fact that it doesn’t work. He didn’t even have to say “you tiny fool”.

But what about suggestions? It seems to me that a really good way would be to provide a little package built with tests, preferably TDD tests. A great way would be to provide an article, not like these Scala ones but like some earlier ones, showing the program, and understanding, growing bit by bit.

That’s really going to be time-consuming. It takes ages to write that kind of article. Great though.

If we want to help the other person rather than just dump an answer on them, we need to gauge just how quick they’re going to be. <p style="padding-left: 30px;">I recall the first time I paired with Ward Cunningham. We were working on an interpreter program for a DSL my team was writing. He looked at what I had and–I believe–saw that it sucked. He said “You know how to write an interpreter in Smalltalk, right?” I hope I said something like “Well, I’m not sure, what do you mean?” Ward gave a hint; I didn’t get it; he gave a stronger hint; I still didn’t get it. This repeated about down to the point where he had to say “OK, now type a star …”.</p> <p style="padding-left: 30px;">At no time did he make me feel bad or say something to make himself feel superior. He just increased the level of help until I could get it … and not a bit more.</p> <p style="padding-left: 30px;">I should be that good, even once. :)</p> Applying this notion here, it seems to me that a “vague” kind of help would be a snippet of code that does something that might be useful if transformed into the current domain. Another kind might be a pointer to a technique. Finally, some working code.

When Philip gave me that snippet of code that I built on, I was in full learning mode, so I didn’t feel threatened by his solution. And I don’t feel threatened by people who are smarter than I am anyway, as I have been around them for so long. I might be a bit unique in that. So one needs to be cautious when tossing someone a solution. They might reject the solution; they might just install it; or they might examine it and learn.

This is easier in an environment of pairing and conversation, and difficult in a medium like this. Anyway, just something to think about.

The Current Solution

My current guess is is that a frame count is absolutely necessary to the solution, because it is seems impossible to look at the last few rolls and know whether they represent one frame or two. The reason is that the final frame is special.

So I decided to put a frame number back in, inside my function. First I changed the tests to insert 1 (integer representing first frame) as a parameter in the score method, then made it work, then added a score(rolls) polymorphic entry point. Could have been done another way. Anyway, here’s what I have, and it works. Well … it passes all the tests.

object Game {
   def score(frame: Int, rolls: List[Int]):Int = {
      rolls match {
           case List(first, second) => first+second

           case List(first, second, third) if frame == 10 => first+second+third

           case 10::second::third::theRest
              => 10+second+third + score(frame+1, second::third::theRest)

           case first::second::third::theRest if (first + second == 10)
                => 10+third + score(frame+1, third::theRest)

           case first::second::theRest
                 => first+second + score(frame+1, theRest)
      }
   }

   def score(rolls: List[Int]):Int = score(1, rolls)
}

</code>

I’ve highlighted the changes. Let me know what you think, and how to do better. Thanks!