Bowling Game Kata in C# Adapted

49
Bowling Game Kata Adapted for C# and nUnit By Dan Stewart (with modifications by Mike Clement) [email protected] [email protected]

description

 

Transcript of Bowling Game Kata in C# Adapted

Page 1: Bowling Game Kata in C# Adapted

Bowling Game KataAdapted for C# and nUnitBy Dan Stewart (with modifications by Mike

Clement)[email protected]

[email protected]

Page 2: Bowling Game Kata in C# Adapted

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.

Page 3: Bowling Game Kata in C# Adapted

A quick design session

Clearly we need the Game class.

Game

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

Page 4: Bowling Game Kata in C# Adapted

A quick design session

A game has 10 frames.

Game 10 Frame

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

Page 5: Bowling Game Kata in C# Adapted

A quick design session

A frame has 1 or two rolls.

Game 10 Frame 1 ..2 Roll

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

- pins : int

Page 6: Bowling Game Kata in C# Adapted

A quick design session

The tenth frame has two or three rolls.It is different from all the other frames.

Game 10 Frame 1 ..2 Roll

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

- pins : int

1

Tenth Frame

Page 7: Bowling Game Kata in C# Adapted

Game 10 Frame 1 ..2 Roll

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

+ score : int - pins : int

1

Tenth Frame

A quick design session

The score function mustinclude all the frames, and calculate all their scores.

Page 8: Bowling Game Kata in C# Adapted

A quick design sessionThe 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

+ score : int - pins : int

1

Tenth Frame

Page 9: Bowling Game Kata in C# Adapted

Begin.

• Create a class library named BowlingGameKata

Page 10: Bowling Game Kata in C# Adapted

Begin.

• Add a test fixture named BowlingGameTests to the projectusing NUnit.Framework;

namespace BowlingGameKata{ [TestFixture] public class BowlingGameTests { [Test] public void GutterGameTest() { } }}

Page 11: Bowling Game Kata in C# Adapted

The first test. [Test] public void GutterGameTest() { Game g = new Game(); }

Page 12: Bowling Game Kata in C# Adapted

The first test.namespace BowlingGameKata{ public class Game { }}

[Test] public void GutterGameTest() { Game g = new Game(); }

Page 13: Bowling Game Kata in C# Adapted

The first test. [Test] public void GutterGameTest() { Game g = new Game(); }

public class Game{}

Page 14: Bowling Game Kata in C# Adapted

The first test.[Test]public void GutterGameTest(){ Game g = new Game();

for (int i = 0; i < 20; i++) { g.Roll(0); }}

public class Game{}

Page 15: Bowling Game Kata in C# Adapted

The first test.public class Game{ public void Roll(int p) { throw new System.NotImplementedException(); }}

[Test]public void GutterGameTest(){ Game g = new Game();

for (int i = 0; i < 20; i++) { g.Roll(0); }}

Page 16: Bowling Game Kata in C# Adapted

The first test.[Test]public void GutterGameTest(){ Game g = new Game();

for (int i = 0; i < 20; i++) { g.Roll(0); }

Assert.That(g.Score(), Is.EqualTo(0));}

public class Game{ public void Roll(int p) { throw new System.NotImplementedException(); }}

Page 17: Bowling Game Kata in C# Adapted

The first test.

Failed GutterGameTest threw exception

[Test]public void GutterGameTest(){ Game g = new Game();

for (int i = 0; i < 20; i++) { g.Roll(0); }

Assert.That(g.Score(), Is.EqualTo(0));}

public class Game{ public void Roll(int p) { throw new System.NotImplementedException(); }

public object Score() { throw new System.NotImplementedException(); }}

Page 18: Bowling Game Kata in C# Adapted

The first test.[Test]public void GutterGameTest(){ Game g = new Game();

for (int i = 0; i < 20; i++) { g.Roll(0); }

Assert.That(g.Score(), Is.EqualTo(0));}

public class Game{ private int score;

public void Roll(int pins) { }

public int Score() { return score; }}

