12,898,392 members (62,575 online)
alternative version

#### Stats

38.6K views
13 bookmarked
Posted 8 Aug 2009

# Ten Pin Bowling Calculator

, 8 Aug 2009 CPOL
 Rate this:
Simple ten-pin bowling calculator to calculate the scores of the game

## Introduction

Recently, I was tasked to carry out this assignment, and have not come across many articles on explaining this, let alone how to compute the scores. I have come across two open-source projects (see the references below) that do it but my needs were more simpler than that, hence this article aims to fill the gap and also it is about how to calculate the scores used in Ten Pin Bowling, using an OOP approach that is intuitive and easy to use. The scoring system can be confusing but will be dispelled once you read this. I used the open-source projects to compare the results of the scores.

## Background

Ten Pin Bowling is a competitive game with a simple objective - to knock down as many pins per throw on a narrow alley with the pins situated at the end of the alley. The game composes of frames, at each frame all ten pins are set up standing, the player gets two throws per frame to knock the pins down. There are ten frames per game. A throw is where the player throws a ball down the alley to attempt knocking down pin(s).

In each frame, there are two throws, with the exception of the last frame (will be discussed shortly).

• If all pins are knocked down on the first throw, that is called a Strike.
• If the player fails to knock down ALL pins on the first throw but succeeds on the second throw, that is called a Spare.
• x pins knocked on the first throw, and the remainder of y pins knocked on the second throw, such that, x + y is less than 10.
Forgive the usage of mathematical notations - everything tis clear as mud aye!

The scoring works like this:

• Strike - Add ten plus the number of pins knocked down by the next two balls to the score of the previous frame.
• Spare - Add ten plus the number of pins knocked down by the next ball to the score of the previous frame.
• Normal - Add the total of pins knocked down by the two throws to the previous frame. A gutter ball, is when the ball ends up in the gutter and does not knock any pins, that is worth zero points! :)

There, that's it, sounds tricky...wait...

On the last frame, if a Strike is thrown, the player has two throws to complete the frame. Likewise, if a Spare is thrown, the player has one throw to complete the frame. Hence the last frame will have three throws! Hmmm... how to compute THAT!

Note: Discussion of tactics/techniques are not discussed here. If you're looking to get a perfect score of maximum 300 points, you've come to the wrong place, but feel free to read it, and no, it will not help you anyway! :)

## Using the Code

I will discuss the classes used and then the GUI, but first let's head on into classes here. Here's an overview of the classes in UML diagram (created with Dia).

### UML Classes

Click on the image below to see the full UML in its glory!

A look at the classes and what they mean:

• `BallThrow` - This class is responsible for holding the number of pins knocked, and can tell if that throw is:
• A Gutter ball
• A Strike
• A Spare

There are only two methods (both overloads) in this class called `MarkBall`.

