Need a project tune-up?

Adventures in C#: Extending the Procedural Bowling Game

Ron Jeffries 11/20/2003

Object aficionados worldwide are concerned that the procedural bowling solution presented last time is just not robust enough. They want to see how we would make it score the frames incrementally. Frankly I haven’t the slightest idea.

Contents:

Frame by Frame Scoring

Our mission, should we decide to accept it, is to modify the procedural version of the bowling game to provide enough information to score each frame of a game of bowling, in “real time” as the game goes on. I’m not even sure just what that would mean. Let’s think about it.

Each frame except the tenth has one little squares in the upper right, where scoring for the frame’s own throws is kept. The first throw is written outside the little square, the second is recorded inside the little square, and the cumulative score goes in the big part of the square. Here is an example game:

For now, I’m going to use a bracket notation to talk about what each cell of the scoresheet looks like, with nested brackets representing the big square, and the space at the top where the little square is. So for the above game, my notation looks like this:

[9 [6 3]] [ 17 [ 7 1 ]] [34 [8 /]]

… and so on. I mean the inner brackets to represent the small numbers, to the left of and in the small square, and the number on the left is the cumulative frame score. I hope that’s “obvious”. The tenth frame is special, because it has three cells up in its top right corner, for each of the three balls that can be rolled as part of that frame. I’ll surely change this notation real soon now, I just needed something to type in.

Reviewing the Code

This version of the game has the rolls all done at the beginning, and does the scoring as a batch:

    public int Score() {
      int rollIndex = 0;
      int total = 0;
      for (int frame = 0; frame < 10; frame++, rollIndex += FrameSize(rollIndex)){
        if ( Strike(rollIndex) ) {
          total += 10 + PinCount(rollIndex+1) + PinCount(rollIndex+2);
        }
        else if ( Spare(rollIndex) ) {
          total += 10 + PinCount(rollIndex+2);
        }
        else {
          total += PinCount(rollIndex) + PinCount(rollIndex+1);
        }
      }
      return total;
    }

    private int FrameSize(int rollIndex) {
      if (Strike(rollIndex)) return 1;
      return 2;
    }

    private bool Strike(int rollIndex) {
      return PinCount(rollIndex) == 10;
    }

    private bool Spare(int rollIndex) {
      return PinCount(rollIndex) + PinCount(rollIndex+1) == 10;
    }

    private int PinCount(int pinPosition) {
      return (int) rolls[pinPosition];
    }

