Download presentation
Presentation is loading. Please wait.
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
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.