• `Frame` - This class holds an internal generic list of type `BallThrow`, and for whatever frame the state of play is at, it will fire an event `EndOfFrame`, likewise when the game is finished, i.e. all ten frames are completed, it fires an event `AllFramesDone`. The knocking of pins is handled here by invoking `ThrowBall`, which in turn invokes the method overloads `MarkBall` of the `BallThrow` class.
• `FrameHandler` - This is the main class which holds an internal generic list of type `Frame`, this also happens to be a consumer of the events fired from the `Frame` class. Speaking of events, this class fires off three events to the external consumer, in this case of the application, the GUI, consumes it and acts accordingly.
• `Score` - This is how the GUI updates the score for each frame.
• `PerfectGame` - This will fire if the maximum points have reached, i.e. 300 (You'd have to be a good bowler or the 'King of Pins' to achieve this.)
• `EndOfGame` - This will fire when the game ends and all ten frames are completed.

It is also used to handle the state of play and keep track of the scoring by referring to the above two classes. When playing a frame, the UI extracts the appropriate frame from this class (`Frame`) by simply indexing using the actual number that denotes the frame and sets it accordingly by invoking the method overloads for `ThrowBall` depending on whether it was a strike/spare or a number used (how the GUI marks it as a strike/spare will be discussed shortly).

• `FrameScoreEventArgs` - A class inherited from `System.EventArgs `to hold the Frame number (`FrameNumber`) and the score for that frame (`FrameScore`). The events that use this class are found in `Frame`.`EndOfFrame` and in `FrameHandler`.`Score`.

Now that the overview of the classes are done, this should make it easier to understand the code as we'll go through below. All XML comments are stripped for brevity here.

### BallThrow

```public class BallThrow{
....
//
public BallThrow() {}
//
public BallThrow(int iPinsKnocked) {
this._iPinsKnocked = iPinsKnocked;
}
//
....
//
public void MarkBall(int iPinsKnocked){
this._iPinsKnocked = iPinsKnocked;
}
//
public void MarkBall(bool bStrike, bool bSpare) {
this._bThrowIsStrike = bStrike;
this._bThrowIsSpare = bSpare;
}
}```
Figure 1.

This class in Figure 1, above, only has a number of properties which uses privately declared variables to determine the state of this throw.

• `PinsKnocked` - The number of pins knocked down, this uses `_iPinsKnocked`.
• `IsAGutter` - Not really used here in this application but useful nonetheless! If no pins are knocked down, then it checks for comparison with `_iPinsKnocked` to zero.
• `SpareBall` - Was this throw a spare? It uses `_bThrowIsSpare` to determine the status of throw.
• `StrikeBall` - Was this throw a strike? This uses `_bThrowIsStrike` to determine if this throw resulted in a strike.

Astute readers will notice that there are two constructors, the reason is, further on higher up in the class hierarchy, if a throw was a spare or strike, a need to instantiate this class with an empty constructor is required and the second overload of `MarkBall` is invoked to mark this throw as spare or a strike. Otherwise, if this was a normal throw, the number of pins knocked down is passed into the constructor.

• `MarkBall(int iPinsKnocked)` - Sets up the number of pins knocked down by assigning `_iPinsKnocked` to the desired quantity.
• `MarkBall(bool bStrike, bool bSpare)` - Sets the appropriate flag to mark this throw as a strike or a spare.

### Frame

```public class Frame {
....
//
public Frame(int iFrameNo) {
this._iFrameNo = iFrameNo;
this._throwList = new List<BallThrow>();
}
//
public BallThrow this[int index] {
get {
if (this._throwList.Count == 0) return null;
if (!this.IsLastFrame) {
if (index >= BTHelpers.FIRST_THROW_INDEX &&
index <= BTHelpers.SECOND_THROW_INDEX) return this._throwList[index];
else return null; // Out of bounds....
} else {
// Last Frame
if (index >= BTHelpers.FIRST_THROW_INDEX &&
index <= BTHelpers.LAST_THROW_INDEX) return this._throwList[index];
else return null; // Out of bounds....
}
}
}
//
...
...
}```
Figure 2.

I deliberately left out the `MarkBall` overloads as this will be discussed shortly. This class in Figure 2, above, only has a number of properties about the frame:

• `this` - This returns back the appropriate `BallThrow` object, there's a validation to check on the index to the `List<BallThrow>` collection.
• `Number` - This simply tells what frame we're at.
• `IsLastFrame` - Are we on the last frame? (Remember the last frame could have three throws)
• `IsFirstFrame` - Are we on the first frame?
• `IsStrike` - Was this frame a strike?
• `IsSpare` - Was this frame a spare?
• `TotalPinsKnocked` - The total number of pins knocked down in this frame. In the case of either a strike or a spare, this returns 10 either way.
• `IsNormal` - Does this frame have no strike or spare, i.e. the sum of pins knocked down which is less than 10.
• `CountOfThrows` - The number of throws in this frame.
• `FirstBall` - Returns back the number of pins knocked down on the first throw, otherwise it will have 10 for a strike.
• `SecondBall` - Returns back the number of pins knocked down on the second throw. If this frame was a spare, then `FirstBall` + this shall equal to 10.
• `LastBall` - This is for if we're the last frame and to return back the number of pins knocked down.

Note that the property `TotalPinsKnocked` is taken care of in the method overloads `MarkBall` and takes into account the last frame especially!

### Frame.MarkBall(...)

```public void ThrowBall(int iPins) {
if (!this.IsLastFrame) {
if (this._throwList.Count == 0) {
// First throw...
BallThrow throwBall = new BallThrow(iPins);
//
if (iPins == BTHelpers.MAX_PINS)
throwBall.MarkBall(true, false); // A Strike was used!
this._throwList.AddRange(new BallThrow[] { throwBall, new BallThrow() });
if (throwBall.StrikeBall) {
throwBall.MarkBall(BTHelpers.MAX_PINS);
int TotalPinsKnocked = BTHelpers.MAX_SCORE;
this._iFrameScore = TotalPinsKnocked;
this._iTotalPinsKnocked = BTHelpers.MAX_SCORE;
this.OnEndOfFrame(new FrameScoreEventArgs
(TotalPinsKnocked, this._iFrameNo));
return;
}
} else {
// Check for the first throw
BallThrow prevThrow = this._throwList[BTHelpers.FIRST_THROW_INDEX];
if (prevThrow.PinsKnocked + iPins == BTHelpers.MAX_PINS) {
// Spare!
this._throwList[BTHelpers.SECOND_THROW_INDEX].MarkBall(false, true);
}
this._throwList[BTHelpers.SECOND_THROW_INDEX].MarkBall(iPins);
int TotalPinsKnocked = this._throwList
[BTHelpers.FIRST_THROW_INDEX].PinsKnocked +
this._throwList
[BTHelpers.SECOND_THROW_INDEX].PinsKnocked;
//
this._iFrameScore = TotalPinsKnocked;
if (this._throwList.Count == BTHelpers.FRAME_STD_THROWS) {
this._iTotalPinsKnocked = TotalPinsKnocked;
this.OnEndOfFrame(new FrameScoreEventArgs
(TotalPinsKnocked, this._iFrameNo));
}
}
} else {
BallThrow prevThrow = null;
BallThrow throwLast = new BallThrow(iPins);
switch (this._throwList.Count) {
case 0:
// Simple enough case...we're on the first throw of the last frame.
// This could be either n pins knocked or a strike
BallThrow throwFirstBall = new BallThrow(iPins);
if (iPins == BTHelpers.MAX_PINS) throwFirstBall.MarkBall(true, false);
break;
case 1:
// Second throw of the last frame
// Check the previous
prevThrow = this._throwList[BTHelpers.FIRST_THROW_INDEX];
if (prevThrow.StrikeBall) {
// this... CANNOT BE A SPARE - IMPOSSIBLE!
// 1st Throw a Strike, IMPOSSIBLE TO HAVE A SPARE in the second
return;
} else {
// Both ordinary no's...i.e. the first throw was n Pins,
// the second throw was (MAX_PINS - n) pins knocked down.
if (prevThrow.PinsKnocked + iPins < BTHelpers.MAX_PINS) {
int TotalPinsKnocked = this._throwList
[BTHelpers.FIRST_THROW_INDEX].PinsKnocked +
this._throwList
[BTHelpers.SECOND_THROW_INDEX].PinsKnocked;
//
this._iFrameScore = TotalPinsKnocked;
this._iTotalPinsKnocked = TotalPinsKnocked;
// Signal end of frame...no more throws at this point
this.OnEndOfFrame(new FrameScoreEventArgs
(TotalPinsKnocked, this._iFrameNo));
// Signal game complete!
this.OnAllFramesDone();
return;
} else {
// Both ordinary no's...i.e. the first throw was n Pins,
// the second throw was spare!
// So another throw would be used i.e. 3rd throw.
if (prevThrow.PinsKnocked + iPins == BTHelpers.MAX_PINS) {
// This is a spare
throwLast.MarkBall(false, true);
int TotalPinsKnocked = BTHelpers.MAX_PINS;
this._iFrameScore = TotalPinsKnocked;
}
}
}
break;
case 2:
default:
// We're on the last throw
prevThrow = this._throwList
[BTHelpers.SECOND_THROW_INDEX]; // Check the second throw
if (prevThrow.StrikeBall) {
// this... CANNOT BE A SPARE - IMPOSSIBLE!
int TotalPinsKnocked =
this._throwList[BTHelpers.SECOND_THROW_INDEX].PinsKnocked +
this._throwList[BTHelpers.LAST_THROW_INDEX].PinsKnocked;
//
this._iFrameScore = TotalPinsKnocked;
this._iTotalPinsKnocked = TotalPinsKnocked;
// Signal end of frame...no more throws at this point
this.OnEndOfFrame(new FrameScoreEventArgs(TotalPinsKnocked,
this._iFrameNo));
this.OnAllFramesDone();
return;
}
if (prevThrow.SpareBall) {
// Oh! the 2nd throw was a spare...
if (prevThrow.PinsKnocked +
iPins < BTHelpers.MAX_PINS) { // Both ordinary no's...
int TotalPinsKnocked = BTHelpers.MAX_PINS +
this._throwList[BTHelpers.LAST_THROW_INDEX].PinsKnocked;
this._iFrameScore = TotalPinsKnocked;
this._iTotalPinsKnocked = TotalPinsKnocked;
// Signal end of frame...no more throws at this point
this.OnEndOfFrame(new FrameScoreEventArgs
(TotalPinsKnocked, this._iFrameNo));
} else {
if (prevThrow.PinsKnocked + iPins ==
BTHelpers.MAX_PINS) { // this throw +
// prev = spare.. Will have another shot!...
throwLast.MarkBall(false, true);
int TotalPinsKnocked = BTHelpers.MAX_PINS +
this._throwList[BTHelpers.LAST_THROW_INDEX].PinsKnocked;
//
this._iFrameScore = TotalPinsKnocked;
this._iTotalPinsKnocked = TotalPinsKnocked;
// Signal end of frame...no more throws at this point
this.OnEndOfFrame(new FrameScoreEventArgs
(TotalPinsKnocked, this._iFrameNo));
} else {
// 2nd throw was a spare
// 3rd throw was a normal one with n pins knocked.
throwLast.MarkBall(false, true);
int TotalPinsKnocked = BTHelpers.MAX_PINS +
this._throwList[BTHelpers.LAST_THROW_INDEX].PinsKnocked;
this._iFrameScore = TotalPinsKnocked;
this._iTotalPinsKnocked = TotalPinsKnocked;
// Signal end of frame...no more throws at this point
this.OnEndOfFrame(new FrameScoreEventArgs
(TotalPinsKnocked, this._iFrameNo));
}
}
// Signal game complete!
this.OnAllFramesDone();
} else {
// 2nd Throw was just an ordinary pins knocked
// this last throw just happened
// Normal digits...
int TotalPinsKnocked = this._throwList
[BTHelpers.FIRST_THROW_INDEX].PinsKnocked +
this._throwList
[BTHelpers.SECOND_THROW_INDEX].PinsKnocked +
this._throwList
[BTHelpers.LAST_THROW_INDEX].PinsKnocked;
//
this._iFrameScore = TotalPinsKnocked;
this._iTotalPinsKnocked = TotalPinsKnocked;
// Signal End of Frame
this.OnEndOfFrame(new FrameScoreEventArgs
(TotalPinsKnocked, this._iFrameNo));
// Signal game complete!
this.OnAllFramesDone();
}
// Allow it to fall through!
break;
}
}
}```
Figure 3.

Now, what a mouthful of code...fear not, for I shall lead you down the bowling alley and the light will shine! :) Ok, seriously, the logic is similar to the second overload of the above method, frames 1 to 9 are simple enough, it's the last frame that's where we need to watch out for, because there are a number of possibilities combining strike(s) and spare(s). It should be noted:

• For frames 1-9 inclusive, if a number of pins was thrown and is the first throw, then this is what happens:
1. An empty `BallThrow` class is instantiated - `throwBall`.
2. `throwBall.MarkBall` (2nd overload)</code /> is invoked to set the flags then... (This is optional unless a strike is thrown.)
3. `throwBall.MarkBall` (1st overload)</code /> is invoked to set the pins knocked down to the desired number of pins (This will be 10 if this was a strike, and hence, this frame gets marked as a strike).
4. The newly instantiated class is added to the collection (`List<BallThrow>`) using the `AddRange` method of the generic list collection adding another empty `BallThrow` to the collection, to make this Frame consist of two throws.
5. The event `OnEndOfFrame` gets fired, in which the `FrameHandler` class can then advance on to the next Frame.

Otherwise, if this was a second throw, then this is what happens:

1. A check is made by obtaining the first throw in the `<BallThrow>` collection, `prevThrow` (type of `BallThrow` class), and checking if the sum of pins knocked down on the previous throw equals 10, then this throw gets marked as a spare.
2. The event `OnEndOfFrame` gets fired, in which the `FrameHandler` class can then advance on to the next Frame.
• For the last frame, a check is made to see what throw we are on by checking the `List<BallThrow>`'s property `Count` and using the `switch` statement to determine the throw we're on..we also create a new `BallThrow` object which gets added to the collection in one of the cases below...
1. If it is 0, then we're on the first throw of this last frame, again, we check if a strike was thrown by comparing to 10. Add it to the collection. No events are fired at this point.
2. If it is 1, then we're on the second throw, again, we check the `_prevThrow` which is a `BallThrow` object accessed from the generic collection...i.e. first throw...
• We then determine if the previous throw was a strike.
• If the `_prevThrow`'s pins plus this throw's pins is less than 10, then we add this throw to the generic collection, fire the event `EndOfFrame` and also fire another event `AllFramesDone`.
• If the `_prevThrow`'s pins plus this throw's pins is equal to 10, then we add this throw to the generic collection, mark this throw as a spare. No events are fired at this point since a third throw will be warranted.
3. If it is 2, then we're on the third throw, again, we check the `_prevThrow` which is a `BallThrow` object accessed from the generic collection...i.e. second throw...
• We then determine if the previous throw was a strike, add it to the generic collection and fire two events `EndOfFrame` and `AllFramesDone`.
• If the `_prevThrow` was a spare, then we add to the generic collection and again, fire two events `EndOfFrame` and `AllFramesDone`.
• If the `_prevThrow`'s pins plus this throw's pins is less than 10, then we add this throw to the generic collection, fire the event `EndOfFrame` and also fire another event `AllFramesDone`.
• If the `_prevThrow`'s pins plus this throw's pins is equal to 10, then we add this throw to the generic collection, mark this throw as a spare, fire the event `EndOfFrame` and also fire another event `AllFramesDone`.