Page 19: Bowling Game Kata in C# Adapted

The Second test.[Test]public void AllOnesTest(){ Game g = new Game(); for (int i = 0; i < 20; i++) { g.Roll(1); }

Assert.That(g.Score(), Is.EqualTo(20));}

public class Game{ private int score;

public void Roll(int pins) { }

public int Score() { return score; }}

Page 20: Bowling Game Kata in C# Adapted

The Second test.- Game creation is duplicated- roll loop is duplicated

[Test]public void AllOnesTest(){ Game g = new Game(); for (int i = 0; i < 20; i++) { g.Roll(1); }

Assert.That(g.Score(), Is.EqualTo(20));}

public class Game{ private int score;

public void Roll(int pins) { }

public int Score() { return score; }}

Page 21: Bowling Game Kata in C# Adapted

The Second test.- Game creation is duplicated- roll loop is duplicated

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

[Test]public void AllOnesTest(){ Game g = new Game(); for (int i = 0; i < 20; i++) { g.Roll(1); }

Assert.That(g.Score(), Is.EqualTo(20));}

public class Game{ private int score;

public void Roll(int pins) { }

public int Score() { return score; }}

Page 22: Bowling Game Kata in C# Adapted

The Second test.- roll loop is duplicated

private Game g;

[SetUp]public void Setup() { g = new Game();}

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

Assert.That(g.Score(), Is.EqualTo(0));}

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

Assert.That(g.Score(), Is.EqualTo(20));}

public class Game{ private int score;

public void Roll(int pins) { score += pins; }

public int Score() { return score; }}

Page 23: Bowling Game Kata in C# Adapted

The Second test.- roll loop is duplicated

[Test]public void GutterGameTest(){ int rolls = 20; int pins = 0; for (int i = 0; i < rolls; i++) { g.Roll(pins); }

Assert.That(g.Score(), Is.EqualTo(0));}

public class Game{ private int score;

public void Roll(int pins) { score += pins; }

public int Score() { return score; }}

Page 24: Bowling Game Kata in C# Adapted

The Second test.- roll loop is duplicated

[Test]public void GutterGameTest(){ int rolls = 20; int pins = 0; RollMany(rolls, pins); for (int i = 0; i < rolls; i++) { g.Roll(pins); }

Assert.That(g.Score(), Is.EqualTo(0));}

public class Game{ private int score;

public void Roll(int pins) { score += pins; }

public int Score() { return score; }}

Page 25: Bowling Game Kata in C# Adapted

[Test]public void GutterGameTest(){ RollMany(20, 0);

Assert.That(g.Score(), Is.EqualTo(0));}

private void RollMany(int rolls, int pins){ for (int i = 0; i < rolls; i++) { g.Roll(pins); }}

The Second test.public class Game{ private int score;

public void Roll(int pins) { score += pins; }

public int Score() { return score; }}

Page 26: Bowling Game Kata in C# Adapted

The Second test.[Test]public void GutterGameTest(){ RollMany(20, 0);

Assert.That(g.Score(), Is.EqualTo(0));}

[Test]public void AllOnesTest(){ RollMany(20, 1);

Assert.That(g.Score(), Is.EqualTo(20));}

private void RollMany(int rolls, int pins){ for (int i = 0; i < rolls; i++) { g.Roll(pins); }}

public class Game{ private int score;

public void Roll(int pins) { score += pins; }

public int Score() { return score; }}

Page 27: Bowling Game Kata in C# Adapted

The Third test.- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

Assert.That(g.Score(), Is.EqualTo(16));}

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

public class Game{ private int score;

public void Roll(int pins) { score += pins; }

public int Score() { return score; }}

Page 28: Bowling Game Kata in C# Adapted

public class Game{ private int score;

public void Roll(int pins) { score += pins; }

public int Score() { return score; }}

The Third test.

tempted to use flag to remember previous roll. So design must be wrong.

- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

Assert.That(g.Score(), Is.EqualTo(16));}

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

Page 29: Bowling Game Kata in C# Adapted

public class Game{ private int score;

public void Roll(int pins) { score += pins; }

public int Score() { return score; }}

The Third test. Roll() calculates score, but name does not imply that.

Score() does not calculate score, but name implies that it does.

Design is wrong. Responsibilities are misplaced.

- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

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

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

Page 30: Bowling Game Kata in C# Adapted

The Third test.[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); //Assert.That(g.Score(), Is.EqualTo(16));}

- ugly comment in test.

public class Game{ private int score;

public void Roll(int pins) { score += pins; }

public int Score() { return score; }}

Page 31: Bowling Game Kata in C# Adapted

The Third test.public class Game{ private int score; private int[] rolls = new int[21]; private int currentRoll;

public void Roll(int pins) { rolls[currentRoll++] = pins; score += pins; }

public int Score() { return score; }}

- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); //Assert.That(g.Score(), Is.EqualTo(16));}

Page 32: Bowling Game Kata in C# Adapted

The Third test.public class Game{ private int[] rolls = new int[21]; private int currentRoll;

public void Roll(int pins) { rolls[currentRoll++] = pins; }

public int Score() { int score = 0;

for (int i = 0; i < rolls.Length; i++) { score += rolls[i]; }

return score; }}

- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); //Assert.That(g.Score(), Is.EqualTo(16));}

Page 33: Bowling Game Kata in C# Adapted

The Third test.- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

Assert.That(g.Score(), Is.EqualTo(16));}

public class Game{ private int[] rolls = new int[21]; private int currentRoll;

public void Roll(int pins) { rolls[currentRoll++] = pins; }

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>

Page 34: Bowling Game Kata in C# Adapted

public int Score(){ int score = 0;

for (int i = 0; i < rolls.Length; i++) { // spare if (rolls[i] + rolls[i+1] == 10) { score += ... }

score += rolls[i]; }

return score;}

The Third test.

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.

- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

Assert.That(g.Score(), Is.EqualTo(16));}

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

Page 35: Bowling Game Kata in C# Adapted

public class Game{ private int[] rolls = new int[21]; private int currentRoll;

public void Roll(int pins) { rolls[currentRoll++] = pins; }

public int Score() { int score = 0;

for (int i = 0; i < rolls.Length; i++) { score += rolls[i]; }

return score; }}

The Third test.- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

//Assert.That(g.Score(), Is.EqualTo(16)); Assert.Inconclusive();}

Page 36: Bowling Game Kata in C# Adapted

The Third test.public int Score(){ int score = 0; int i = 0;

for (int frame = 0; frame < 10; frame++) { score += rolls[i] + rolls[i + 1]; i += 2; }

return score;}

- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); //Assert.That(g.Score(), Is.EqualTo(16)); Assert.Inconclusive();}

Page 37: Bowling Game Kata in C# Adapted

The Third test.- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

Assert.That(g.Score(), Is.EqualTo(16));}

public int Score(){ int score = 0; int i = 0;

for (int frame = 0; frame < 10; frame++) { score += rolls[i] + rolls[i + 1]; i += 2; }

return score;}

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

Page 38: Bowling Game Kata in C# Adapted

The Third test.public int Score(){ int score = 0; int i = 0;

for (int frame = 0; frame < 10; frame++) { // spare if (rolls[i] + rolls[i + 1] == 10) { score += 10 + rolls[i + 2]; i += 2; } else { score += rolls[i] + rolls[i + 1]; i += 2; } }

return score;}

- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

Assert.That(g.Score(), Is.EqualTo(16));}

Page 39: Bowling Game Kata in C# Adapted

public int Score(){ int score = 0; int roll = 0;

for (int frame = 0; frame < 10; frame++) { // spare if (rolls[roll] + rolls[roll + 1] == 10) { score += 10 + rolls[roll + 2]; roll += 2; } else { score += rolls[roll] + rolls[roll + 1]; roll += 2; } }

return score;}

The Third test.- ugly comment in test.- ugly comment in

conditional.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

Assert.That(g.Score(), Is.EqualTo(16));}

