Showing posts with label tdd bowling j. Show all posts
Showing posts with label tdd bowling j. Show all posts

Sunday, April 3

TDD bowling, and J, revised, part 1

So, I implemented a bowling scorer, in J, using TDD, and I was not very satisfied with the result.  And I keep meaning to come back to it...

The problem, as it were, is that "you get what you test for".  And all I was testing for was a single numeric result that was the total score.  I left the expression of bowling score "frames" as extra credit work.  And then I went back and tried an extra credit exercise, but it had classic second system effects, and I was left feeling dissatisfied, and I am sure that both of the people that bothered to look at my posts were not particularly enthralled, either.

So... I need better tests.

In other words, instead of testing for "just the right score" I also need to test for "the right structure".   And that means that I need to design "the right structure".  So what is "the right structure"?

I am going to go with the structure proposed by Ron Jeffries:  An array with one row for each frame, padded out so that each row has 3 columns.  I am ambivalent about the third column, so I might come back and revise this to say that the number of columns can not exceed 3.   I am also ambivalent about requiring the thing always be 10 rows, so I am going to say that the number of rows cannot exceed 10.  I can come back and add tests requiring exactly 10 rows and exactly 3 columns if I later on decide that these are important.  So what can I test here?

Since this structure represents the game's score, I am going to require that +/@, on the structure gives me the right score.

   assert 0 -: +/@, 20#0

I also need to check that the result has rows and columns (and nothing else)

   assert 2 -: #$ 10 3$0


I also need to check that I have no more than 10 rows and no more than 3 columns:


   assert 10 3 >: 10 3$0


And, I these tests are fundamental, every test that I add needs to incorporate these tests.

   isScore=: -:&(+/@,)  (10 3 >: ]),  2 -: #@$@]
   assert 0 isScore 10 3$0
|length error: isScore
|   assert 0     isScore 10 3$0
   isScore=: -:&(+/@,)  (10 3 >: $@]),  2 -: #@$@]
   assert 0 isScore 10 3$0
|assertion failure: assert
|       assert 0 isScore 10 3$0
   isScore=: -:&(+/@,),  (10 3 >: $@]),  2 -: #@$@]
   assert 0 isScore 10 3$0

Ok, so now I have my test rig.  Part 2 will be to about writing the bowling scorer itself.


I want to stop here so this post remains bite, sized, and because I have already been working on this blog entry for half an hour. 


Hmm... and it looks like some whitespace has crept into the post itself which is not showing up in the blogger edit interface.  I will have to be cautious to avoid that in the future, but after fighting it for 10 minutes I have decided to give up on trying to get that whitespace out of this post.

Thursday, February 17

Spares vs. strikes (take 2)

So... spares vs. strikes is a hot issue for some people, in bowling.  So how about if we make tracking them be a requirement?

Now, instead of being some kind of strange coding artifact, they need to be a part of the result.  This also means that our previous result (the total score) is no longer an acceptable result.  Instead, let's say that we need a running total of the score for each frame, along with an indication of whether the frame was open, a spare, or a strike.

Now, I could start over from scratch here, but I am not inclined to do so.  This seems like an easy change to make, so let's just jump right in:

assert (10#"0 'open';0) -: bowlingScores 20#0

In other words, I want
   bowlingScores 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
to give me the result
+----+----+----+----+----+----+----+----+----+----+
|open|open|open|open|open|open|open|open|open|open|
+----+----+----+----+----+----+----+----+----+----+
|0   |0   |0   |0   |0   |0   |0   |0   |0   |0   |
+----+----+----+----+----+----+----+----+----+----+

(Note that I am using ascii boxes here, rather than line drawing characters, because in my experience some browser implementations do the wrong thing with line drawing characters. Yes, ascii is ugly. But it is ubiquitous.)

  bowlingScores=:3 :0
   i=.0
   frameType=.frameScore=.''
   while.(i<#y) *. 10>#frameScore do.
    bonus=. 10 = +/\ 2{.i}. y
    frameType=.frameType, #.bonus
    frameScore=.frameScore ,+/(2++./bonus){.i}.y
    i=. i+2-10=i}y
   end.
   (frameType { ;:'open spare strike') ,: ]&.> +/\frameScore
  )