### FrameHandler

Now we're almost done, this class is next up for discussion.

```public class FrameHandler : IEnumerable<frame>, IEnumerator<frame> {
....
//
public FrameHandler() {
for (int nLoopCnt = 0; nLoopCnt < BTHelpers.MAX_FRAMES; nLoopCnt++) {
Frame f = new Frame(nLoopCnt + 1);
f.EndOfFrame += new EventHandler<FrameScoreEventArgs>(f_EndOfFrame);
}
Frame fLastFrame = this[BTHelpers.LAST_FRAME_INDEX];
fLastFrame.AllFramesDone +=
new EventHandler<EventArgs>(fLastFrame_AllFramesDone);
}
//
....
//
void fLastFrame_AllFramesDone(object sender, EventArgs e) {
System.Diagnostics.Debug.WriteLine("**** THIS GAME IS COMPLETE ****");
Frame fPrev = this[BTHelpers.LAST_FRAME_INDEX - 1];
Frame fCurr = this[BTHelpers.LAST_FRAME_INDEX];
fCurr.Score = fCurr.TotalPinsKnocked + fPrev.Score;
this.OnScore(new FrameScoreEventArgs(fCurr.Score, fCurr.Number));
this.OnEndOfGame();
if (fCurr.Score == BTHelpers.MAX_FRAMES_SCORE) this.OnPerfectGame();
}
//
void f_EndOfFrame(object sender, FrameScoreEventArgs e) {
this.Reset();
foreach (Frame f in this) {
if (!f.IsLastFrame) this.OnScore
(new FrameScoreEventArgs(f.Score, f.Number));
}
}

// IEnumerable<frame> Members
....
//
....
//
private int CalcScore(int iFrameNo) {
int iScore = 0;
//System.Diagnostics.Debug.WriteLine(string.Format("[DEBUG] -
//CalcScore() - FRAME #{0} **********", iFrameNo));
for (int iCurrentFrame = BTHelpers.FIRST_FRAME_INDEX;
iCurrentFrame < iFrameNo; iCurrentFrame++) {
Frame f = this[iCurrentFrame];
Frame fNext = this[iCurrentFrame + 1];
Frame fNextNext = this[iCurrentFrame + 2];
int iNextTwoBalls = 0;
if (fNext != null){
iNextTwoBalls += fNext.FirstBall;
if (fNext.SecondBall == 0) {
if (fNextNext != null) {
iNextTwoBalls += fNextNext.FirstBall;
}
} else {
iNextTwoBalls += fNext.SecondBall;
}
}
if (f.IsStrike)  iScore += BTHelpers.MAX_SCORE + iNextTwoBalls;
else if (f.IsSpare) iScore += BTHelpers.MAX_SCORE + fNext.FirstBall;
else iScore += f.TotalPinsKnocked;
f.Score = iScore;
//System.Diagnostics.Debug.WriteLine
//(string.Format("[DEBUG] - CalcScore() - Score: {0}", iScore));
}
return iScore;
}
}```
Figure 4.