Page 40: Bowling Game Kata in C# Adapted

public int Score(){ int score = 0; int roll = 0;

for (int frame = 0; frame < 10; frame++) { if (IsSpare(roll)) { score += 10 + rolls[roll + 2]; 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.- ugly comment in test.

[Test]public void OneSpareTest(){ g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0);

Assert.That(g.Score(), Is.EqualTo(16));}

Page 41: Bowling Game Kata in C# Adapted

The Third test.[Test]public void OneSpareTest(){ RollSpare(); g.Roll(3); RollMany(17, 0);

Assert.That(g.Score(), Is.EqualTo(16));}

private void RollSpare(){ g.Roll(5); g.Roll(5);}

public int Score(){ int score = 0; int roll = 0;

for (int frame = 0; frame < 10; frame++) { if (IsSpare(roll)) { score += 10 + rolls[roll + 2]; 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;}

Page 42: Bowling Game Kata in C# Adapted

The Fourth test.- ugly comment in OneStrikeTest.

[Test]public void OneStrikeTest(){ g.Roll(10); // strike g.Roll(3); g.Roll(4); RollMany(16, 0); Assert.That(g.Score(), Is.EqualTo(24));}

public int Score(){ int score = 0; int roll = 0;

for (int frame = 0; frame < 10; frame++) { if (IsSpare(roll)) { 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>

Page 43: Bowling Game Kata in C# Adapted

The Fourth test.- ugly comment in

OneStrikeTest.- ugly comment in

conditional.- ugly expressions. public int Score()

{ int score = 0; int roll = 0;

for (int frame = 0; frame < 10; frame++) { if (rolls[roll] == 10) // strike { 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;}

[Test]public void OneStrikeTest(){ g.Roll(10); // strike g.Roll(3); g.Roll(4); RollMany(16, 0); Assert.That(g.Score(), Is.EqualTo(24));}

Page 44: Bowling Game Kata in C# Adapted

The Fourth test.- ugly comment in OneStrikeTest.

- ugly comment in conditional. public int Score()

{ int score = 0; int roll = 0;

for (int frame = 0; frame < 10; frame++) { if (rolls[roll] == 10) // strike { score += 10 + StrikeBonus(roll); 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];}

[Test]public void OneStrikeTest(){ g.Roll(10); // strike g.Roll(3); g.Roll(4); RollMany(16, 0); Assert.That(g.Score(), Is.EqualTo(24));}

Page 45: Bowling Game Kata in C# Adapted

The Fourth test.public int Score(){ int score = 0; int roll = 0;

for (int frame = 0; frame < 10; frame++) { 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;}

private bool IsStrike(int roll){ return rolls[roll] == 10;}

- ugly comment in OneStrikeTest.

[Test]public void OneStrikeTest(){ g.Roll(10); // strike g.Roll(3); g.Roll(4); RollMany(16, 0); Assert.That(g.Score(), Is.EqualTo(24));}

Page 46: Bowling Game Kata in C# Adapted

The Fourth test.[Test]public void OneStrikeTest(){ RollStrike(); g.Roll(3); g.Roll(4); RollMany(16, 0); Assert.That(g.Score(), Is.EqualTo(24));}

private void RollStrike(){ g.Roll(10);}

public int Score(){ int score = 0; int roll = 0;

for (int frame = 0; frame < 10; frame++) { 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;}

Page 47: Bowling Game Kata in C# Adapted

The Fifth test.[Test]public void PerfectGameTest(){ RollMany(12, 10); Assert.That(g.Score(), Is.EqualTo(300));}

public int Score(){ int score = 0; int roll = 0;

for (int frame = 0; frame < 10; frame++) { 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;}

Page 48: Bowling Game Kata in C# Adapted

Discussion/Review

Page 49: Bowling Game Kata in C# Adapted

Adapted from: