Presentation is loading. Please wait.

Presentation is loading. Please wait.

Bowling Game Kata In C# with NUnit.

Similar presentations


Presentation on theme: "Bowling Game Kata In C# with NUnit."— Presentation transcript:

1 Bowling Game Kata In C# with NUnit

2 Bowling Game Kata Object Mentor, Inc. www.objectmentor.com
blog.objectmentor.com fitnesse.org Copyright  by Object Mentor, Inc All copies must retain this page unchanged.

3 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.

4 Game Rules Summary A game has ten frames
Frames 1 – 9 have one or two rolls A strike on the first roll ends the frame The score for a spare or a strike depends on the frame’s successor A strike earns ten pins plus the pins for the next two balls thrown A spare earns ten pins plus the pins for the next ball thrown The tenth frame has up to three rolls You get bonus roll(s) following a strike or spare in the tenth frame

5 Requirements Write a class named Game that has two methods:
Roll(pins : int) Called each time the player rolls a ball Pins is the number of pins knocked down Score() : int Called only at the very end of the game Returns the total score for that game I don’t really get why he calls this requirements

6 A quick design session Clearly we need the Game class.
Why implementation is completely different from design ? Excellent question! It's because we could not see how simple the solution really was when we drew that design! == UB

7 A quick design session A game has 10 frames.

8 A quick design session A frame has 1 or two rolls.

9 A quick design session The tenth frame has two or three rolls.
It is different from all the other frames.

10 A quick design session The score function must iterate through all the
frames, and calculate all their scores.

11 A quick design session The score for a spare or a strike depends on the frame’s successor

12 Begin Red Green Refactor
Create a Class Library project named BowlingGame Add NUnit NuGet package Create a unit test class named BowlingGameTest Red Green Refactor

13 The First Test Gutter game

14 No tests found in BowlingGameTest
using NUnit.Framework; [TestFixture] public class BowlingGameTest { } No tests found in BowlingGameTest

15 using NUnit.Framework; [TestFixture] public class BowlingGameTest {
public void Gutter_game() var game = new Game(); }

16 using NUnit.Framework;
[TestFixture] public class BowlingGameTest { [Test] public void Gutter_game() var game = new Game(); } public class Game { }

17 using NUnit.Framework; [TestFixture] public class BowlingGameTest {
public void Gutter_game() var game = new Game(); for (var i = 0; i < 20; i++) game.Roll(0); } public class Game { }

18 using NUnit.Framework; [TestFixture] public class BowlingGameTest {
public class Game { public void Roll(int pins) } using NUnit.Framework; [TestFixture] public class BowlingGameTest { [Test] public void Gutter_game() var game = new Game(); for (var i = 0; i < 20; i++) game.Roll(0); }

19 using NUnit.Framework; [TestFixture] public class BowlingGameTest {
public void Gutter_game() var game = new Game(); for (var i = 0; i < 20; i++) game.Roll(0); Assert.That(game.Score(), Is.EqualTo(0)); } public class Game { public void Roll(int pins) }

20 expected:0 but was: -1 using NUnit.Framework; [TestFixture]
public class BowlingGameTest { [Test] public void Gutter_game() var game = new Game(); for (var i = 0; i < 20; i++) game.Roll(0); Assert.That(game.Score(), Is.EqualTo(0)); } public class Game { public void Roll(int pins) } public int Score() return -1; expected:0 but was: -1

21 Change Score() to return 0
using NUnit.Framework; [TestFixture] public class BowlingGameTest { [Test] public void Gutter_game() var game = new Game(); for (var i = 0; i < 20; i++) game.Roll(0); Assert.That(game.Score(), Is.EqualTo(0)); } public class Game { public void Roll(int pins) } public int Score() return 0; Change Score() to return 0

22 The Second Test Single pins

23 expected:20 but was: 0 Game creation is duplicated
Roll loop is duplicated [TestFixture] public class BowlingGameTest { [Test] public void Gutter_game() var game = new Game(); for (var i = 0; i < 20; i++) game.Roll(0); Assert.That(game.Score(), Is.EqualTo(0)); } public void Rolling_All_ones() game.Roll(1); Assert.That(game.Score(), Is.EqualTo(20)); public class Game { public void Roll(int pins) } public int Score() return 0; Game creation is duplicated Roll loop is duplicated expected:20 but was: 0

24 expected:20 but was: 0 Game creation is duplicated
Roll loop is duplicated [TestFixture] public class BowlingGameTest { [Test] public void Gutter_game() var game = new Game(); for (var i = 0; i < 20; i++) game.Roll(0); Assert.That(game.Score(), Is.EqualTo(0)); } public void Rolling_All_ones() game.Roll(1); Assert.That(game.Score(), Is.EqualTo(20)); public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score; Introduce _scope field and update Roll() to make test pass expected:20 but was: 0

25 Game creation is duplicated Roll loop is duplicated
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } [Test] public void Gutter_game() for (var i = 0; i < 20; i++) _game.Roll(0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() _game.Roll(1); Assert.That(_game.Score(), Is.EqualTo(20)); public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score; Introduce _game field and SetUp()

26 Roll loop is duplicated
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } [Test] public void Gutter_game() var n = 20; var pins = 0; for (var i = 0; i < n; i++) _game.Roll(pins); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() for (var i = 0; i < 20; i++) _game.Roll(1); Assert.That(_game.Score(), Is.EqualTo(20)); public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score; First step of removing duplicate roll loop

27 Roll loop is duplicated
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } [Test] public void Gutter_game() var n = 20; var pins = 0; RollMany(n, pins); Assert.That(_game.Score(), Is.EqualTo(0)); private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); public void All_ones() for (var i = 0; i < 20; i++) _game.Roll(1); Assert.That(_game.Score(), Is.EqualTo(20)); public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score; Second step of removing duplicate roll loop

28 Roll loop is duplicated
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); public void All_ones() for (var i = 0; i < 20; i++) _game.Roll(1); Assert.That(_game.Score(), Is.EqualTo(20)); public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score;

29 Roll loop is duplicated
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score;

30 Roll loop is duplicated
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score; Moves Roll() above first test that uses it What about the naming of the n parameter? Uncle Bob does not mention it but I think that is a bad name. How about times, rolls, or numberOfRolls?

31 The Third Test A single spare

32 Comment needed to signify a spare
Ugly comment Design is wrong, responsibilities are misplaced [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() _game.Roll(5); _game.Roll(5); // spare _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); Tempted to use a flag to remember previous roll. So design must be wrong. public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score; Comment needed to signify a spare expected:16 but was: 13

33 expected:16 but was: 13 Ugly comment
Design is wrong, responsibilities are misplaced [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() _game.Roll(5); _game.Roll(5); // spare _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score; Roll() calculates score, but name does not imply that. Score() does not calculate score, but name implies that it does. No code changes, just showing issues with Game design expected:16 but was: 13

34 Design is wrong, responsibilities are misplaced
Ugly comment Design is wrong, responsibilities are misplaced [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); //[Test] //public void One_spare() //{ // _game.Roll(5); // _game.Roll(5); // spare // _game.Roll(3); // RollMany(17, 0); // Assert.That(_game.Score(), Is.EqualTo(16)); //} public class Game { private int _score; public void Roll(int pins) _score += pins; } public int Score() return _score; Have to go back to green so we can refactor Game to support spares

35 Design is wrong, responsibilities are misplaced
Ugly comment Design is wrong, responsibilities are misplaced [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); //[Test] //public void One_spare() //{ // _game.Roll(5); // _game.Roll(5); // spare // _game.Roll(3); // RollMany(17, 0); // Assert.That(_game.Score(), Is.EqualTo(16)); //} public class Game { private int _score; private int[] _rolls = new int[21]; private int _currentRoll; public void Roll(int pins) _score += pins; _rolls[_currentRoll++] = pins; } public int Score() return _score; 34 Refactor Roll() to track what frame the Game is on

36 Design is wrong, responsibilities are misplaced
Ugly comment Design is wrong, responsibilities are misplaced [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); //[Test] //public void One_spare() //{ // _game.Roll(5); // _game.Roll(5); // spare // _game.Roll(3); // RollMany(17, 0); // Assert.That(_game.Score(), Is.EqualTo(16)); //} public class Game { private int  _score; private int[] _rolls = new int[21]; private int _currentRoll; public void Roll(int pins) _score += pins; _rolls[_currentRoll++] = pins; } public int Score() var score = 0; for(var i = 0; i < _rolls.Length; i++) score += _rolls[i]; return score; 35 Refactor Score() to calculate the score but with no frame support yet

37 Design is wrong, responsibilities are misplaced
Ugly comment Design is wrong, responsibilities are misplaced [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); //[Test] //public void One_spare() //{ // _game.Roll(5); // _game.Roll(5); // spare // _game.Roll(3); // RollMany(17, 0); // Assert.That(_game.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() var score = 0; for(var i = 0; i < _rolls.Length; i++) score += _rolls[i]; return score; 36 removes _score field

38 expected:16 but was: 13 Ugly comment 37 [TestFixture]
public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() _game.Roll(5); _game.Roll(5); // spare _game.Roll(3); RollMany(17, 0); Assert.That(_game.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() var score = 0; for(var i = 0; i < _rolls.Length; i++) score += _rolls[i]; return score; 37 expected:16 but was: 13

39 Need to walk through array two balls (one frame) at a time.
Ugly comment [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() _game.Roll(5); _game.Roll(5); // spare _game.Roll(3); RollMany(17, 0); Assert.That(_game.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() var score = 0; for(var i = 0; i < _rolls.Length; i++) if (_rolls[i] + _rolls[i + 1] == 10) // spare 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. 38 A spare can be triggered here when the rolls are not in the same frame

40 Ugly comment 39 Design is wrong so go back to green so we can refactor
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); //[Test] //public void One_spare() //{ // _game.Roll(5); // _game.Roll(5); // spare // _game.Roll(3); // RollMany(17, 0); // Assert.That(_game.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() var score = 0; for(var i = 0; i < _rolls.Length; i++) score += _rolls[i]; return score; 39 Design is wrong so go back to green so we can refactor

41 Ugly comment [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); //[Test] //public void One_spare() //{ // _game.Roll(5); // _game.Roll(5); // spare // _game.Roll(3); // RollMany(17, 0); // Assert.That(_game.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() var score = 0; var i = 0; for (var frame = 0; frame < 10; frame++) score += _rolls[i] + _rolls[i + 1]; i += 2; return score; 40 Introduce frame tracking but keep scoring algorithm the same, i.e., does not account for spares yet. Refactor under green

42 expected:16 but was: 13 Ugly comment 41
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() _game.Roll(5); _game.Roll(5); // spare _game.Roll(3); RollMany(17, 0); Assert.That(_game.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() var score = 0; var i = 0; for (var frame = 0; frame < 10; frame++) score += _rolls[i] + _rolls[i + 1]; i += 2; return score; 41 Add back the failing spare test expected:16 but was: 13

43 Ugly comment 42 Make spare test pass
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() _game.Roll(5); _game.Roll(5); // spare _game.Roll(3); RollMany(17, 0); Assert.That(_game.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() var score = 0; var i = 0; for (var frame = 0; frame < 10; frame++) if (_rolls[i] + _rolls[i + 1] == 10) // spare score += 10 + _rolls[i + 2]; i += 2; else score += _rolls[i] + _rolls[i + 1]; return score; 42 Make spare test pass Note, there is not a test for 10 pins across frames, e.g., [0,5] [5,0] 10 pins between frames 1 and 2

44 Ugly comment Ugly comment in conditional
i is a bad name for this variable [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() _game.Roll(5); _game.Roll(5); // spare _game.Roll(3); RollMany(17, 0); Assert.That(_game.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() var score = 0; var i = 0; for (var frame = 0; frame < 10; frame++) if (_rolls[i] + _rolls[i + 1] == 10) // spare score += 10 + _rolls[i + 2]; i += 2; else score += _rolls[i] + _rolls[i + 1]; return score; i is bad variable name Ugly comment 43

45 Ugly comment Ugly comment in conditional
i is a bad name for this variable [TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() _game.Roll(5); _game.Roll(5); // spare _game.Roll(3); RollMany(17, 0); Assert.That(_game.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() var score = 0; var frameIndex = 0; for (var frame = 0; frame < 10; frame++) if (_rolls[frameIndex] + _rolls[frameIndex + 1] == 10) // spare score += 10 + _rolls[frameIndex + 2]; frameIndex += 2; else score += _rolls[frameIndex] + _rolls[frameIndex + 1]; return score; 44 Renames i to frameIndex

46 Ugly comment Ugly comment in conditional 45
[TestFixture] public class BowlingGameTest { private Game _game; [SetUp] public void SetUp() _game = new Game(); } private void RollMany(int n, int pins) for (var i = 0; i < n; i++) _game.Roll(pins); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() _game.Roll(5); _game.Roll(5); // spare _game.Roll(3); RollMany(17, 0); Assert.That(_game.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() var score = 0; var frameIndex = 0; for (var frame = 0; frame < 10; frame++) if (IsSpareFrame(frameIndex) score += 10 + _rolls[frameIndex + 2]; frameIndex += 2; else score += _rolls[frameIndex] + _rolls[frameIndex + 1]; return score; private bool IsSpareFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1] == 10; 45 extract method IsSpareFrame(int)

47 Ugly comment 46 Extract method RollSpare() public class Game {
private int[] _rolls = new int[21]; private int _currentRoll; public void Roll(int pins) _rolls[_currentRoll++] = pins; } public int Score() var score = 0; var frameIndex = 0; for (var frame = 0; frame < 10; frame++) if (IsSpareFrame(frameIndex) score += 10 + _rolls[frameIndex + 2]; frameIndex += 2; else score += _rolls[frameIndex] + _rolls[frameIndex + 1]; return score; private bool IsSpareFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1] == 10; // ... private void RollMany(int n, int pins) { for (var i = 0; i < n; i++) _game.Roll(pins); } [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() RollSpare(); _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); private void RollSpare() _game.Roll(5); 46 Extract method RollSpare()

48 Extract method RollSpare()
public class Game { private int[] _rolls = new int[21]; private int _currentRoll; public void Roll(int pins) _rolls[_currentRoll++] = pins; } public int Score() var score = 0; var frameIndex = 0; for (var frame = 0; frame < 10; frame++) if (IsSpareFrame(frameIndex) score += 10 + _rolls[frameIndex + 2]; frameIndex += 2; else score += _rolls[frameIndex] + _rolls[frameIndex + 1]; return score; private bool IsSpareFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1] == 10; // ... private void RollMany(int n, int pins) { for (var i = 0; i < n; i++) _game.Roll(pins); } private void RollSpare() _game.Roll(5); [Test] public void Gutter_game() RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() RollSpare(); _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); 46 Extract method RollSpare()

49 The Fourth Test Single Strike

50 expected:24 but was: 17 Ugly comment needed to denote a strike
// ... [Test] public void Gutter_game() { RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); } public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() RollSpare(); _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); public void One_strike() _game.Roll(10); // strike _game.Roll(4); RollMany(16, 0); Assert.That(_game.Score(), Is.EqualTo(24)); private void RollSpare() _game.Roll(5); public class Game { private int[] _rolls = new int[21]; private int _currentRoll; public void Roll(int pins) _rolls[_currentRoll++] = pins; } public int Score() var score = 0; var frameIndex = 0; for (var frame = 0; frame < 10; frame++) if (IsSpareFrame(frameIndex) score += 10 + _rolls[frameIndex + 2]; frameIndex += 2; else score += _rolls[frameIndex] + _rolls[frameIndex + 1]; return score; private bool IsSpareFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1] == 10; Ugly comment expected:24 but was: 17

51 Ugly comment needed to denote a strike
Ugly expressions to calculate scores (x3) // ... [Test] public void Gutter_game() { RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); } public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() RollSpare(); _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); public void One_strike() _game.Roll(10); // strike _game.Roll(4); RollMany(16, 0); Assert.That(_game.Score(), Is.EqualTo(24)); private void RollSpare() _game.Roll(5); // ... public void Roll(int pins) { _rolls[_currentRoll++] = pins; } public int Score() var score = 0; var frameIndex = 0; for (var frame = 0; frame < 10; frame++) if (_rolls[frameIndex] == 10) // strike score += 10 + _rolls[frameIndex + 1] + _rolls[frameIndex + 2]; frameIndex++; else if (IsSpareFrame(frameIndex) score += 10 + _rolls[frameIndex + 2]; frameIndex += 2; else score += _rolls[frameIndex] + _rolls[frameIndex + 1]; return score; private bool IsSpareFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1] == 10; Ugly comment Ugly expression Ugly expression Ugly expression Add scoring logic to handle strikes Now we have three ugly expressions to calculate scores; strikes, spares and normal

52 Ugly comment needed to denote a strike
Ugly expressions to calculate scores (x2) // ... [Test] public void Gutter_game() { RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); } public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() RollSpare(); _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); public void One_strike() _game.Roll(10); // strike _game.Roll(4); RollMany(16, 0); Assert.That(_game.Score(), Is.EqualTo(24)); private void RollSpare() _game.Roll(5); // ... public int Score() { var score = 0; var frameIndex = 0; for (var frame = 0; frame < 10; frame++) if (_rolls[frameIndex] == 10) // strike score += 10 + StrikeBonus(frameIndex); frameIndex++; } else if (IsSpareFrame(frameIndex) score += 10 + _rolls[frameIndex + 2]; frameIndex += 2; else score += _rolls[frameIndex] + _rolls[frameIndex + 1]; return score; private int StrikeBonus(int frameIndex) return _rolls[frameIndex + 1] + _rolls[frameIndex + 2]; private bool IsSpareFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1] == 10; Replace score calculation expression with StrikeBonus() method

53 Ugly comment needed to denote a strike
Ugly expressions to calculate scores (x1) // ... [Test] public void Gutter_game() { RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); } public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() RollSpare(); _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); public void One_strike() _game.Roll(10); // strike _game.Roll(4); RollMany(16, 0); Assert.That(_game.Score(), Is.EqualTo(24)); private void RollSpare() _game.Roll(5); // ... public int Score() { var score = 0; var frameIndex = 0; for (var frame = 0; frame < 10; frame++) if (_rolls[frameIndex] == 10) // strike score += 10 + StrikeBonus(frameIndex); frameIndex++; } else if (IsSpareFrame(frameIndex) score += SpareBonus(frameindex); frameIndex += 2; else score += _rolls[frameIndex] + _rolls[frameIndex + 1]; return score; private int StrikeBonus(int frameIndex) return _rolls[frameIndex + 1] + _rolls[frameIndex + 2]; private int SpareBonus(int frameIndex) return _rolls[frameIndex + 2]; private bool IsSpareFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1] == 10;

54 Ugly comment needed to denote a strike
Ugly expressions to calculate scores // ... public int Score() { var score = 0; var frameIndex = 0; for (var frame = 0; frame < 10; frame++) if (_rolls[frameIndex] == 10) // strike score += 10 + StrikeBonus(frameIndex); frameIndex++; } else if (IsSpareFrame(frameIndex) score += SpareBonus(frameindex); frameIndex += 2; else score += SumOfBallsInFrame(frameIndex); return score; private int StrikeBonus(int frameIndex) return _rolls[frameIndex + 1] + _rolls[frameIndex + 2]; private int SpareBonus(int frameIndex) return _rolls[frameIndex + 2]; private bool IsSpareFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1] == 10; private int SumOfBallsInFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1]; // ... [Test] public void Gutter_game() { RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); } public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() RollSpare(); _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); public void One_strike() _game.Roll(10); // strike _game.Roll(4); RollMany(16, 0); Assert.That(_game.Score(), Is.EqualTo(24)); private void RollSpare() _game.Roll(5);

55 Ugly comment needed to denote a strike
// ... for (var frame = 0; frame < 10; frame++) { if (IsStrike(frameIndex)) score += 10 + StrikeBonus(frameIndex); frameIndex++; } else if (IsSpareFrame(frameIndex) score += SpareBonus(frameindex); frameIndex += 2; else score +=  SumOfBallsInFrame(frameIndex); return score; private bool IsStrike(int frameIndex) return _rolls[frameIndex] == 10; private int StrikeBonus(int frameIndex) return _rolls[frameIndex + 1] + _rolls[frameIndex + 2]; private int SpareBonus(int frameIndex) return _rolls[frameIndex + 2]; private int SumOfBallsInFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1]; // ... [Test] public void Gutter_game() { RollMany(20, 0); Assert.That(_game.Score(), Is.EqualTo(0)); } public void All_ones() RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); public void One_spare() RollSpare(); _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); public void One_strike() _game.Roll(10); // strike _game.Roll(4); RollMany(16, 0); Assert.That(_game.Score(), Is.EqualTo(24)); private void RollSpare() _game.Roll(5); Replaces expression with IsStrike()

56 Ugly comment needed to denote a strike
// ... for (var frame = 0; frame < 10; frame++) { if (IsStrike(frameIndex)) score += 10 + StrikeBonus(frameIndex); frameIndex++; } else if (IsSpareFrame(frameIndex) score += SpareBonus(frameindex); frameIndex += 2; else score +=  SumOfBallsInFrame(frameIndex); return score; private bool IsStrike(int frameIndex) return _rolls[frameIndex] == 10; private int StrikeBonus(int frameIndex) return _rolls[frameIndex + 1] + _rolls[frameIndex + 2]; private int SpareBonus(int frameIndex) return _rolls[frameIndex + 2]; private int SumOfBallsInFrame(int frameIndex) return _rolls[frameIndex] + _rolls[frameIndex + 1]; // ... [Test] public void All_ones() { RollMany(20, 1); Assert.That(_game.Score(), Is.EqualTo(20)); } public void One_spare() RollSpare(); _game.Roll(3); RollMany(17, 0); Assert.That(_game.Score(), Is.EqualTo(16)); public void One_strike() RollStrike(); _game.Roll(4); RollMany(16, 0); Assert.That(_game.Score(), Is.EqualTo(24)); private void RollStrike() _game.Roll(10); private void RollSpare() _game.Roll(5);

57 The Fifth Test A perfect game

58 End


Download ppt "Bowling Game Kata In C# with NUnit."

Similar presentations


Ads by Google