You are on page 1of 49

ADAPTED FOR C# 4.

0
(With Visual Studio 2010)
By Dan Stewart
dan@stewshack.com
SCORING BOWLING.

The game consists of 10 frames as shown above. In each frame the


player has two opportunities to knock down 10 pins. The score for the
frame is the total number of pins knocked down, plus bonuses for strikes
and spares.

A spare is when the player knocks down all 10 pins in two tries. The
bonus for that frame is the number of pins knocked down by the next roll.
So in frame 3 above, the score is 10 (the total number knocked down)
plus a bonus of 5 (the number of pins knocked down on the next roll.)

A strike is when the player knocks down all 10 pins on his first try. The
bonus for that frame is the value of the next two balls rolled.

In the tenth frame a player who rolls a spare or strike is allowed to roll
the extra balls to complete the frame. However no more than three balls
can be rolled in tenth frame.
A QUICK DESIGN SESSION
Game
+ roll(pins : int) Clearly we need the Game class.
+ score() : int
A QUICK DESIGN SESSION
Game 10 Frame
+ roll(pins : int)
+ score() : int

A game has 10 frames.


A QUICK DESIGN SESSION
Game 10 Frame 1 ..2 Roll
+ roll(pins : int) - pins : int
+ score() : int

A frame has 1 or two rolls.


A QUICK DESIGN SESSION
Game 10 Frame 1 ..2 Roll

+ roll(pins : int) - pins : int


+ score() : int

Tenth Frame

The tenth frame has two or three rolls.


It is different from all the other frames.
A QUICK DESIGN SESSION
Game 10 Frame 1 ..2 Roll

+ roll(pins : int) + score : int - pins : int


+ score() : int

1
The score function must
include all the frames,
and calculate all their scores. Tenth Frame
A QUICK DESIGN SESSION
The score for a spare or a strike
depends on the frame’s successor
Next frame

Game 10 Frame 1 ..2 Roll

+ roll(pins : int) + score : int - pins : int


+ score() : int

Tenth Frame
BEGIN.
 Create a console application named BowlingGame
 Add a MSTest project named BowlingGameTest to
the solution
BEGIN.
 Add a unit test named GameTest to the project
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace BowlingGameTest
{
[TestClass]
public class GameTest
{
private TestContext testContextInstance;
public TestContext TestContext
{
get { return testContextInstance; }
set { testContextInstance = value; }
}

[TestMethod]
public void TestMethod1()
{
}
}
}
THE FIRST TEST.
[TestMethod]
public void TestGutterGame()
{
Game g = new Game();
}
THE FIRST TEST.
[TestMethod] namespace BowlingGame
public void TestGutterGame() {
{ public class Game
Game g = new Game(); {
} }
}
THE FIRST TEST.
[TestMethod] public class Game
public void TestGutterGame() {
{ }
Game g = new Game();
}
THE FIRST TEST.
[TestMethod] public class Game
public void TestGutterGame() {
{ }
Game g = new Game();

for (int i = 0; i < 20; i++)


{
g.Roll(0);
}
}
THE FIRST TEST.
[TestMethod] public class Game
public void TestGutterGame() {
{ public void Roll(int p)
Game g = new Game(); {
throw new System.NotImplementedException();
}
for (int i = 0; i < 20; i++) }
{
g.Roll(0);
}
}
THE FIRST TEST.
[TestMethod] public class Game
public void TestGutterGame() {
{ public void Roll(int p)
Game g = new Game(); {
throw new System.NotImplementedException();
}
for (int i = 0; i < 20; i++) }
{
g.Roll(0);
}

Assert.AreEqual(0, g.Score());
}
THE FIRST TEST.
[TestMethod] public class Game
public void TestGutterGame() {
{ public void Roll(int p)
Game g = new Game(); {
throw new System.NotImplementedException();
}
for (int i = 0; i < 20; i++)
{ public object Score()
g.Roll(0); {
} throw new System.NotImplementedException();
}
Assert.AreEqual(0, g.Score()); }
}

Failed TestGutterGame threw exception


