wp-content/uploads/2016/01/PBS_Logo.png

This instalment will be the last before we go on hiatus for a few weeks while Allison goes off exploring Europe. When Allison comes back we’ll be changing gears and switching for focusing on JavaScript to focusing on HTML and CSS. We’ll learn about the free and open source Bootstrap 4 CSS library. This library provides many useful features, but we’ll start simple. Firstly, the library providers modern and elegant default styles for all the HTML elements we already know and love. It also provides a handful of simple CSS classes for defining page layouts (columns, rows, that kind of thing), and thirdly, it provides simple CSS classes for creating so-called responsive web pages, i.e. pages who’s layout changes automatically depending on screen size.

As this is the last instalment before the hiatus we’ll use it to wrap up our work on the Cellular Automata prototypes. We’ll start with a sample solution to the challenge from the previous instalment, and finish with a worked example where we use our prototypes to create three distinct CAs.

The final code for the worked example is included in this instalment’s ZIP file which you can download here.

Since episode 50 was a special episode, it’s been a while since we last looked at these prototypes, so let’s take a moment for a quick summary of what we’re trying to achieve.

Conceptually, a Cellular Automaton (CA) is a grid of cells, each of which is on one of a finite set of states. CAs move from a current state to a next state in lock-step, that is to say, all the cells change from their current state to their next state in one step. Each CA defines its own set of rules for how the next state of each cell should be calculated. This set of rules has only two inputs — the current state of the cell itself, and the current state of all neighbouring cells. Practically, we need a way of seeing our automaton, so each CA also needs to define a set of rules for how to display a given state.

An example of a specific cellular automaton is Conway’s Game of Life. In this specific example there are only two possible states for each cell — alive & dead, and the set of rules for calculating the next state of each cell are:

  1. Any live cell with fewer than two live neighbours dies
  2. Any live cell with two or three live neighbours lives on
  3. Any live cell with more than three live neighbours dies
  4. Any dead cell with exactly three live neighbours becomes a live cell

We’ve modelled the abstract concept of Cellular Automata with three prototypes — one to represent a CA as a whole (bartificer.ca.Automaton), one to represent a single cell (bartificer.ca.Cell), and one to represent a cell state (bartificer.ca.State). Every Automaton contains a grid of Cells, and every Cell has a current State.

Within the Automaton prototype we store the set of rules for calculating the next state of each cell as a reference to a function, and we refer to it as the step function.

We also store the set of rules for displaying a state as a reference to a function, and we refer to that as the render function.

As things stand at the start of the challenge, we do not store the set of allowed states at all, and since we don’t even store it, we definitely can’t enforce it. That’s the problem the challenge asked you to solve.

PBS 49 Challenge Sample Solution

You’ll find the full source code for my sample solution as the named release PBS49-Challenge-Solution on GitHub.

Part 1 — Add a .equals() Function to bartificer.ca.State

We get started with a quick and easy little function. Since this is an instance function, it will be invoked on an instance of the class bartificer.ca.State. The function will take one argument, and should compare the instance it was called on (this) to that one argument. If the passed value is a bartificer.ca.State, and, has the same value and label as the instance itself, it should return true, otherwise, it should return false:

While I didn’t explicitly request it in the assignment, I also created a test for this new function:

Part 2 — Re-factor the bartificer.ca.Automaton constructor

The first step in refactoring the constructor is to convert the renderFn argument from required to optional by adding a default render function that renders truthy states as green, and falsey states as red.

This is simply a matter of changing this section of the constructor:

To this:

Now we’re ready to collapse all the optional arguments into a single object. This involves a lot of changes to the constructor, so I’ve included the complete constructor below with the modified regions marked:

Changing how the constructor works also required much of the test suite to be re-written, not just the tests for the constructor itself, but all calls to the constructor in all tests. The changes are too extensive to include in the show notes, but they are all committed to GitHub.

Finally, with the constructor refactored, we now need to update the call to the constructor in sample.html:

Part 3 — Add a List of Supported States as an Instance Property to bartificer.ca.Automaton

The idea here to add the ability of a cellular automata to know what states are and are not valid within its universe.

The first step is to update the constructor so it performs the following two tasks:

  1. Stores a set of states in a private instance variable named ._cellStates. These states can come from the user via the cellStates key in the opts argument, or, a default set of Alive and Dead can be used.
  2. Builds a matching looking table named ._statesByValue.

Below is my updated constructor with the changes highlighted:

Next we need a simple read-only accessor for ._cellStates. This is pretty much just like all the others with the small exception that it returns a fresh array rather than a reference to the original. This is to prevent spooky action at a distance. If we returned a reference to the internal array the user could inadvertently alter it and cause very weird and difficult to track down bugs.

We can now add the special accessor .stateFromValue():

Finally, we can add the .hasState() utility function:

Note that while I’m not including the code here, the GitHub release also contains an updated version of the test suite with updated tests for the constructor, and new tests for the newly added functions.

Part 4 — Improve .step() in bartificer.ca.Automaton

At this stage our automaton can store a set of allowed states, but it doesn’t in any way enforce them. Our step function is literally anarchy, it will accept any value what so ever returned by the instance’s user-supplied step function:

This might seem forgiving, but it’s not, because the .nextState() function rigidly enforces discipline:

What we need if for our .step() function to be as helpful as possible and pass what the user meant to .nextStep() rather than the exact value they returned. In CS jargon, our .step() function should coerce the value returned by the user’s step function into a bartificer.ca.State object if possible.

Let’s illustrate this point with a hypothetical example. Imagine the user of our API has created a CA and specified that it supports following two states:

  1. Alive (true)
  2. Dead (false)

If the user’s step function true or false, then there is no ambiguity, so our .step() function should be able to translate those primitive values into their matching bartificer.ca.State objects, and pass those objects on to .nextState() rather than the original primitive value.

Because we’ve already added the .stateFromValue() function, there’s not actually much more we need to do:

Again, I updated the test suite for the .step() function so it checks that coercions are being applied, and you can find that code in the git release.

A Final Example — Multiple Different CAs

While we’ve focused on Conway’s Game of Life, that is not the only CA in town. Firstly, there are a myriad of simple variations of the Game of Life where you keep the concept of two states, but change the number of neighbours needed to be born or to die, together, all these rule sets are are known the Life class of CAs, you can see man of them described here. We’ll implement one of these, the so-called Maze Rule.

But of course, there’s no need to limit yourself to just two states! To prove that point we’ll implement the best known of the three-state rules, Brian’s Brain.

Let’s start with a basic HTML 5 page that loads jQuery and our bartificer.ca prototypes, includes some very basic CSS for styling our automata (copied directly from sample.html), and creates placeholders for our three CAs:

Let’s start on familiar territory and create the Game of Life CA. To do that we’ll need three things:

  1. A set of allowed states (alive & dead)
  2. A render function that can render aliveness and deadness
  3. A step function that implements the rules of the game of life
  4. A function to randomly return alive or dead to initialise the grid with

Let’s start with the set of allowed states:

Next, let’s define a simple rendering function:

Next, let’s write the step function for the Game of Life:

Finally, the easy part, a function to generate a random boolean:

We’re now ready to create the Game of Life CA and set it running. Because the constructor interacts with the DOM (inserts a table) we have to do this inside the jQuery document ready handler:

At this stage our sample page loads the game of life and starts it running. Nothing really new so far. Now, let’s implement the Maze!

The Maze uses the same set of allowed states, so we don’t need to define a new set of states, or create a new rendering function, or create a new function for generating random states, we can re-use those we already created. All we need to do is create a new step function that implements the rules for the Maze:

  1. If alive, must have between 1 and 5 live neighbours to stay alive, otherwise, die
  2. If dead, must have exactly 3 live neighbours to come to life, otherwise, stay dead

We can code this up as:

We can then create our second CA similarly to the first:

Now let’s really shake things up with Brian’s Brain.

Brian’s Brain doesn’t have live and dead cells, instead, each cell is imagined to be a neurone in Brian’s Brain, so it’s in on of three states:

  1. Ready to Fire
  2. Firing
  3. Recharging

Given those three states the following rules apply:

  1. All cells cycle from ready, to firing, to recharging and back to ready, no other transitions are possible
  2. A cell only fires when exactly two of its neighbours are firing
  3. When a cell fires it stays in that state for exactly one step
  4. When a cell is re-charging it also stays in that state for exactly one step

So let’s translate that into code. First, the set of states:

Now, the set of rules (i.e. the step function):

Now we need to get practical — our existing render function can only deal with two states, so we need to write another one for dealing with three states:

And now we need a function to return a random brain state:

And now, we’re ready to add our final CA:

And that’s all there is to it!

I’ve included the entire file as pbs51.html in this instalment’s ZIP file.

A Challenge

Given that we’ve just wrapped up one chapter, and will be starting something completely fresh next time, there isn’t really an obvious challenge to set. But, if you would like to practice your coding skills while we’re on hiatus, I suggest you set yourself the same challenge I set myself for instalment 50.

Final Thoughts

At this stage we’ve come a very long way indeed. We’ve learned how to define the structure of a web page with HTML, how to alter the presentation of a page with CSS, and how bring that page to life with JavaScript. We’ve learned how to use jQuery to interact with the DOM, and how to create our own classes. We’ve now put all that together to create an API for building Cellular Automata. We’ve not just created a web app, we’ve created an API that enables others to create web apps of their own which can display any 2D cellular automaton they care to dream up!

When we return we’ll switch our focus away from JavaScript and back onto HTML and CSS with an introduction to the popular and powerful open source CSS library Bootstrap 4.