This class is the meat of it all and handles the consuming of the `Frame`'s events `EndOfFrame` and `AllFramesDone`. When this class gets instantiated, it initializes the generic list collection, `List<Frame>`. Notice how we looped from Frame 1 to 9 inclusive and assign the event handler for `EndOfFrame`.
Then we specifically add a new `Frame` that denotes the tenth frame and assign the event handler to `AllFramesDone`.
It also implements the `IEnumerable<Frame>` and `IEnumerator<Frame>` to make it easy to iterate through the generic collection `List<Frame>`. Notice how I did not allow the `Frame`'s events to get propagated to the outside, instead it gets handled here, and this class simply forwards it on to the outside, `EndOfGame` and `PerfectGame`.

From this so far, due to the way this was designed, perhaps a weakness maybe, that you cannot go back to a frame and stick in a value as it's already set up and would lead to incorrect results as the throws for a frame that you went back onto, was already done.
There is only one property for this class accessible to the outside:

• `this[index]` - This returns back the appropriate frame.

The exposable method to the outside is `ClearAllThrows` which simply iterates through the collection and resets the `Frame`'s collection of `BallThrow`.

• Upon receiving the `Frame`'s event `EndOfFrame`, the class invokes `CalcScore` and iterates through the collection, repeatedly firing off `Score` event to update the score as it goes through each frame.
• After the `Frame`'s event `AllFramesDone` gets trapped, we then add on the sum of Frame 9 to Frame 10, to give the final score for Frame 10 and fire off the event `EndOfGame`. We also check to see if the score has reached maximum points of 300 and fire the event `PerfectGame`.

In referring to the `private `method `CalcScore`, the code to handle the scoring logic is this in pseudocode...

```int score = 0;
for each frame
if frame is strike then score += 10 + next_two_balls();
else if frame is spare then score += 10 + next_ball();
else score += frame's score.

next_two_balls()
return firstball + secondball;
next_ball()
return firstball;```
Figure 5.