THE FIRST TEST.
[TestMethod] public class Game
public void TestGutterGame() {
{ private int score;
Game g = new Game();
public void Roll(int pins)
for (int i = 0; i < 20; i++) {
{ }
g.Roll(0);
} public int Score()
{
Assert.AreEqual(0, g.Score); return score;
} }
}
THE SECOND TEST.
[TestMethod] public class Game
public void TestAllOnes() {
{ private int score;
Game g = new Game();
public void Roll(int pins)
for (int i = 0; i < 20; i++) {
{ }
g.Roll(1);
} public int Score()
{
Assert.AreEqual(20, g.Score()); return score;
} }
}
- Game creation is
duplicated
- roll loop is duplicated
THE SECOND TEST.
[TestMethod] public class Game
public void TestAllOnes() {
{ private int score;
Game g = new Game();
public void Roll(int pins)
for (int i = 0; i < 20; i++) {
{ }
g.Roll(1);
} public int Score()
{
Assert.AreEqual(20, g.Score()); return score;
} }
}
- Game creation is
duplicated
- roll loop is duplicated
THE SECOND TEST.
[TestMethod] public class Game
public void TestAllOnes() {
{ private int score;
Game g = new Game();
public void Roll(int pins)
for (int i = 0; i < 20; i++) {
{ }
g.Roll(1);
} public int Score()
{
Assert.AreEqual(20, g.Score()); return score;
} }
}

Assert.AreEqual failed. Expected:<20>. Actual:<0>.


- roll loop is duplicated
THE SECOND TEST.
private Game g; public class Game
[TestInitialize] {
public void Initialize() private int score;
{
g = new Game();
} public void Roll(int pins)
{
[TestCleanup] score += pins;
public void Cleanup()
{ }
g = null;
} public int Score()
[TestMethod] {
public void TestGutterGame() return score;
{ }
for (int i = 0; i < 20; i++)
{ }
g.Roll(0);
}

Assert.AreEqual(0, g.Score());
}

[TestMethod]
public void TestAllOnes()
{
for (int i = 0; i < 20; i++)
{
g.Roll(1);
}

Assert.AreEqual(20, g.Score());
}
- roll loop is duplicated
THE SECOND TEST.
[TestMethod] public class Game
public void TestGutterGame() {
{ private int score;
int rolls = 20;
int pins = 0; public void Roll(int pins)
{
for (int i = 0; i < rolls; i++) score += pins;
{ }
g.Roll(pins);
} public int Score()
{
Assert.AreEqual(0, g.Score()); return score;
} }
}
- roll loop is duplicated
THE SECOND TEST.
[TestMethod] public class Game
public void TestGutterGame() {
{ private int score;
int rolls = 20;
int pins = 0; public void Roll(int pins)
{
RollMany(rolls, pins); score += pins;
}
for (int i = 0; i < rolls; i++)
{ public int Score()
g.Roll(pins); {
} return score;
}
Assert.AreEqual(0, g.Score()); }
}
THE SECOND TEST.
public class Game
[TestMethod]
{
public void TestGutterGame()
private int score;
{
RollMany(20, 0);
public void Roll(int pins)
{
Assert.AreEqual(0, g.Score());
score += pins;
}
}
private void RollMany(int rolls, int pins)
public int Score()
{
{
for (int i = 0; i < rolls; i++)
return score;
{
}
g.Roll(pins);
}
}
}
THE SECOND TEST.
[TestMethod] public class Game
public void TestGutterGame() {
{ private int score;
RollMany(20, 0);
public void Roll(int pins)
Assert.AreEqual(0, g.Score()); {
} score += pins;
}
[TestMethod]
public void TestAllOnes() public int Score()
{ {
RollMany(20, 1); return score;
}
Assert.AreEqual(20, g.Score()); }
}

private void RollMany(int rolls, int pins)


{
for (int i = 0; i < rolls; i++)
{
g.Roll(pins);
}
}
- ugly comment in test.
THE THIRD TEST.
[TestMethod] public class Game
public void TestOneSpare() {
{ private int score;
g.Roll(5);
g.Roll(5); // spare public void Roll(int pins)
g.Roll(3); {
RollMany(17, 0); score += pins;
}
Assert.AreEqual(16, g.Score());
} public int Score()
{
return score;
}
}

Failed Assert.AreEqual Expected:<16>. Actual:<13>


