Tuesday, February 15

TDD Bowling and J

Intro: This was written in response to a suggestion by (@RonJeffries) that someone present a test driven design example in J. I doubt I am the best person to be writing such things, but I thought I would give it a shot.

Using http://xprogramming.com/articles/scala-bowling-i-think/ as a pattern, the implementation code follows the tests which the code is expected to satisfy, and each implementation should satisfy all of the preceding tests.

assert 0 -: score 20$0

assert 90 -: score 20$4 5

score=: +/

In other words, for simple games, we just add up the scores from each ball.

assert 22 -: score 20$6 4 5 2

spareBonus=: 0 0, _1}. 10= 2 +/\ ]
score=: +/ .* (1 + spareBonus)

In other words:

   (2 +/\ ]) 6 4 5 2 0 0
10 9 7 2 0

we find the pairwise sums, then then we find the cases which are equal to ten, shift them over a bit, and use that to multiply the scores from balls that follow spare frames.

assert 31 -: score 20 {. 10 5 2 7

strikeBonus=: [: +/ _1 _2 |.!.0"0 1=&10
score=: +/ .* (1 + spareBonus + strikeBonus)

Here, we are doing something similar, but we do not need pairwise sums to find strikes, =&10 is good enough.  On the other hand, we need to give strikes two bonus balls, so we shift that result into two different positions:

   (_1 _2 |.!.0"0 1=&10) 10 5 2 7 0 0
0 1 0 0 0 0
0 0 1 0 0 0
   ([: +/ _1 _2 |.!.0"0 1=&10) 10 5 2 7 0 0
0 1 1 0 0 0

assert 300 -: score 12$10

Problem: we need a frame structure, and none of the implementations so far has cared about frames.  A simple way to implement frames involves a while loop.  Each time through the loop we can decide where the next frame starts:  

score=:3 :0
while.(i<#y) *. 10>#score do.
bonus=. 10 e. +/\ 2{.i}. y
score=.score, +/(2+bonus){.i}.y
i=. i+2-10=i}y

Note that to determine if we have a spare or a strike we always examine two balls, looking for the possibility that all the pins got knocked down before the end of the frame.  Anyways, we wind up with three possibilities:

strike: first ball 10, frame size 1, frame score based on 3 balls
spare: first and second balls sum to 10, frame size 2, frame score based on 3 balls
open: first and second balls sum to less than 10, frame size 2, frame score based on 2 balls

But this code looks so very different from the previous iterations.  What happened here?

One issue, I think, is that the tests lead me to add features before developing the structure that was needed for those features to make sense. So, I was happily computing simple sums, when we all know that those would never be suitable. And then, when faced with a perfect game, I decided to bite the bullet write something to deal with the frames.

That said, this code does look cleaner than the implementation at https://docs.google.com/document/pub?id=16sicMNkODy-zeE1pkTtDWYd3x374g-Htx1XcXq96b9c and as an added bonus, it passes all my randomly selected tests there.

One of the benefits of using J, is that it’s fairly trivial to re-architect a program when the architecture is in expressions rather than in control structures. However, that benefit does not appear very useful here, and may even be something of a hazard for this style of development, but that could also be my lack of practice.

Nevertheless, the core "sum" mechanism is present, in some form, in all of these implementations.

Finally, if you were building a scoring machine, you would want an event driven scorer (though a variation on this monolith could work in the event handler, recomputing "the entire game so far" after each ball).

See also:

No comments:

Post a Comment