Tennis Score Code Kata

cedd burge
RES Software Team
Published in
5 min readOct 5, 2020

This was the second RES Code Kata, where Software Developers, Analysts, Data Scientists and Power Plant Optimisers all get together to practice coding. Everybody was working at home this time, so the whole thing took place on Zoom, using breakout rooms for each pair, and a main room for group discussion. Some teams also used VS Live Share to aid collaboration.

In our day jobs, we are often in a bit of a rush, and tend to do the same things. This makes us good at doing those same things, in the way that we always do them, but does not make us better at different things, or at doing the same things in a different way.

During the Kata we were aiming to do some Deliberate Practice. This involves trying something that you can’t quite do yet, but that isn’t too much of a stretch. It takes more time and effort, but you learn and adapt. The key is to “do less, but with more awareness”, or “rush less and concentrate, in order to maximise learning”, or similar.

So during the Kata we tried to extend our boundaries. We all programmed in pairs and used TDD, which already takes most / all of us out of our comfort zone. The Tennis Score Problem we were solving is quite simple, so some pairs also chose a constraint.

The plan was to learn some things during the Kata, but also to learn the method of deliberate practice, so that we can continously improve when return to our normal lives. We tried to apply the RES Cost Effective Coding Guidelines.

Interesting things

One pair was using the No Naked Primitives constraint, which led them to encapsulate the score as an Enum, instead of an integer or a string or similar. Everyone liked this in the first group discussion, and most solutions pivoted to use it.

One soltuion used an immutable object pattern, which means that in the following code snippet, you can be sure that do_something has not changed a_score, so you don’t need to look at its implementation when debugging / reasoning about the code.

a_score = GameScore(PlayerScore.Thirty, PlayerScore.Zero)do_something(a_score)

One pair initially included PlayerScore.Loses and PlayerScore.Duece in an Enum, but after discussion it became obvious that these weren’t required. “Duece” is just the text representation of an “40:40” score. Instead of “Loses”, it makes more sense to retain useful information and keep the player score. This means thatthe end result will be “Wins:40” or “Wins:15” or similar, instead of “Wins:Loses”.

None of the pairs did any validation of the scores, such as disallowing “Advantage:15”, or disallowing a player to score a point after a game had been won.

There was some discussion about tests, and how they allow us to be sure that changes to the code haven’t broken any existing functionality. Using a build server can also ensures that every commit to master passes all the tests, so that when you download the code you can be confident that it works. A build server is also on another machine, so it forces us to make sure that the code is portable, and can work on other peoples machines (usually via a conda or virtualenv environment). One pair used parameterised tests.

We talked about whether to test the formatted score (for example score == "40:30"), or the individual scores (for example server_score == PlayerScore.Fourty). We decided that you should test the public interface of a class / module, and that the public interface should be as small as possible, while still being useful. So if the formatted score is the only thing that is used, then we should expose and test that. However if the individiual scores are used, then they should be exposed and tested as well. In this case you would probably create a new class to do the formatting.

Some solutions (1, 2)were quite simple, and almost just listed out all the possible combinations of scores alongside what the next score should be. Some of the authors of these solutions were less experienced, and commented that they were less robust than the more complex solutions. However the majority view was that the simple solutions were actually more robust, as the implementation was very obvious, and it was obvious that there weren’t any bugs. There was however some duplication, and generally more code, so as always it is a balancing act, which relates well to our Cost Effective Coding Guidelines.

A lot of solutions “incremented” the enum that represented the score, for example Score(Scores.Fifteen.value + 1) becomes Score.Thirty. This is slightly hacky, but pragmatically useful. It is possible to hide the hackinesss, by adding methods to Score to increment / decrement, as in this stack overflow post.

One solution always incremented values, and then “validated” the new score, turning “Advantage:15” in to “Win:15” and similar, which was an interesting concept (most solutions check first and then increment, but this one increments first and then checks). This pair were using Silent Pair Programming, so maybe the lack of usual methods of communication led to a more novel design. They certainly enjoyed using the constraint, and thought that it saved time and made the code better.

One pair solved the problem fairly quickly, and went on to pivot the solution to use a new, imaginary scoring system that goes up to 50.

An early solution had a server_wins function, that caused some confusion. It wasn’t initially clear whether the function was updating the score after the server had won a point, or whether the server had won the game.

Using instance variables (self.a_variable) allow multiple instances of a class to exist at the same time, with different copies of the data. It also allows the class methods to have less parameters, as they can all access the instance variables.

Python f strings are good!

Solutions

Conclusion

Code Katas are a fun and engaging way to share and gain knowledge across a wide range of backgrounds. They are inexpensive to run and can be run on zoom without major issues. Some valuable learning occurs on the day, and ongoing learning continues after the event with the application of deliberate practice.

--

--