Incremental scoring will be a big problem for this code, it seems. If we were to call it before we have enough balls, the PinCount method will surely throw an exception. (I could write a test for that, I suppose, but it doesn’t seem interesting. When you write your own article, you can write that test if you want to.

How might we proceed? The bracketed things, and the picture up above, make me think that the Score() algorithm , or some relative of it, needs to produce some kind of collection of objects that have the first throw, second throw, and cumulative score information. Let’s try to write a test for that:

    [Test] public void FirstFrameInfo() {
      game.Roll(6);
      game.Roll(3);
      FrameInfo frameInfo = (FrameInfo) game.GetFrameInfo()[0];
      AssertEquals(6, frameInfo.FirstBall);
      AssertEquals(3, frameInfo.SecondBall);
      AssertEquals(9, frameInfo.Score);
    }

Here I’m positing a method GetFrameInfo() that returns some kind of collection of FrameInfo objects, each of which has FirstBall, SecondBall, and Score properties. That seems like quite a lot already, so I’ll stop here.

Let me mention in passing that a number of my dear colleagues have developed the habit of writing tests that contain only one assert. Obviously I’m not doing that, and it’s primarily because I haven’t the slightest idea how it would make things better. I’ve specified here what I want, and for the moment it seems like it might not be too big. Let’s see.

The compiler informs us, as if we didn’t know, that FrameInfo is unknown to it, which has caused a bit of trouble thereafter. We need a new class, and here it is.

using System;

namespace BowlingProcedural {

  public class FrameInfo {

    public FrameInfo() {
    }

    public int FirstBall {
      get { return 6; }
    }

    public int SecondBall {
      get { return 3; }
    }

    public int Score {
      get { return 9; }
    }
  }
}

The compiler also suggests implementing GetFrameInfo(), and we oblige with:

    public ArrayList GetFrameInfo() {
      ArrayList frames = new ArrayList();
      frames.Add(new FrameInfo());
      return frames;
    }

The test works. Our work here is done! Well, not quite. There was something a bit “fake it” about that implementation of the FrameInfo. Let’s see whether we can do a bit better before bedtime. I can think of two ways to go. I might beef up the current GetFrameInfo to use code like Score() to fill in some real FrameInfo objects, but if I do, I’ll have this problem with running off the end of the list. Or, I could set up a whole game, and then generate the list, with the size not an obstacle. I like that idea better. I’ll make it a game of all threes again:

    [Test] public void ThreesFrameInfo() {
      RollMany(20,3);
      ArrayList frames = game.GetFrameInfo();
      int frame = 1;
      foreach (FrameInfo f in frames ) {
        AssertEquals(3, f.FirstBall);
        AssertEquals(3, f.SecondBall);
        AssertEquals(6*frame++, f.Score);
      }
      return frames;
    }

Now let’s make GetFrameInfo() work. We recode it this way:

    public ArrayList GetFrameInfo() {
      int rollIndex = 0;
      ArrayList frames = new ArrayList();
      for (int frame = 0; frame < 10; frame++ ) {
        frames.Add(new FrameInfo(PinCount(rollIndex), PinCount(rollIndex+1)));
      }
      return frames;
    }

This nearly works. The test fails on the second frame because the score is not accumulated. Good spot to take a break for sleeping as it seems to be 2 AM here. What’s up with that??? See you tomorrow!

A New Day Dawns

I’m not terribly interested in working on this right now, but I’ll start and see if I pick up speed. I think I’m going to ignore the total score issue, because I think that when the Score() method and this frame notion converge, it will go away. Instead, I’m going to focus next on the partial score issue. I was thinking I might create a partial list, but the scoring mechanism wants to display all ten frames. So I know there are going to be ten thingies. What about creating some filled in and some null? Let’s first go to an array in the existing code … no, wait, I have a broken test. I’m deferring the full scoring idea for now and going another way. I can comment out that test and write a new one:

    [Test] public void SomeFrames() {
      game.Roll(4);
      game.Roll(3);
      game.Roll(1);
      game.Roll(8);
      FrameInfo[] frames = game.GetFrameInfo();
      AssertEquals(1, frames[1].FirstBall);
      AssertEquals( null, frames[2]);
    }

And make it work with this:

    public FrameInfo[] GetFrameInfo() {
      int rollIndex = 0;
      FrameInfo[] frames = new FrameInfo[10];
      for (int frame = 0; frame < 10; frame++ ) {
        if ( rollIndex < rolls.Count) {
          frames[frame] = new FrameInfo(PinCount(rollIndex), PinCount(rollIndex+1));
          rollIndex += 2;
        }
        else
          frames[frame] = null;
      }
      return frames;
    }

This has lots of assumptions in it but it’s getting at the idea: an array of FrameInfo where we have a score, and null where we do not. That feels like progress, though I wish I had a pair here with a clue. Anyway, the tests are running, with that socoring one commented out. Is it time to put it back? What do we need next?

No, I have what I think is a better idea. I’m going to make the whole FrameInfo array, and I’m going to give all the rolls to each FrameInfo for now. Then I can move scoring into them, and then change things to give them just their own rolls, if I really care about that when I get there. First step, change how FrameInfo is created and how it works:

    public FrameInfo[] GetFrameInfo() {
      int rollIndex = 0;
      FrameInfo[] frames = new FrameInfo[10];
      for (int frame = 0; frame < 10; frame++ ) {
        frames[frame] = new FrameInfo(rolls, rollIndex);
        rollIndex += 2;
      }
      return frames;
    }

Then make that work:

  public class FrameInfo {
    ArrayList rolls;
    int rollIndex;

    public FrameInfo() {
    }

    public FrameInfo(ArrayList rolls, int rollIndex) {
      this.rolls = rolls;
      this.rollIndex = rollIndex;
    }

    public int FirstBall {
      get { return Pins(rollIndex); }
    }

    public int SecondBall {
      get { return Pins(rollIndex+1); }
    }

    public int Score {
      get { return FirstBall + SecondBall; }
    }

    private int Pins(int index) {
      return (int) rolls[index];
    }
  }

This runs the tests except that we don’t have the nulls any more. We’ll change it to assert that FirstBall is null, signifying that the FrameInfo knows that it isn’t alive yet:

    [Test] public void SomeFrames() {
      game.Roll(4);
      game.Roll(3);
      game.Roll(1);
      game.Roll(8);
      FrameInfo[] frames = game.GetFrameInfo();
      AssertEquals(1, frames[1].FirstBall);
      AssertEquals( null, frames[2].FirstBall);
    }

This doesn’t pass yet because FirstBall accesses beyond the end of rolls, so we have to fix that. But arrgh! int is only sort of an object, so it can’t be null. Why don’t these people figure out that an object should be an object? We’ll make the FrameInfo just answer whether it has data:

    [Test] public void SomeFrames() {
      game.Roll(4);
      game.Roll(3);
      game.Roll(1);
      game.Roll(8);
      FrameInfo[] frames = game.GetFrameInfo();
      AssertEquals(1, frames[1].FirstBall);
      Assert( ! frames[2].hasData);
    }

And we’ll build in FrameInfo:

    public bool hasData {
      get { return rollIndex < rolls.Count; }
    }

The tests now work. Time for a break, but first some reflection:

We have sketched in a new object, FrameInfo, that is in a position to reflect individual frame scoring. It seems clear that we can give it the same kind of logic as is now in the Score() method, and that it can be incremental, knowing whether or not it knows its answer. (Well, clear enough that I’m not afraid. I don’t see quite how to do it neatly, but time will tell.)

With that object working, the frames might be moved higher in the object, and we can either continue to give them all the rolls (which I am inclined to do), or we can give them all just their own rolls (which I am not inclined to do). The FrameInfo guys can certainly return whatever the display routines may need. So far so good. But on the dark side …

We see the duplication in the Pins method, and we foresee lots of duplication between FrameInfo and Score(). But that’s OK, because we think we’ll remove Score’s functionality and dispatch it to the frames. There’s duplication now, and there will be more, but I’m OK with that because I’m in the middle of doing this new feature. It’ll be all nice before I release it.

For now, I have to go and work on something else. See you later …