Fizzbuzz in Statecharts

Fizzbuzz in programming is a program that writes out the numbers 1 through 100 swapping out multiples of 3 and 5 with the words Fizz and Buzz respectively, like so:

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11…

For numbers that are multiples of both 3 and 5, you write out FizzBuzz. Imperative solutions are easy to come by, but here I’d like to explore using statecharts to solve this problem, showing different mechanisms and tradeoffs.

Here’s some code that only caters for the first two rules, and doesn’t handle the case wher the number is divisible by both 3 and 5:

// Before adding divisible by 3 AND divisible by 5 => fizzbuzz
For i = 1; i < 100; i++ {
  If i%3 == 0, print fizz
  Else If i%5 == 0, print buzz
  Else print digit
}

There are different ways of adding the “new” constraint of being divisible by both 3 and 5:

Example 1: Simply check it first

// After (example 1)
For i = 1; i < 100; i++ {
  If i%3 && i%5 == 0, print fizzbuzz
  Else If i%3 == 0, print fizz
  Else If i%5 == 0, print buzz
  Else print digit
}

Example 2: Build up a string using concatination, and print out the result:

// After (example 2)
For i = 1; i < 100; i++ {
  result = ''
  If i%3 == 0, result += 'fizz'
  If i%5 == 0, result += 'buzz'
  If result is not empty, print result
  Else print digit
}

Example 3: Split the i%3 branch in two, by placing an extra test inside that if-test to check if i%5 also matches.

// After (example 3)
For i = 1; i < 100; i++ {
  If i%3 == 0
    If i%5==0, print fizzbuzz
    Else print fizz
  Else If i%5 == 0, print buzz
  Else print digit
}

The tradeoffs we’ll be exploring are:

In each of these, consider

Look at how the code evolved when adding the constraint “for numbers divisible by both 3 and 5, print fizzbuzz”. This tells us about the maintainability of the original code. The maintainability of the resulting code can be explored by doing thought experiments such as “How would I add more constraints to the code, or change the existing constraints?”. For example, divisible by seven should print out Woof and divisible by three, five and seven should print out Woofizzbuzz”

Event driven

However, in order to do anything with statecharts, we have to relate to the fact that statecharts are by nature event-driven: They react only to events. So the first thing we need to do is make the FizzBuzz problem event driven, by defining an event that the state machine needs to react to:

actions.print_digit = (i) => console.log(i);
actions.print_fizz = () => console.log('fizz');
actions.print_buzz = () => console.log('buzz');
actions.print_fizzbuzz = () => console.log('fizzbuzz');

// After event-driving it
For i = 1; i < 100; i++ {
  state = machine.transition(state, 'increment', i)
  For each state.actions
    Perform the action
}

Actions are things that happen instantaneously. The statechart will tell us what to do, and we will blindly do what it tells us to do.

We can also model the printing of some text to be a long running process, perhaps setting the text of a <div> (and subsequently removing the text when the activity is supposed to stop) the text.

// alternative, using activities
For i = 1; i < 100; i++ {
  state = machine.transition(state, 'increment', i)
  // state.activities tells us what activities should be happening right now
}

Every time something happens in our loop (i++) we send the ‘increment’ event to the state machine, and do whatever the state machine tells us to do. Now that the problem has been framed in an event driven manner, we can start to discuss how to design a statechart that has these properties.