How To Perform the Bowling Game Kata in C?


Programming Katas are good to prone your skills. A famous Kata is the Bowling Game Kata, often shown as example in Java. But can you do this Kata also in C?

In total is the Bowling Game Kata in Pure C not very different from its counterpart in Java or C++. You just have to consider the fact that C is not an OOP language and therefore the approach with classes will not work.

Bowling Pins
Image fromStockSnap atPixabay

We will see a complete example of the Bowling Kata in Pure C in this article.

The Bowling Game Kata in Pure C

So what does this strange word Kata even mean? Wikipedia says:

Kata is a Japanese word ( or ) meaning “form”. It refers to a detailed choreographed pattern of martial arts movements made to be practised alone.

Source: https://en.wikipedia.org/wiki/Kata, retrieved @21.09.2022

So a Kata for programmers is a practice task (form) that you do repeatedly in order to get better at certain skills like Test Driven Development or in a specific language.

You can find the original Bowling Kata on Uncle Bobs HomepageOpens in a new tab. where you can download it.

A Kata is meant to be memorized. Students of a kata study it as a form, not as a conclusion.

Robert C. Martin aka “Uncle Bob”

The Bowling Kata is about programming a piece of software that correctly calculates the scores of a Bowling Game. Normally the design involves a Game class and member functions of that class. Because we do it in C, there will be no classes and our approach will be slightly different.

How to Perform the Bowling Game Kata in Pure C

In my article about Unit Testing with Google Test in Pure C I already showed the first and second test of this Kata. However, we will not use Google Test but the CppUTest Framework. I described several Unit Test Frameworks for C in another article.

First we setup our CommandLineTestRunner in our main file in order to get our test suite up and running:

#include "../cpputest-3.8/include/CppUTest/CommandLineTestRunner.h"

int main(int argc, char** argv)
{
	return CommandLineTestRunner::RunAllTests(argc, argv);
}

Because we use a C++ Unit Test Framework, our Test-Files have to be cpp-Code. We won’t use much C++ Syntax and our Production Code will be pure C.

The First Test

Before we start, remember: Not compiling equals a failing test. We write only so much test code that will compile, then we write only enough production code that the test will pass.

The first test is to score a simple game with twenty rolls, but without spares or strikes. As we have no classes in C, we want to have an interface function roll in our game header file.

#include "../cpputest-3.8/include/CppUTest/TestHarness.h"

TEST_GROUP(BowlingKataTest)
{
};

TEST(BowlingKataTest, GutterGame)
{
	for (int i = 0; i < 20; i++) {
		game_roll(0);
	}
}

Our program does not compile which equals a failing test. We create the files BowlingKata.h and BowlingKata.c and include the header in our test program. Because we include C-Code into C++, we have to use the extern keyword.

...
extern "C" {
#include "BowlingKata.h"
}
...
#pragma once

void game_roll(const int score);
#include "BowlingKata.h"

void game_roll(const int score) { }

Now the program compiles. We can move forward with our test:

...
TEST(BowlingKataTest, GutterGame)
{
	for (int i = 0; i < 20; i++) {
		game_roll(0);
	}
	CHECK_EQUAL(0, game_score());
}
...

We make this compile with:

...
int game_score();
...
...
int game_score()
{
	return -1;
}
...

Now the Test fails because 0 and -1 are different results. We fix this by setting the return value to the constant 0 and our test passes.

...
int game_score()
{
	return 0;
}
...

The Second Test

No score rolled means no score to be counted. Next up is a slightly better bowler who throws a one on every roll:

...
TEST(BowlingKataTest, AllOnes)
{
	for (int i = 0; i < 20; i++) {
		game_roll(1);
	}
	CHECK_EQUAL(20, game_score());
}
...

This test fails, because score(…) returns 0 every time. We did not really use the function roll until now, so lets make the test pass with its help. We also have to make use of the setup function in our tests and create an initialization function in our production code. In OOP, the class would be constructed automatically, in C we have to take care of the initialization ourselves.

...
TEST_GROUP(BowlingKataTest)
{
	void setup()
	{
		game_initialize();
	}
};
...
...
void game_initialize();
...
#include "BowlingKata.h"

static int _score = 0;

void game_initialize()
{
	_score = 0;
}

void game_roll(const int score)
{
	_score += score;
}

int game_score()
{
	return _score;
}

Now all tests pass and it is time for refactoring. We improve the test code by removing duplication:

...
void rollMany(const int& times, const int& pins)
{
	for (int i = 0; i < times; i++) {
		game_roll(pins);
	}
}

TEST(BowlingKataTest, GutterGame)
{
	rollMany(20, 0);
	CHECK_EQUAL(0, game_score());
}