This next test also succeeds, so that is probably good enough?

assert ((,: +each/\)/10#"0'strike';30) -: bowlingScores 12#10

Here, I was looking for:

   (,: +each/\)/10#"0'strike';30
+------+------+------+------+------+------+------+------+------+------+
|strike|strike|strike|strike|strike|strike|strike|strike|strike|strike|
+------+------+------+------+------+------+------+------+------+------+
|30    |60    |90    |120   |150   |180   |210   |240   |270   |300   |
+------+------+------+------+------+------+------+------+------+------+

Since that test all by itself is getting a bit complicated (and because @RonJefferies suggested it might be a good idea), I should take time here to explain what I am doing:

The phrase 10#"0'strike';30 works the same way that the previous phrase (10#"0'strike';30) worked.  I am just replicating each item into a list of ten copies.  Meanwhile the / operator puts the verb on its left between each item of the array on its right.  In this case that means:

   (10#<'strike')  (,: +each/\) (10#<30)

That (,: +each/\) is a hook, which means that the left verb combines the left argument (10 copies of the box containing the word 'strike') with the result of the right verb on the right argument (the individually boxed running sum of 10 copies of the number 30).

And, no, that implementation of bowlingScores was not quite good enough:

   bowlingScores 10 1 0 6 3 10 0 6 4 2 10 6 4 1 0 1 3
|index error: bowlingScores

The problem, here, is that when i=5, I am computing 10=+/\10 0 which is equivalent to 10=10 10 which gives me a result of 1 1, and #. 1 1 is 3.  But my list of frame types only has 3 elements: 0: open, 1: spare, 2: strike.

I could deal with this by adding a fourth 'strike' element to that list.  Or, I could deal with it by preventing the result obtained from #.bonus from ever being larger than 2.

But, also, my implementation of bowlingScores was a bit unwieldy, with no modularity between the (hypothetically useful) computational code and the (perhaps dubious) presentation code.  So perhaps I should refactor it:

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

 bowlingScores=:3 :0
   types=. ;:'open spare strike'
   ((types{~{.),: [:]&.>{:) bScores y
 )

Here, bScores does all the work, and bowlingScores just repackages that to match my (somewhat arbitrary) test oriented representation.  And, it also works when I have a strike followed by a gutter ball:

   bScores 10 1 0 6 3 10 0 6 4 2 10 6 4 1 0 1 3
 2  0  0  2  0  0  2  1  0  0
11 12 21 41 47 53 73 84 85 89

   bowlingScores 10 1 0 6 3 10 0 6 4 2 10 6 4 1 0 1 3
+------+----+----+------+----+----+------+-----+----+----+
|strike|open|open|strike|open|open|strike|spare|open|open|
+------+----+----+------+----+----+------+-----+----+----+
|11    |12  |21  |41    |47  |53  |73    |84   |85  |89  |
+------+----+----+------+----+----+------+-----+----+----+

I did not have an explicit test result I was checking here -- just running the code was test enough.  I think that that is an appropriate test, sometimes.

Perhaps next up would be to track down a full set of specifications about how scoring machines work, in bowling alleys, and add requirements so that the scoring routine could represent game progress at any point in a game.  In other words, we would want an adjacent turky count, a done indicator, as well as indicators for incomplete spares and incomplete spikes, and perhaps other requirements.  (But probably leave out the timer that shuts down your lanes if you take too long, since that can be imposed independently of the scoring system.)

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
i=.0
score=.''
while.(i<#y) *. 10>#score do.
bonus=. 10 e. +/\ 2{.i}. y
score=.score, +/(2+bonus){.i}.y
i=. i+2-10=i}y
end.
+/score
)


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: