Bowling Game Kata In C# with NUnit.

Slides:



Advertisements
Similar presentations
Olve Maudal TDD in CPP 05 February 2007 (frontpage) TDD in CPP... or The Bowling Game Kata with C++ and QUnit Olve Maudal, (A 10 minutes Lightning.
Advertisements

Bowling Game Kata Object Mentor, Inc. fitnesse.org Copyright 2005 by Object Mentor, Inc All copies must retain this page unchanged.
1 CSC 221: Computer Programming I Fall 2006 interacting objects modular design: dot races constants, static fields cascading if-else, logical operators.
CSE 332: C++ exceptions Overview of C++ Exceptions Normal program control flow is halted –At the point where an exception is thrown The program call stack.
29-Jun-15 Recursion. 2 Definitions I A recursive definition is a definition in which the thing being defined occurs as part of its own definition Example:
By Samantha Furman.  Bowling is a great social activity and arm work out.  Bowling isn’t very strenuous, but it is still an active way to spend time.
Chapter 7 Designing Classes. Class Design When we are developing a piece of software, we want to design the software We don’t want to just sit down and.
Scrum and Hyper-productivity Object Mentor, Inc. Copyright  by Object Mentor, Inc All Rights Reserved fitnesse.org
Data Objects (revisited) Recall that values are stored in data objects, and that each data object holds one value of a particular type. Data objects may.
Iteration. Adding CDs to Vic Stack In many of the programs you write, you would like to have a CD on the stack before the program runs. To do this, you.
1 CSC 221: Computer Programming I Spring 2010 interaction & design  modular design: roulette game  constants, static fields  % operator, string equals.
Refactoring Improving the structure of existing code Refactoring1.
Refactoring1 Improving the structure of existing code.
1 Advanced Issues on Classes Part 3 Reference variables (Tapestry pp.581, Horton 176 – 178) Const-reference variables (Horton 176 – 178) object sharing:
11 Adding Tomato Targets Session Session Overview  We now have a game which lets a player bounce a piece of cheese on a bread bat  Now we have.
Level 1 - FOUNDATION COACH Scoring FUNdamentals Instructor.
Module 3: Steering&Arrays #1 2000/01Scientific Computing in OOCourse code 3C59 Module 3: Algorithm steering elements If, elseif, else Switch and enumerated.
Chapter 21 Test-Driven Development 1CS6359 Fall 2011 John Cole.
Bowling Game Kata Object Mentor, Inc. fitnesse.org Copyright  2005 by Object Mentor, Inc All copies must retain this page unchanged.
By Mr. Schmidt Bowling. Bowling Etiquette Tip #1 – Observe the foul line at all times. Tip #2 – Be ready to bowl when it is your turn. Tip #3 – Don’t.
M1G Introduction to Programming 2 3. Creating Classes: Room and Item.
SEG 4110 – Advanced Software Design and Reengineering Topic T Introduction to Refactoring.
Can You Throw A Strike? Start! Click here to Start Game.
Test-Driving ASP.NET Development Tampa Code Camp – July 15 th, 2006 Cory Foy
Refactoring1 Improving the structure of existing code.
1 CSC 221: Computer Programming I Fall 2005 simple conditionals and expressions  if statements, if-else  increment/decrement, arithmetic assignments.
Session 7 Introduction to Inheritance. Accumulator Example a simple calculator app classes needed: –AdderApp - contains main –AddingFrame - GUI –CloseableFrame.
CSSE 375 Organizing Data – Part 1 Shawn and Steve Q1.
Refactoring. DCS – SWC 2 Refactoring ”A change made to the internal structure of software to make it easier to understand and cheaper to modify without.
Clue Paths. Memory Alias Review Remember that it’s possible for two unique variables to point to the same memory location myList MyClass mc 0x100 someFn.
1. Understanding the execution of local variable declaration (in a method body) new expression ( 3 steps ) method call (method frames, call stack) examples.
Minimising memory churn
Chapter Topics Chapter 16 discusses the following main topics:
Welcome to Simple Bowling
Computer Science 4 Mr. Gerb
Building with Numbers Module 4: Investigation 2 Timers and Stopwatches
Building Java Programs
C++ coding standard suggestion… Separate reasoning from action, in every block. Hi, this talk is to suggest a rule (or guideline) to simplify C++ code.
Lecture 15: More Iterators
CSE 143 Introduction to C++ [Appendix B] 4/11/98.
slides created by Marty Stepp
Counted Loops.
Recursion 12-Nov-18.
Bowling Game Kata Object Mentor, Inc.
Writing Methods AP Computer Science A.
slides created by Marty Stepp and Ethan Apter
Fundamentals of Programming
Building Java Programs
Arrays versus ArrayList
Recursion 2-Dec-18.
Recursion 2-Dec-18.
Bowling Scoring.
Improving the structure of existing code
Recursion 29-Dec-18.
Stacks.
slides created by Marty Stepp and Hélène Martin
Widely used guidance for writing better code.
Flowcharts and Pseudo Code
Java Programming Language
Fundaments of Game Design
Recursion 23-Apr-19.
Dr. Sampath Jayarathna Cal Poly Pomona
slides created by Ethan Apter
slides adapted from Marty Stepp
Building Java Programs
Lecture 7: Linked List Basics reading: 16.2
Expanding the PinBallGame
CSE 206 Course Review.
CMSC 202 Exceptions.
Welcome to Simple Bowling
Presentation transcript:

Bowling Game Kata In C# with NUnit

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

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.

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

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

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

A quick design session A game has 10 frames.

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

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

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

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

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

The First Test Gutter game

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

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

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

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 { }

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); }

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) }

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

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

The Second Test Single pins

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

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

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()

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

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

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;

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;

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?

The Third Test A single spare

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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)

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()

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()

The Fourth Test Single Strike

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

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

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

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;

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);

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()

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);

The Fifth Test A perfect game

End