TEST(BowlingKataTest, AllOnes)
{
	rollMany(20, 1);
	CHECK_EQUAL(20, game_score());
}

The Third Test

The calculation of the Bowling Game score becomes a little bit more difficult when the bowler actually throws a spare. We will provide a test for this, but before we take a look at our production code. Although there was no duplication to remove in the refactoring phase, there are some issues which we have to handle before our next test.

Production Code Refactoring

The function roll(..) calculates the score, but the name does not imply this, where score() implies it but does not calculate. We also have to take into account that a frame consists of two rolls in oder to be able to detect a spare. So we improve the production code whilst making sure all tests pass all the time:

...
void game_roll(const int pins);
...
#include "BowlingKata.h"
#include <string.h>

static int _rolls[21];
static int _currentRoll = 0;

void game_initialize()
{
	_currentRoll = 0;
	memset(_rolls, 0, sizeof(_rolls));
}

void game_roll(const int pins)
{
	_rolls[_currentRoll++] = pins;
}

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

	return score;
}

Throwing a Spare

Now we are ready for test number three. We throw one spare:

...
TEST(BowlingKataTest, OneSpare)
{
	game_roll(5);
	game_roll(5); // spare
	game_roll(3);
	rollMany(17, 0);
	CHECK_EQUAL(16, game_score());
}
...

It fails (as expected) because score() returns 13 instead of 16. We need to cover this special case:

...
int game_score()
{
	int score = 0;
	int i = 0;
	int frame = 0;
	for (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];
			i += 2;
		}
	}

	return score;
}
...

Refactoring again

The code works now but the test and the production code have ugly comments in the conditionals for the spare. Also we have a generic i variable which deserves a better name.

...
int _isSpare(const int frameIndex);
...
int game_score()
{
	int score = 0;
	int frameIndex = 0;
	int frame = 0;
	for (frame = 0; frame < 10; frame++) {
		if (_isSpare(frameIndex)) {
			score += 10 + _rolls[frameIndex + 2];
			frameIndex += 2;
		}
		else {
			score += _rolls[frameIndex] + _rolls[frameIndex + 1];
			frameIndex += 2;
		}
	}

	return score;
}

int _isSpare(const int frameIndex)
{
	return _rolls[frameIndex] +
		_rolls[frameIndex + 1] == 10;
}
...
void rollSpare() 
{
	game_roll(5);
	game_roll(5);
}
...
TEST(BowlingKataTest, OneSpare)
{
	rollSpare();
	game_roll(3);
	rollMany(17, 0);
	CHECK_EQUAL(16, game_score());
}

The Fourth Test

A spare is good, but a strike is better. We write our test to get the correct score, but instead of the expected 24 we will only get 17 from the game_score() function as a result.

TEST(BowlingKataTest, OneStrike) {
	game_roll(10);	// strike
	game_roll(3);
	game_roll(4);
	rollMany(16, 0);
	CHECK_EQUAL(24, game_score());
}

The goal is to get the correct result as quickly as possible, ensuring the code quality is the task of the refactoring stage.

...
int game_score()
{
	int score = 0;
	int frameIndex = 0;
	int frame = 0;
	for (frame = 0; frame < 10; frame++) {
		if (_rolls[frameIndex] == 10) {	// strike
			score += 10 + 
					 _rolls[frameIndex + 1] + 
					 _rolls[frameIndex + 2];
			frameIndex++;
		}
		else if (_isSpare(frameIndex)) {
			score += 10 + _rolls[frameIndex + 2];
			frameIndex += 2;
		}
		else {
			score += _rolls[frameIndex] + _rolls[frameIndex + 1];
			frameIndex += 2;
		}
	}

	return score;
}
...

Again, this is ugly, but we will refactor right now, so don’t worry.

Strike Refactoring

...
int _isStrike(const int frameIndex);
int _sumOfBallsInFrame(int frameIndex);
int _strikeBonus(const int frameIndex);
int _isSpare(const int frameIndex);
int _spareBonus(const int frameIndex);
...
int game_score()
{
	int score = 0;
	int frameIndex = 0;
	int frame = 0;
	for (frame = 0; frame < 10; frame++) {
		if (_isStrike(frameIndex)) {
			score += 10 + _strikeBonus(frameIndex);
			frameIndex++;
		}
		else if (_isSpare(frameIndex)) {
			score += 10 + _spareBonus(frameIndex);
			frameIndex += 2;
		}
		else {
			score += _sumOfBallsInFrame(frameIndex);
			frameIndex += 2;
		}
	}

	return score;
}

int _isStrike(const int frameIndex)
{
	return _rolls[frameIndex] == 10;
}

int _sumOfBallsInFrame(int frameIndex)
{
	return _rolls[frameIndex] + _rolls[frameIndex + 1];
}

int _strikeBonus(const int frameIndex)
{
	return _rolls[frameIndex + 1] + _rolls[frameIndex + 2];
}

int _isSpare(const int frameIndex)
{
	return _rolls[frameIndex] +
		_rolls[frameIndex + 1] == 10;
}

int _spareBonus(const int frameIndex)
{
	return _rolls[frameIndex + 2];
}

Of course, we have to take care of our test code, too:

...
void rollStrike()
{
	game_roll(10);
}
...
TEST(BowlingKataTest, OneStrike) {
	rollStrike();
	game_roll(3);
	game_roll(4);
	rollMany(16, 0);
	CHECK_EQUAL(24, game_score());
}

The Fifth Test

The last test in this Kata tests a perfect game, wich means that you roll a strike twelve times in a row. We write the test as follows:

...
TEST(BowlingKataTest, PerfectGame) {
	rollMany(12, 10);
	CHECK_EQUAL(300, game_score());
}

And now something strange happens: The test passes without changing the production code. This can happen and can be a sign that you test too much, or, like in our case, that the implementation is complete.

The Final Code in C

Here are all three files with the entire code of this example.

#include "../cpputest-3.8/include/CppUTest/TestHarness.h"

extern "C" {
#include "BowlingKata.h"
}

TEST_GROUP(BowlingKataTest)
{
	void setup()
	{
		game_initialize();
	}
};

void rollMany(const int& times, const int& pins)
{
	for (int i = 0; i < times; i++) {
		game_roll(pins);
	}
}

void rollSpare() 
{
	game_roll(5);
	game_roll(5);
}

void rollStrike()
{
	game_roll(10);
}

TEST(BowlingKataTest, GutterGame)
{
	rollMany(20, 0);
	CHECK_EQUAL(0, game_score());
}

TEST(BowlingKataTest, AllOnes)
{
	rollMany(20, 1);
	CHECK_EQUAL(20, game_score());
}

TEST(BowlingKataTest, OneSpare)
{
	rollSpare();
	game_roll(3);
	rollMany(17, 0);
	CHECK_EQUAL(16, game_score());
}

TEST(BowlingKataTest, OneStrike) {
	rollStrike();
	game_roll(3);
	game_roll(4);
	rollMany(16, 0);
	CHECK_EQUAL(24, game_score());
}

TEST(BowlingKataTest, PerfectGame) {
	rollMany(12, 10);
	CHECK_EQUAL(300, game_score());
}
#pragma once

void game_initialize();
void game_roll(const int pins);
int  game_score();
#include "BowlingKata.h"
#include <string.h>

static int _rolls[21];
static int _currentRoll = 0;

int _isStrike(const int frameIndex);
int _sumOfBallsInFrame(int frameIndex);
int _strikeBonus(const int frameIndex);
int _isSpare(const int frameIndex);
int _spareBonus(const int frameIndex);

void game_initialize()
{
	_currentRoll = 0;
	memset(_rolls, 0, sizeof(_rolls));
}

void game_roll(const int pins)
{
	_rolls[_currentRoll++] = pins;
}

int game_score()
{
	int score = 0;
	int frameIndex = 0;
	int frame = 0;
	for (frame = 0; frame < 10; frame++) {
		if (_isStrike(frameIndex)) {
			score += 10 + _strikeBonus(frameIndex);
			frameIndex++;
		}
		else if (_isSpare(frameIndex)) {
			score += 10 + _spareBonus(frameIndex);
			frameIndex += 2;
		}
		else {
			score += _sumOfBallsInFrame(frameIndex);
			frameIndex += 2;
		}
	}

	return score;
}

int _isStrike(const int frameIndex)
{
	return _rolls[frameIndex] == 10;
}

int _sumOfBallsInFrame(int frameIndex)
{
	return _rolls[frameIndex] + _rolls[frameIndex + 1];
}

int _strikeBonus(const int frameIndex)
{
	return _rolls[frameIndex + 1] + _rolls[frameIndex + 2];
}

int _isSpare(const int frameIndex)
{
	return _rolls[frameIndex] +
		_rolls[frameIndex + 1] == 10;
}

int _spareBonus(const int frameIndex)
{
	return _rolls[frameIndex + 2];
}

And that’s it, the Bowling Kata in C is done. Now try it out for yourself, either with CppUTest or with another Unit Testing Framework of your choice.

Marco Lieblang

Professional Programmer since 2003, passionate Programmer since the mid 90's. Developing in many languages from C/C++ to Java, C#, Python and some more. And I also may know a bit about Assembly Languages and Retro Systems.

Recent Posts