Algemeen Improve Nieuws Scala testing7 juli 2017

Kickoff coding Guild

As a Tech company it is important to keep updating our knowledge. Therefore we organize a Coding Guild every month. In this Coding Guild we share knowledge and experiment on how we should write Software. Monday June 26 we took up a programming challenge or kata. The challenge was to implement the bowling score (highly inspired by Uncle Bob: http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata). We created different groups that would take their language of choice to implement a solution. It was completely free format and very interesting to see how the different teams approached the problem and what solution they came up with. In this blog we share our experience.

There were 5 groups that took up the challenge to create an implementation for the bowling game. The groups were allowed to spend 2 hours (1 hour before diner and 1 hour after) to create a solution. At the end, the groups presented their solution and approach to the group so that everybody could learn from each other. Because Trivento is a Scala company, most groups used Scala to create and implementation. However, there was 1 group to use JavaScript and 1 group to use Java. Below you’ll read how each group approached the problem and came up with their solution.

Scala Group 1

This group of 4 started by thoroughly reading the rules of bowling to understand how the scoring in bowling works. When everyone in the group was up to speed, they started by implementing a random bowling game generator. This generator would generate 10 random frames (a bowling game consists of 10 frames with 2 rolls). The randomness is controlled by a seed, so with the same seed, you will get the same randomly generated bowling game. This allows for deterministic output which makes it easier to test the solution. To be able to create a game, the team decided to implement the concepts of bowling in an ADT (Algebraic Data Type: https://en.wikipedia.org/wiki/Algebraic_data_type). The ADT consists of types for every kind of frame (think Spare, Strike, Roll, LastFrame). By using an ADT the code becomes more expressive and easier to reason about. Being Scala developers we love strong types, so that the compiler will help during development. To calculate the scores the team used a sliding (https://www.scala-lang.org/api/current/scala/collection/IterableLike.html#sliding(size:Int,step:Int):Iterator[Repr]) window to iterate over the List[Frame] to be able to look at the next 2 frames to calculate the score of a frame. The problem they ran into was the fact that sliding will not make sure that you will receive every element in first position. Therefore they needed to make up for this by adding extra slices to the Iterator[List[Frame]].

val beurten: IndexedSeq[FrameScore]

val slidingWindow = beurten.sliding(3).toList ++ 
                          Seq(beurten.takeRight(2), beurten.takeRight(1))

This team tested their solution by doing some exploratory testing with different inputs. The result is printed to console and visually inspected. The group used scanleft to see all the intermediate result of the total sum of all frames. It is possible to see the total score after each frame. Just like a real bowling score.

Scala Group 2

This group of 4 also started by thoroughly reading the rules of bowling to understand how the scoring rules work. This group decided to tackle the problem using TDD. So this meant writing a very simple first case. The simplest case being that a new Game has a score of 0.

new Game().score shouldEqual 0

TDD dictates that every added functionality in the production code should be preceded with a failing test case. By following this rules, the team discovered the best API via the tests instead of inventing the API. This led to many refactoring to the API. This team also came up with an ADT for the data structure and discovered the use of the same sliding functionality of team 1. However, this team made a different choice in adding the missing frames from the iteration and during the presentation this team learned from team 1 how to add missing windows to the sliding list, before starting to interpret the windows.

Using TDD forced this team to work very focussed on small increments. The team did not implement all functionality, but because the test coverage is really high, it would be fairly easy to hand over to another team to finish, or finish at another time.

Scala Group 3

This was the last Scala team of 3 members and their approach was different from all other teams. This team started with an extensive 45 minutes discussion about the problem and how it could be solved. They thought about solving this with Shapeless, Scalaz/Cats in an attempt to make their solution so that all checks could be done at compile time. This would mean that an attempt to roll a frame with more than 11 pins would not compile, as opposed to a runtime error. In the end they decided for runtime checks, because it should be possible to use their solution with a REST front end.

In the code the team also used an ADT, but a lot smaller ADT than the other teams. This team decided not to model Spares and Strikes in their ADT, but only differentiate between a Frame and the 10th Frame. The 10th frame is different from the other frames, because it can potentially hold 3 rolls. They also used Scalaz Validation to validate all input, instead of throwing exceptions. Validation makes a more pure functional interface. This team also decided that they didn’t want to iterate over the list to calculate the scores, but rather calculate the scores on the fly and adding it to the data structure and thereby having an O(1) to get the score. The problem didn’t need this optimisation, but the goal is to learn and therefore they decided to investigate how to do this.

This team was also not able to create a complete solution. The long discussion in the beginning took away a lot of the coding time, but gave them a very good idea how they wanted to solve the problem.

Java Group

This group of 4 decided to try to create a solution using Java to see how it would stack up against Scala. After understanding the bowling rules they had a discussion how they should model the bowling game. It resulted in an elegant solution where a game consisted of Frames where each Frame would point to the next Frame. Essentially creating a linked list. This solution didn’t have an “extensive” ADT, but just the frames. Every Frame has 2 rolls of type int and a third roll of type Integer. The third roll is optional (and only for frame 10) and was therefore Integer, so it could be null to indicate that it was missing.

The team was quite surprised that the Java solution didn’t have more code than the Scala solutions. There were different views on the imperative structure of the code. The scoring method consisted of a lot of nested if-statements to match all cases. Something the team wanted to improve on if there was more time, because all these if-statements make the code less readable.

public int score() {
    int score;
    if (isStrike()) {
      if (frameNumber != 10) {
        if (nextFrame.isStrike()) {
          score = firstRoll + nextFrame.getFirstRoll() + 
                  nextFrame.getNextFrame().getFirstRoll();
        } else {
          score = firstRoll + nextFrame.getTotalPins();
        }
      } else {
        score = getTotalPins() + extraRoll;
      }
    } else if (isSpare()) {
      if (frameNumber != 10) {
        score = getTotalPins() + nextFrame.getFirstRoll();
      } else {
        score = getTotalPins() + extraRoll;
      }
    } else {
      score = getTotalPins();
    }
    return score;
  }

The solution of this team was also based on random generated games, printing out the scorecard to console and then visually inspecting the results.

One of the developers of this team decided to implement another approach by himself. In this approach he dropped the linked list structure of the frames and decided to store every roll in an array and writing a method to iterate over the array to build up the frames on the fly and thereafter calculating the scores.

JavaScript Group

The last group of 2 decided to try to solve the bowling problem with JavaScript. This was the only group with a proper GUI to input rolls. They spent most of the time implementing all kinds of input validations to make a good UI (using Angular).

A big difference with Java and Scala code compared to JavaScript is that the JavaScript team needed to validate whether the input was an integer. This is due to the dynamic types of JavaScript. But they came up with quite subtle validations as well, especially on the 10th frame: if it starts with a strike (all of the pins have been knocked down with the first ball), it allows for 2 more balls. Basically it can be considered to be a new frame (the sum of the 2 balls is 10 or less), unless the second ball knocks all pins down as well. In that case all pins are restored and the last ball can knock 10 pins down again.

let valideer = function (worp, frame, frameNumber, worpNumber) {
   if (worp.score < 0) {
       $scope.errorMessage = 'Worp moet groter of gelijk zijn aan 0.';
   } else if (worp.score > 10) {
       $scope.errorMessage = 'Worp moet kleiner of gelijk zijn aan 10.';
   } else if (frame.worpen[0].score + frame.worpen[1].score > 10 &&
       ((frameNumber < 9 && worpNumber == 1) ||
       frameNumber == 9 && frame.worpen[0].score < 10)) {
       $scope.errorMessage = 'Worp 1 plus worp 2 mag niet groter dan 10 zijn.';
   } else if (frameNumber == 9 && frame.worpen[0].score == 10 &&
      frame.worpen[1].score + frame.worpen[2].score > 10 && frame.worpen[1].score != 10) {
       $scope.errorMessage = 'In laatste frame mag na een strike in de eerste beurt zonder strike in de 2e beurt niet meer dan 10 kegels omgebowled worden.'
   } else if (worp.score !== parseInt(worp.score)) {
       $scope.errorMessage = 'Worp moet een geheel getal zijn.';
   } else {
       $scope.errorMessage = '';
   }
};

This team also didn’t write any test. One of the reasons was because they were not familiar with any test framework to use it for this exercise.

Conclusion

It was really fun to try to come up with a solution for the bowling problem in small groups. Within the groups people learned from each other during the discussions and implementation. Each team had their own approach to solve the puzzle and by presenting their approach and solution the groups could learn from each other. One of the things that stood out was that most groups didn’t write tests and a lot of developers in the room wanted to take as a lesson learned, that writing tests helps in structuring your thoughts and making robust maintainable code.