Bowling game
The refactoring phase
In the previous kata, in general, the TDD cycles we’re execute in quite a fluent manner.
However, you may have noticed that sometimes, making a new test pass involved doing a certain refactoring in the production code before we were able to face the necessary changes to make the test pass.
The kata that we’re about to practice, apart from being one of the classic ones, has a peculiarity: almost every new piece of functionality that we add, every new test, requires a relatively large refactoring of the algorithm. This creates a dilemma: we can’t refactor if the test is not green.
Or, put in another way: sometimes we’ll run into a situation where a new test provides us with some new information that we didn’t have before, and shows us a refactoring opportunity that we have to take before implementing the new piece of functionality.
For this reason, with the Bowling Game kata we’ll learn how to handle this situation and take a step back to refactor the production code using what we learn when we think about the new test.
In a sense, the information from the future will help us change the past.
History
The Bowling kata is very well known. We owe it to Robert C. Martin, although there’s a very popular version by Ron Jeffries in the book Adventures in C#.
Problem statement
The kata consists in creating a program to calculate the scores of a Bowling game, although to avoid complicating it too much, only the final result is calculated without performing any validations.
If you’re not familiar with the game and its scoring system, here are the rules that you need to know:
- In each game, the player has 10 turns called frames.
- In each frame, the player has two tries, or rolls, to knock down the 10 pins (which results in a total of 20 ball rolls throughout the whole game).
- In each roll, the knocked down pins are counted.
- If no pin was knocked down, that’s a Gutter.
- If the player hasn’t knocked all of the bowling pins by the end of their second roll, the score of the frame is just the sum of both rolls. For example 3 + 5 = 8 points in the frame.
- If the player knocks down all 10 pins in the frame (for example 4 + 6), that’s called spare, and grants a bonus equal to the score of the next roll, the first one of the next frame (10 from the current frame, plus 3 from the next throw, for example, equalling 13). That is, the final score of a spare is calculated after the following roll, and in a sense, that roll is counted twice (once as a bonus, and a second time as a regular roll).
- If the player knocks down all 10 pins in a single roll, that’s a strike. In that case, the bonus is equal to the score of the whole next frame (for example, 10 + (3 + 4) = 17). After a strike, the frame ends without a second roll.
- In the case that this happens in the tenth and last frame, there may be one or two extra rolls as necessary.
Hints to solve it
The Bowling Game is an interesting kata because of the challenge of handling the spares and the strikes. When we detect one of these cases we have to look up the result of the following rolls, and therefore we need to keep track of the history of the match.
This will force us to change the algorithm several times in quite a radical way, which leads us to the problem of how to manage the changes without breaking the TDD cycles, that is, refactoring the production code while keeping the tests green.
To better understand what we’re talking about, a situation in which we might find ourselves would be the following:
After a couple of cycles, we start testing the spare case. At this point, we realize that we need to make a relatively large change to the way that we were calculating the total score. Ultimately, what happens is that we have to refactor at while having a test that’s not passing. But, this contradicts the refactoring phase definition, which requires all tests to be passing.
The solution, fortunately, is very simple: take a step back.
Once we know that we want to refactor the algorithm, it’s enough to comment out the new test to deactivate, and keeping the last test green, refactor the production code. When we’re done, we bring the new test back to life, and develop the new behavior.