The trick lies in the usage of the next_two_balls in the above Figure 5, since when a frame has a strike, it has two `BallThrow`'s in that `Frame`'s generic collection, the first element of the zero-th index would be 10, the next element of the first index would be 0, this would obviously not make sense i.e. 10 + 0 = 10... (I fell for this when I discovered the code didn't calculate properly - duh!), so I used the next frame's first ball! That would explain why I had two variables `fNext` and `fNextNext` and check on them to compute `iNextTwoBalls` as shown in Figure 4 above. The results computed then get assigned to that frame. Ok, everyone got that - aye tis clear as mud, wrong? right!

Now that's the meat of the bowling calculator out of the way, time to move on to the UI. By now, you can imagine, when the UI receives the events from the `FrameHandler`'s class, namely `EndOfGame`, `PerfectGame` and `Score`, you can deduce it notifies the user of that - and updates the UI to reflect that. So far, so good...

## The User Interface

The UI code is split up into two files Form1.cs and frmBowlClass.cs. Let's talk about the controls for frames used which makes up the interface...

• TextBoxes - For frames 1-9 inclusive, there's two textboxes (representing a throw each), for the last frame, there's three, inputs for each of the frame allowed are:
• Numeric - 0..9 only
• Spare - / only
• Strike - X only

It should be noted that the input length is 1 only, any other inputs are discarded (or assigned to /dev/null for that matter) and also the inputs advance on to the next frame automatically.

• Only the first throw can have an 'X' (a strike). The second input box becomes disabled.
• Only the second throw can have an '/' (a spare).
• The above two is for frames 1-9 inclusive!
• Numbers 0-9 on both throws are accepted.
• Labels - This is purely to show the score for each of the frames.

Each of the above are placed into the group box (or child controls if you prefer). The {FrameNo} and {ThrowNo} are substituted with the actual numbers indicating the frame and throw respectively. The names I gave to these controls are:

• Group boxes - grpFrame{FrameNo}, and in each group box, there's two text boxes and a label..
• Text boxes - txtBoxF{FrameNo}Throw{ThrowNo}...
• Labels - lblFrame{FrameNo}.

Right on, let's move on from here...

### Form1.cs

```private const string KEY_TAB = "{TAB}";
//
private const string LABELNAME = "lblFrame{0}";
private const string FIRSTTHROW_ANY_FRAME = "txtBoxF{0}Throw1";
private const string SECONDTHROW_ANY_FRAME = "txtBoxF{0}Throw2";
private const string THIRDTHROW_LAST_FRAME = "txtBoxF10Throw3";
//
private const string SPARE = "/";
private const string STRIKE = "X";
//
private const string REGEXP_FRAMENO = "frameNo";
private const string REGEXP_THROWNO = "throwNo";
//
private readonly string VALID_KEYS_THROW = "/0123456789X";
//
private System.Text.RegularExpressions.Regex _reTextInputName = new Regex(@
"^txtBoxF(?<frameno>\d+)Throw(?<throwno />\d+)\$",
System.Text.RegularExpressions.RegexOptions.Compiled |
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
//
private System.Text.RegularExpressions.Regex _reLblName = new Regex(@
"^lblFrame(?<frameno>\d+)",
System.Text.RegularExpressions.RegexOptions.Compiled |
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
//
private System.Text.RegularExpressions.Regex _reGrpName = new Regex(@"
^grpFrame(?<frameno>\d+)",
System.Text.RegularExpressions.RegexOptions.Compiled |
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
//
private BowlTracker.FrameHandler _frameHandler = null;
//
private System.Collections.Generic.Dictionary<int, /> _hashCtls = new Dictionary<int, />();
//
public frmBowlCalc() {
InitializeComponent();
this.HashGroups();
this._frameHandler = new FrameHandler();
this._frameHandler.Score +=
new EventHandler<framescoreeventargs>(_frameHandler_Score);
this._frameHandler.EndOfGame += new EventHandler<eventargs />(_frameHandler_EndOfGame);
this._frameHandler.PerfectGame += new EventHandler<eventargs />(_frameHandler_PerfectGame);
}```
Figure 6.

This explains why you're seeing the regex's and the constants...Looks crappy but...(I am my own worst enemy). Let's take a look at this constructor, it calls `HashGroups` and initializes a new instance of `FrameHandler`, sets up the event handlers for `Score`, `EndOfGame` and `PerfectGame`. Now, but wtf is `HashGroups`... hmmm... is there some hocus pocus going on here?.... Let's take a dive into it... `HashGroups` is situated in frmBowlClass.cs so look in there.

### HashGroups

```private void HashGroups() {
foreach (Control c in this.Controls) {
if (c is GroupBox) {
GroupBox grp = (GroupBox)c as GroupBox;
if (grp != null) {
Match m = this._reGrpName.Match(grp.Name);
if (m.Success) {
int iFrameNo = int.Parse(m.Groups[REGEXP_FRAMENO].Value);
}
}
}
}
}```
Figure 7.

The above method, shown in the above Figure 7, is a generic collection `Dictionary<int, GroupBox>` which simply stores the `GroupBox `into the dictionary with the key being as the Frame Number. Yup, you've guessed it, each control in each group has a frame number from 1 to 10 inclusive! This makes it easier to pull in the label, find out the contents entered into the appropriate text box and perform some magic on it.

From looking at Figure 7, you can see how the controls on the UI are iterated and checked if the control is a group box, extract the frame number from the group box's `Name` property by performing a regexp matching to extract the desired frame number and add it to the dictionary.
This makes things easier to "get at" the appropriate label to update the score...see Figure 8 below for the two methods used:

```private TextBox LookUpTextBox(int iFrameNo, string sTxtBoxTargetName) {
GroupBox grpBox = this._hashCtls[iFrameNo];
if (grpBox != null) {
foreach (Control t in grpBox.Controls) {
if (t is TextBox) {
TextBox txtBox = (TextBox)t;
if (txtBox.Name.Equals(sTxtBoxTargetName)) return txtBox;
}
}
}
return null;
}

private Label LookUpLabel(int iFrameNo, string sLblBoxTargetName) {
GroupBox grpBox = this._hashCtls[iFrameNo];
if (grpBox != null) {
foreach (Control t in grpBox.Controls) {
if (t is Label) {
Label lblBox = (Label)t;
if (lblBox.Name.Equals(sLblBoxTargetName)) return lblBox;
}
}
}
return null;
}```
Figure 8.

Those two Figures, 7 and 8 respectively are in frmBowlClass.cs...Was that code just being smart and clever?.... Now it is obvious how the score gets shoved into the label on the UI, `LookUpLabel`... likewise the similar method for `LookUpTextBox`...

Let's take a look at how the score "gets at" the appropriate label for a frame, this is in Form1.cs by the way...

```void _frameHandler_Score(object sender, FrameScoreEventArgs e) {
if (e.FrameScore > 0) {
Label lblFrame = this.LookUpLabel
lblFrame.Text = e.FrameScore.ToString();
}
}```
Figure 9.

There... wasn't so difficult, was it... notice how the `LookUpLabel` was used to get the specific label for a specific frame number as shown in Figure 9.

The common code that is shared between all text box inputs to filter out fluff is situated in `txtBoxThrow_KeyPress`, see Figure 10 below. The code to handle each throw is in `txtBoxThrow`(One|Two|Three)`_KeyUp` event handlers. ...let's take a look at the first throw input box...again, as shown below in Figure 10.

```// in Form1.cs!!
private void txtBoxThrowOne_KeyUp(object sender, KeyEventArgs e) {
TextBox txtBox = (TextBox)sender as TextBox;
if (txtBox != null && txtBox.Text.Length > 0) {
if (this.HandleFirstThrow(txtBox)) {
e.Handled = true;
SendKeys.Send(KEY_TAB);
} else e.Handled = false;
}
}

private void txtBoxThrowOne_KeyUp(object sender, KeyEventArgs e) {
TextBox txtBox = (TextBox)sender as TextBox;
if (txtBox != null && txtBox.Text.Length > 0) {
if (this.HandleFirstThrow(txtBox)) {
e.Handled = true;
SendKeys.Send(KEY_TAB);
} else e.Handled = false;
}
}

// in frmBowlClass.cs
private bool HandleFirstThrow(TextBox txtBox) {
string sName = txtBox.Name;
Match m = this._reTextInputName.Match(sName);
if (m.Success) {
int iFrameNo = int.Parse(m.Groups[REGEXP_FRAMENO].Value);
int iThrowNo = int.Parse(m.Groups[REGEXP_THROWNO].Value);
if (iFrameNo < BowlTracker.BTHelpers.MAX_FRAMES) {
if (txtBox.Text.Equals(STRIKE)) {
this.DisableSecondThrow(iFrameNo);
this._frameHandler[iFrameNo].ThrowBall(true, false);
return true;
}
if (txtBox.Text.Equals(SPARE)) { 	// Cannot have a spare on the
// first throw! - illogical!
this.InvalidThrow(txtBox);
return false;
}
} else {
// Last Frame...
if (txtBox.Text.Equals(STRIKE)) {
this._frameHandler[iFrameNo].ThrowBall(true, false);
return true;
}
}
// Anything else here would be numeric...
int iPins = int.Parse(txtBox.Text);
this._frameHandler[iFrameNo].ThrowBall(iPins);
// Go back to previous frame and check if a spare was used
return true;
}
return false;
}```
Figure 10.

Looking at the txtBoxThrowXXX_KeyUp event handler, it is obvious how if the input is deemed valid then it advances on to the next frame using the `Send` method of `SendKeys` class.

In the function `HandleFirstThrow` returns a boolean which will determine if it's ok to advance onwards to the next input.

The parameter `TextBox`'s `Name` property gets passed through regexp to determine which throw and which frame is this textbox under, we now also can reference the appropriate frame by using the indexer to the `FrameHandler` class to manipulate the frame. A check is made here to see which frame are we on.

• We then check if a strike was entered, if it was, the method `DisableSecondThrow` is called which prevents any input in the textbox for the second throw. And the method `ThrowBall` gets called marking the selected frame as a strike.
• Otherwise, we perform a simple check if the Spare was entered, if it was, then a method `InvalidThrow` gets called which simply clears the textbox thus forcing the user to enter a proper input.
• Anything else entered, i.e. numeric input, gets passed into the method `ThrowBall` of the appropriate frame's class.

Let's look at the handler for handling second throws below in Figure 11.

```private bool HandleSecondThrow(TextBox txtBox) {
string sName = txtBox.Name;
Match m = this._reTextInputName.Match(sName);
if (m.Success) {
int iFrameNo = int.Parse(m.Groups[REGEXP_FRAMENO].Value);
int iThrowNo = int.Parse(m.Groups[REGEXP_THROWNO].Value);
if (iFrameNo < BowlTracker.BTHelpers.MAX_FRAMES) {
// We're on the other frame(s)...
Throw2ndSpare:
if (txtBox.Text.Equals(SPARE)) {
this._frameHandler[iFrameNo].ThrowBall(false, true);
return true;
}
if (txtBox.Text.Equals(STRIKE)) {
this.InvalidThrow(txtBox);
return false;
}
TextBox prevThrow = this.LookUpTextBox
(iFrameNo, string.Format(FIRSTTHROW_ANY_FRAME, iFrameNo));
int iFirstThrow = 0;
if (prevThrow != null) iFirstThrow = int.Parse(prevThrow.Text);
int iSecondThrow = int.Parse(txtBox.Text);
if (iFirstThrow + iSecondThrow == BowlTracker.BTHelpers.MAX_PINS) {
// Set it to spare
txtBox.Text = SPARE;
goto Throw2ndSpare;
}
if (iFirstThrow + iSecondThrow < BowlTracker.BTHelpers.MAX_PINS) {
this._frameHandler[iFrameNo].ThrowBall(iSecondThrow);
return true;
}
// Any thing else here is an error!
this.InvalidThrow(txtBox);
return false;
} else {
// We're on the last Frame...
if (txtBox.Text.Equals(SPARE) || txtBox.Text.Equals(STRIKE)) {
if (txtBox.Text.Equals(STRIKE))
this._frameHandler[iFrameNo].ThrowBall(true, false);
if (txtBox.Text.Equals(SPARE))
this._frameHandler[iFrameNo].ThrowBall(false, true);
return true;
} else {
TextBox txtBoxPrevThrow = this.LookUpTextBox
(BowlTracker.BTHelpers.MAX_FRAMES,
string.Format(FIRSTTHROW_ANY_FRAME,
BowlTracker.BTHelpers.MAX_FRAMES));
//
if (!txtBoxPrevThrow.Text.Equals(STRIKE)) {
// Disable the third
TextBox txtBoxLastFrameLastThrow = this.LookUpTextBox
(BowlTracker.BTHelpers.MAX_FRAMES, THIRDTHROW_LAST_FRAME);
if (txtBoxLastFrameLastThrow != null)
txtBoxLastFrameLastThrow.Enabled = false;
}
}
int iSecondThrow = int.Parse(txtBox.Text);
this._frameHandler[iFrameNo].ThrowBall(iSecondThrow);
return true;
}
}
return false;
}```
Figure 11.

Again, this function in Figure 11, above, has a similar signature as shown in Figure 10....

In the function `HandleSecondThrow` returns a boolean which will determine if it's ok to advance onwards to the next input.
The parameter `TextBox`'s `Name` property gets passed through regexp to determine which throw and which frame is this textbox under, we now also can reference the appropriate frame by using the indexer to the `FrameHandler` class to manipulate the frame. A check is made here to see which frame we are on.

• Frames 1-9 inclusive:
• If a spare was used, then it marks the frame's appropriate throw - ...Gasp! Oh dear! Did I just see a label `Throw2ndSpare`....OMG!...read on...relax...
• Since this is the second throw, it is impossible to have a strike here and hence calls `InvalidThrow`.
• We check the previous throw and determine if the pins knocked on this throw plus the previous throw is equal to ten then we... GASP! ARGC! ARGV! Did I just see the unspeakable code there `goto`...??? well, there we are, we need to jump back up to the label `Throw2ndSpare` to replace the text box with a / to indicate a spare. Hence the usage of the unspeakably sloppy `goto`! :) Otherwise we just, merely set the number of pins for this throw.
• Last Frame - We check if a spare or a strike was entered and set the frame accordingly, otherwise, we check the previous throw, and if that is not a strike, then this frame will have two throws so we disable the third input box.

