Session 7

Homework 2 Redux

We discussed:

• many questions folks still have
• the process of attacking this problem
• the Dutch auction allocation strategy: tests to write and the method itself

Make simplifying assumptions until you have the problem under control. For example, even if the spec requires your object to handle unsorted arrays, you might assume that your input is sorted. Write a test that assumes as much and write the code to make it pass. Only then do you worry about sorting the array.

If the test you just wrote makes you think, "I have no idea how to make this test pass", then you need to write a simpler test!

Implementing the Frame Class

We ended last session with a Frame class that almost handles spares completely.

Actually, we ended with a broken test: handling a spare in the tenth frame. By stopping with a broken test, we know just where we want to start up, and we know just what to do next: make this test pass:

```    public void testSpareInLastFrame()    // tries to score "eleventh frame"
{
Frame spareFrame = new Frame();
for ( int i = 0; i < 18; i++ )
spareFrame.roll( 0 );

spareFrame.roll( 5 );
spareFrame.roll( 5 );
spareFrame.roll( 5 );
assertEquals( 15, spareFrame.gameScore() );
}
```

I added the comment before we left, because we had some idea of what the problem was before we quit. Why let that knowledge slip away?

Quick Exercise: Describe at least two ways that Frame can pass this test.

The 10th frame must be sure not to send its next frame a gameScore() message. Keep in mind that there may be an "eleventh frame" holding the bonus ball for a 10th frame that is a spare... But how can a Frame know it is the 10th frame?

• Each Frame is created with a frame number.
• Each Frame is told its frame number when it is asked to compute its gameScore().

Either will work. The first option requires an instance variable, which seems a bit more complicated, so unless we can think of a good reason to go that way, I'd prefer the second option. I can't, ao let's go with the second option.

It's easy to add an argument to gameScore() and have the Frame pass an incremented value to its next frame.

```    public int gameScore( int frameNumber )
{
if (( nextFrame == null ) || ( frameNumber == 10 ))
return score();
return score() + nextFrame.gameScore( frameNumber + 1 );
}
```

But this requires users of the Frame to pass a 1 when it sends the gameScore() to the first frame. But should it have to?

Discuss why this is a bad idea... Pushing implementation detail out of the class into the space where objects communicate. Make analogy to working with people...

What is the alternative? Maintain gameScore() as an interface method:

```    public int gameScore()
{
return gameScore( 1 );
}

private int gameScore( int frameNumber )
{
if (( nextFrame == null ) || ( frameNumber == 10 ))
return score();
return score() + nextFrame.gameScore( frameNumber + 1 );
}
```

Run the tests. They pass! Run the OpenFrameTests, too. They pass. Success.

Discuss why this is a better idea... Hiding implementation detail from the objects with which Frames communicate. Step through the "recursive" nature of the algorithm. Distinguish from ordinary recursion in which a method within an object calls itself. Here the recursion is via simpler objects... The idea of Object Recursion.

Note: We cannot avoid this issue by opting for the first design option above, creating Frame objects with their frame numbers. That approach encounters the same problem, only in the form of its constructors!

The Final Frontier: Strikes

We are almost there. Strikes are not a lot different than spares, so the code we wrote to handle spares has prepared us well for strikes. The new issues are:

• having the Frame know that when its first ball is a strike then there is no second ball -- the ball passes on to a next frame
• having the frame ask for a second bonus ball from its next frame

But, as always, we'll start simple and let our tests lead our code to these issues in good time.

What's the simplest test case to consider? We can use our design thinking from having implemented spares:

• one strike (to ensure we get the rolls right and use two bonus balls)
• back-to-back strikes (to ensure we handle the case where the next frame doesn't have a second bonus ball for us)
• a strike in the 10th frame (to ensure we handle this thorny boundary condition)
• all strikes (as an extra assurance)

Implementing Strikes

Walk through the test cases one at a time.

• Add roll-handling logic first, then single frame scoring logic.
• Add next-frame-as-strike logic next.
• Try a perfect game for good measure!

Voilá! Our Frame class seems to do the trick. Be sure to run OpenFrameTest and SpareFrameTest, too. We don't want our new functionality to break prior tests...

Now all we have left is to fold this back into our BowlingGame -- and implement the new requirement that drove us here.

Wrap Up

• Reading -- Study today's code, so that you know it well. Try to understand how and why we went where we went. You might even try changing the implementation -- say, implementing the first design option above -- for some practice.

• Programming -- Homework 3 will be available next session.

Eugene Wallingford ..... wallingf@cs.uni.edu ..... February 1, 2005