- ugly comment in test.
THE THIRD TEST.
[TestMethod] public class Game
public void TestOneSpare() {
{ private int score;
g.Roll(5);
g.Roll(5); // spare public void Roll(int pins)
g.Roll(3); {
RollMany(17, 0); score += pins; tempted to use
} flag to remember
Assert.AreEqual(16, g.Score()); previous roll. So
} public int Score() design must be
{ wrong.
return score;
}
}

Failed Assert.AreEqual Expected:<16>. Actual:<13>


- ugly comment in test.
THE THIRD TEST. Roll() calculates
score, but name
[TestMethod] public class Game does not imply
public void TestOneSpare() { that.
{ private int score;
g.Roll(5);
g.Roll(5); // spare public void Roll(int pins)
g.Roll(3); {
RollMany(17, 0); score += pins;
}
Assert.AreEqual(16, g.Score);
} public int Score()
{
return score; Score() does not
} calculate score,
} but name implies
that it does.

Design is wrong.
Responsibilities are
misplaced.

Failed Assert.AreEqual Expected:<16>. Actual:<13>


- ugly comment in test.
THE THIRD TEST.
[TestMethod] public class Game
public void TestOneSpare() {
{ private int score;
g.Roll(5);
g.Roll(5); // spare public void Roll(int pins)
g.Roll(3); {
RollMany(17, 0); score += pins;
//Assert.AreEqual(16, g.Score()); }
Assert.Inconclusive();
} public int Score()
{
return score;
}
}
- ugly comment in test.
THE THIRD TEST.
[TestMethod] public class Game
public void TestOneSpare() {
{ private int score;
g.Roll(5); private int[] rolls = new int[21];
g.Roll(5); // spare private int currentRoll;
g.Roll(3);
RollMany(17, 0); public void Roll(int pins)
//Assert.AreEqual(16, g.Score()); {
Assert.Inconclusive(); rolls[currentRoll++] = pins;
} score += pins;
}

public int Score()


{
return score;
}
}
- ugly comment in test.
THE THIRD TEST.
[TestMethod] public class Game
public void TestOneSpare() {
{ private int[] rolls = new int[21];
g.Roll(5); private int currentRoll;
g.Roll(5); // spare
g.Roll(3); public void Roll(int pins)
RollMany(17, 0); {
//Assert.AreEqual(16, g.Score()); rolls[currentRoll++] = pins;
Assert.Inconclusive(); }
}
public int Score()
{
int score = 0;

for (int i = 0; i < rolls.Length; i++)


{
score += rolls[i];
}

return score;
}
}
- ugly comment in test.
THE THIRD TEST.
[TestMethod] public class Game
public void TestOneSpare() {
{ private int[] rolls = new int[21];
g.Roll(5); private int currentRoll;
g.Roll(5); // spare
g.Roll(3); public void Roll(int pins)
RollMany(17, 0); {
rolls[currentRoll++] = pins;
Assert.AreEqual(16, g.Score()); }
}
public int Score()
{
int score = 0;

for (int i = 0; i < rolls.Length; i++)


{
score += rolls[i];
}

return score;
}
}

Failed Assert.AreEqual Expected:<16>. Actual:<13>


- ugly comment in test.
THE THIRD TEST.
[TestMethod] public int Score()
public void TestOneSpare() {
{ int score = 0;
g.Roll(5);
g.Roll(5); // spare for (int i = 0; i < rolls.Length; i++)
g.Roll(3); {
RollMany(17, 0); // spare
if (rolls[i] + rolls[i+1] == 10)
Assert.AreEqual(16, g.Score()); {
} score += ...
}

score += rolls[i];
}

return score;
}

This isn’t going to work because i


might not refer to the first ball of
the frame.

Design is still wrong.

Need to walk through array two