I can promise you that the last part of this before we wrap up will be shorter...

```private bool HandleLastThrow(TextBox txtBox) {
string sName = txtBox.Name;
Match m = this._reTextInputName.Match(sName);
if (m.Success) {
int iFrameNo = int.Parse(m.Groups[REGEXP_FRAMENO].Value);
int iThrowNo = int.Parse(m.Groups[REGEXP_THROWNO].Value);
if (iThrowNo == 3) {
if (txtBox.Text.Equals(STRIKE)) {
this._frameHandler[iFrameNo].ThrowBall(true, false);
return true;
}
if (txtBox.Text.Equals(SPARE)) {
this._frameHandler[iFrameNo].ThrowBall(false, true);
return true;
}
// Anything else here is number by default
int iVal = int.Parse(txtBox.Text);
this._frameHandler[iFrameNo].ThrowBall(iVal);
return true;
}
}
return false;
}```
Figure 12.

`HandleLastThrow` is far more simpler and again, similar signature to the previous two highlighted above. And the checking is simpler, since if we're executing in this function, then it must accept either a number or a spare or a strike and return back `true` to the event handler `txtBoxThrowThree_KeyUp`.

That wasn't so bad, was it...Now I hope you have a better understanding of the game of Ten pin Bowling and how the scoring works. The real scoring machines that you see in a real bowling alley are more complex than this. As, it would have, sensors and electronical-what-nots all over the place to be able to calculate as the game-play advances along and can tell instantly the score. Now, you know why you cannot go backwards i.e. playing on frame 6, then go back to a frame previously and shove in something into the input, but realistically speaking, that should be impossible since game play has advanced...but then again, this code can be easily modified to cater for that as I'm sure that there are sophisticated ways of achieving that in a real bowling alley to "resolve" scoring "conflicts"...

## Points of Interest

Heh! Taking cue from this template, I never understood the scoring in bowling. And also, when I realized the scoring wasn't coming out correctly - I spent a day trying to figure out where or what or how it happened, as I pointed out above earlier on when talking about the pseudo-code in Figure 5 above.

All in all, it was good fun - Simple as that! :) Now, where did I leave my bowling ball & shoes...

Since I am very keen on Mono, I thought I'd include the screenshot of the calculator for your pleasure!

Happy bowling and may you get the "Perfect game" next time! ;)

Sources used...

## History

• 7th August, 2009 - First release

## Share

 Software Developer (Senior) Ireland
B.Sc. in Information Systems.
Languages: C, Assembly 80x86, VB6, Databases, .NET, Linux, Win32 API.

Short Note:
Having worked with IT systems spanning over 14 years, he still can remember writing a TSR to trap the three-finger salute in the old days of DOS with Turbo C. Having worked or hacked with AS/400 system while in his college days, graduating to working with Cobol on IBM MVS/360, to RS/6000 AIX/C. He can remember obtaining OS/2 version 2 and installing it on an antique 80386. Boy it ran but crawled! Keen to dabble in new technologies. A self-taught programmer, he is keen to relinquish and acquire new bits and bytes, but craves for the dinosaur days when programmers were ultimately in control over the humble DOS and hacking it!!

## You may also be interested in...

 First Prev Next
 this bowling system can add database??? Member 1067557126-Apr-14 13:29 Member 10675571 26-Apr-14 13:29
 a little bug or feature gharutyu11-Oct-12 22:48 gharutyu 11-Oct-12 22:48
 not bad... but.... Blubbo10-Aug-09 9:33 Blubbo 10-Aug-09 9:33
 Comments [modified] Tomas Brennan8-Aug-09 13:24 Tomas Brennan 8-Aug-09 13:24
 Last Visit: 31-Dec-99 18:00     Last Update: 30-Apr-17 16:53 Refresh 1