Bowling is a nice coding kata, cause score calculation is a bit tricky. A strike in this throw gives a point advantage for the next two throws for example. So it is not valid to just iterate over the throws of a game to calculate points. Here it is shown how to iterate with a window of 3 throws to have the point calculation logic as concise and readable as possible.
The picture shows the three iterators. The current throw, the last throw and the throw before the last throw. The window helps to iterate all three together to express the scoring algorithm as understandable as possible.
for throw_before_last_throw, last_throw, throw in iter_over_throws(
game.split("|"), window_size=3
):
score += throw.points
if throw_before_last_throw and throw_before_last_throw.strike:
score += throw.points
if last_throw and (last_throw.spare or last_throw.strike):
score += throw.points
As you see, in the code above, it is pretty readable how scoring is done. Taking the normal points and double points if throw before last throw was a strike or if the last throw was a strike/spare.
Testing is done on multiple levels.
First there is unittesting with 100% statement coverage.
To ensure that all aspects of the code are covered with a test, there is also mutation testing in place. Strong mutation testing. This ensures that the functionallity is correct.
To show that these tests cover everything, there is on top of this also a fuzzy testing in place. This shows that all (currently known) 54 branches are covered and that there is no unexpected behaviour.
docker build -t bowling . &&
echo -n '11|11|11|11|11|11|11|11|11|11||' | docker run -i bowling