balls (one frame) at a time.
Failed Assert.AreEqual Expected:<16>. Actual:<13>
- ugly comment in test.
THE THIRD TEST.
[TestMethod] public class Game
public void TestOneSpare() {
{ private int[] rolls = new int[21];
g.Roll(5); private int currentRoll;
g.Roll(5); // spare
g.Roll(3); public void Roll(int pins)
RollMany(17, 0); {
rolls[currentRoll++] = pins;
//Assert.AreEqual(16, g.Score()); }
Assert.Inconclusive();
} public int Score()
{
int score = 0;

for (int i = 0; i < rolls.Length; i++)


{
score += rolls[i];
}

return score;
}
}
- ugly comment in test.
THE THIRD TEST.
[TestMethod] public int Score()
public void TestOneSpare() {
{ int score = 0;
g.Roll(5); int roll = 0;
g.Roll(5); // spare
for (int frame = 0; frame < 10; frame++)
g.Roll(3);
{
RollMany(17, 0); score += rolls[roll] + rolls[roll + 1];
//Assert.AreEqual(16, g.Score()); roll += 2;
Assert.Inconclusive(); }
}
return score;
}
- ugly comment in test.
THE THIRD TEST.
[TestMethod] public int Score()
public void TestOneSpare() {
{ int score = 0;
g.Roll(5); int roll = 0;
g.Roll(5); // spare
for (int frame = 0; frame < 10; frame++)
g.Roll(3);
{
RollMany(17, 0); score += rolls[roll] + rolls[roll + 1];
roll += 2;
Assert.AreEqual(16, g.Score()); }
}
return score;
}

Failed Assert.AreEqual Expected:<16>. Actual:<13>


- ugly comment in test.
THE THIRD TEST.
[TestMethod] public int Score()
public void TestOneSpare() {
{ int score = 0;
int roll = 0;
g.Roll(5);
g.Roll(5); // spare for (int frame = 0; frame < 10; frame++)
g.Roll(3); {
RollMany(17, 0); // spare
if (rolls[roll] + rolls[roll + 1] == 10)
Assert.AreEqual(16, g.Score()); {
} score += 10 + rolls[roll + 2];
}
else
{
score += rolls[roll] + rolls[roll + 1];
}

roll += 2;
}

return score;
}
-ugly comment in test.
-ugly comment in conditional.
THE THIRD TEST.
[TestMethod] public int Score()
public void TestOneSpare() {
{ int score = 0;
int roll = 0;
g.Roll(5);
g.Roll(5); // spare for (int frame = 0; frame < 10; frame++)
g.Roll(3); {
RollMany(17, 0); // spare
if (rolls[roll] + rolls[roll + 1] == 10)
Assert.AreEqual(16, g.Score()); {
} score += 10 + rolls[roll + 2];
}
else
{
score += rolls[roll] + rolls[roll + 1];
}

roll += 2;
}

return score;
}
- ugly comment in test.
THE THIRD TEST.
[TestMethod] public int Score()
public void TestOneSpare() {
{ int score = 0;
int roll = 0;
g.Roll(5);
g.Roll(5); // spare for (int frame = 0; frame < 10; frame++)
g.Roll(3); {
RollMany(17, 0); if (IsSpare(roll))
{
Assert.AreEqual(16, g.Score()); score += 10 + rolls[roll + 2];
} }
else
{
score += rolls[roll] + rolls[roll + 1];
}

roll += 2;
}

return score;
}

private bool IsSpare(int roll)


{
return rolls[roll] +
rolls[roll + 1] == 10;
}
THE THIRD TEST.
[TestMethod] public int Score()
public void TestOneSpare() {
{ int score = 0;
int roll = 0;
RollSpare();
g.Roll(3); for (int frame = 0; frame < 10; frame++)
RollMany(17, 0); {
if (IsSpare(roll))
Assert.AreEqual(16, g.Score()); {
} score += 10 + rolls[roll + 2];
}
else
private void RollSpare() {
{ score += rolls[roll] + rolls[roll + 1];
g.Roll(5); }
g.Roll(5);
} roll += 2;
}

return score;
}

private bool IsSpare(int roll)


{
return rolls[roll] +
rolls[roll + 1] == 10;
}
- ugly comment in
testOneStrike. THE FOURTH TEST.
[TestMethod] public int Score()
public void TestOneStrike() {
{ int score = 0;
int roll = 0;
g.Roll(10); // strike
g.Roll(3); for (int frame = 0; frame < 10; frame++)
g.Roll(4); {
RollMany(16, 0); if (IsSpare(roll))
Assert.AreEqual(24, g.Score()); {
} score += 10 + rolls[roll + 2];
}
else
{
score += rolls[roll] + rolls[roll + 1];
}

roll += 2;
}

return score;
}

private bool IsSpare(int roll)


{
return rolls[roll] +
rolls[roll + 1] == 10;
}

Failed Assert.AreEqual Expected:<24>. Actual:<17>


-ugly comment in
testOneStrike.
-ugly comment in conditional.
-ugly expressions.
THE FOURTH TEST.
[TestMethod] public int Score()
public void TestOneStrike() {
int score = 0;
{ int roll = 0;
g.Roll(10); // strike
g.Roll(3); for (int frame = 0; frame < 10; frame++)
g.Roll(4); {
RollMany(16, 0); if (rolls[roll] == 10) // strike
{
Assert.AreEqual(24, g.Score()); score += 10 + rolls[roll + 1]
} + rolls[roll + 2];
roll++;
}
else if (IsSpare(roll))
{
score += 10 + rolls[roll + 2];
roll += 2;
}
else
{
score += rolls[roll] + rolls[roll + 1];
roll += 2;
}
}

return score;
}
-ugly comment in
testOneStrike.
-ugly comment in conditional.
THE FOURTH TEST.
[TestMethod] public int Score()
{
public void TestOneStrike() int score = 0;
{ int roll = 0;
g.Roll(10); // strike
for (int frame = 0; frame < 10; frame++)
g.Roll(3); {
g.Roll(4); if (rolls[roll] == 10) // strike
RollMany(16, 0); {
score += 10 + StrikeBonus(roll);
Assert.AreEqual(24, g.Score()); roll++;
} }
else if (IsSpare(roll))
{
score += 10 + SpareBonus(roll);
roll += 2;
}
else
{
score += SumOfBallsInFrame(roll);
roll += 2;
}
}

return score;
}

private int SumOfBallsInFrame(int roll)


{
return rolls[roll] + rolls[roll + 1];
}

private int SpareBonus(int roll)


{
return rolls[roll + 2];
}

private int StrikeBonus(int roll)


{
return rolls[roll + 1] + rolls[roll + 2];
}
- ugly comment in
testOneStrike. THE FOURTH TEST.
[TestMethod] public int Score()
public void TestOneStrike() {
int score = 0;
{ int roll = 0;
g.Roll(10); // strike
g.Roll(3); for (int frame = 0; frame < 10; frame++)
g.Roll(4); {
RollMany(16, 0); if (IsStrike(roll))
{
Assert.AreEqual(24, g.Score()); score += 10
} + StrikeBonus(roll);

roll++;
}
else if (IsSpare(roll))
{
score += 10
+ SpareBonus(roll);
roll += 2;

}
else
{
score += SumOfBallsInFrame(roll);
roll += 2;
}
}

return score;
}

private bool IsStrike(int roll)


{
return rolls[roll] == 10;
}
THE FOURTH TEST.
[TestMethod] public int Score()
public void TestOneStrike() {
{ int score = 0;
RollStrike(); int roll = 0;
g.Roll(3);
for (int frame = 0; frame < 10; frame++)
g.Roll(4);
{
RollMany(16, 0); if (IsStrike(roll))
{
Assert.AreEqual(24, g.Score()); score += 10
} + StrikeBonus(roll);

private void RollStrike() roll++;


{ }
g.Roll(10); else if (IsSpare(roll))
} {
score += 10
+ SpareBonus(roll);
roll += 2;

}
else
{
score += SumOfBallsInFrame(roll);
roll += 2;
}
}

return score;
}
THE FIFTH TEST.
[TestMethod] public int Score()
public void TestPerfectGame() {
{ int score = 0;
RollMany(12, 10); int roll = 0;

for (int frame = 0; frame < 10; frame++)


Assert.AreEqual(300, g.Score());
{
} if (IsStrike(roll))
{
score += 10
+ StrikeBonus(roll);

roll++;
}
else if (IsSpare(roll))
{
score += 10
+ SpareBonus(roll);
roll += 2;

}
else
{
score += SumOfBallsInFrame(roll);
roll += 2;
}
}

return score;
}
QUESTIONS?

You might also like