A story of repeating patterns of behaviour.
The Game of Life by the mathematician John Horton Conway in 1970 is a zero-player game based on replicating cellular autonoma. this post details three different versions I made and the reason why for each.
The Game of Life presents an N sized grid where each cell has two possible states, alive or empty (dead). With each game tick, the cell can change state or remain as it is based on the number of cells around it. A cell with too many neighbours will die of overpopulation, a cell with too few will die without ‘civilisation’ to sustain it. Cells with the right amount of neighbours will come alive and so on.
V1 – React.js
V2 – React-Redux
V3 – Three.js
In this way, the Game of Life, a Turing Complete model in which each generation is a direct result of the previous state, is the perfect challenge for a state-building exercise.
My first version took a number of weeks, built in React, it helped me learn fundamental algorithm concepts and state-management techniques which would come in useful later.
I revisited this project again years later, reproducing it in a couple of hours, determined to make a cleaner, more robust rendition. At the time I was working with WebGL and three.js, so went back one more time to create a 3D game inspired by the 2D Game of Life.
I’m going to take a ‘poking fun’ approach to talking about these, I believe a little bit of mocking you’re old work is good for illustrating how much has changed since then, and appreciating the quirks of the learning process.
Remember kidos, in learning, there’s no such thing as “bad code”, just code that hasn’t seen growth yet.
Version One: October 2017
This original game, submitted as part of the freeCodeCamp challenge, came straight after the Recipe List and came with a steep learning curve for me.
I knew that I needed a table-like layout for rows and columns, I was familiar with using array’s and
.map() but was yet to learn that you could make arrays of JSX and just… dump them in where you need them. I thought there was something magical about an inline
array.map. 🤷♀️ This lead to some interesting patterns such as creating an N-sized row or column with and N-sized array in state.
<Table /> acted as the game renderer and controller, a pattern which would stick in future projects. It’s state held a ticker which would advance by one with each generation. It rendered a table by looking over the
arr state item above twice (you were locked into square layouts) and rendered
<Cell /> components.
Following the idea that “components should be self contained”, each <Cell /> had it’s own internal life state (nice)… and then on mount, writes this state to a ‘global’ object (i.e. outside the react tree) (not nice) with the following format string as a key: ‘”‘ + “col-” + this.props.colId + “-row-” + this.props.rowId + ‘”‘. 🤮 Don’t ask me why the quote marks are in double brackets because I cant remember.
Cells each had a method to look at adjacent cells in this global object and calculate their next state, which was hard coded because ofcourse.
The last anti pattern was that this function was called when the lifecycle method
componentWillReceiveProps was called, i.e. when the ‘tick’ being passed down from the board went up.
Now this idea to tick a game forward in itself isn’t too bad, though it should have used
componentShouldUpdate and checked that the new tick was changed. The issue here was that every cell, updating itself, was reading and writing from the same object. The cell does not know how much of the board is from the previous generation and how much form the last. Also, the fact that the
globalObject existed outside the react app meant that it existed outside of the application lifecycle structure which could exacerbate this issue.
Remarkably though, it works most of the time.
Somehow, unless my machine is running slower than usuual, the cell updates sync in such a way that the game behaves as it should. the submission was accepted but this always bugged me, it haunted me, it’s remaking was inevitable.
A big thing I took away from this project that I hope could benifit others who may read this is that this potential cell de-sync issue was not enough to wreck the project. The code is duck-taped together in so many ways, its highly innefficient and jumbled but it works.
Often times I see people who achieve things and don’t allow themselves credit becuase they believe they somehow cheated or missed the point of the project. Its a part of imposter syndrome; they think that there is some sort of universally accepted correct structure that they were supposed to find but didn’t and are soon to be revealed as a fraud.
In my view this is how we get into innane discources such as “is HTML a programing language” or “this site design is invalid becuse you used a CSS framework”. In industry, clients generally don’t care whats gone on behind the scenes, they just care about the specification and the end result. To quote my favourite TV show, if you are asked to make somthing which behaves in a certain way then…
Coding isn’t the thing, its the thing that gets us to the thing.
Version Two: April 2019
It took longer than I thought for the urge to remake the game to finally take hold (other stuff took precedent).
This new game was built with create-react-app and included Redux. This time, the store had a two main keys
board which contained data relating to the board display and
control which controlled UI elements (in the end this was only used for the ‘paint’ mode, allowing users to write to the board).
The board was a two dimensional array with cell objects inside. Each cell object stored its x-y position and an ‘alive’ property. The reducer for this section only had two actions, one to manually change the state of a cell (for painting) and one which would loop over the whole board, calculate the new state for each cell, then write this new state to an entrely new board array, thus achieving feed-forward imutability.
<Board /> class had two properties, one to generate rows by calling the next which would generate cells.
<Cell /> classes still performed the check individually to see how they should render and would listen for mouse events to paint the board.
And that’s it, cells update when they need to, React takes care of that, the controller component dispatches to tick the game forward, and the files are simple and light
apart from the enormous bundle size of create react app.
There were still questions as to whether performing the board loop calculation within the reducer violated Redux’s pure function policy; that reducers should be pure functions and do little work. Despite this, the function would produce the same outcome every time and has no side affects so it is still pure in that sense.
Regardless, this was the remake that I had wanted to do since forever, and it only took a couple of hours, as opposed to approximately a week for the previous.
But I wasn’t finished.
Version Three: May 2019
Around this time I had been investigating WebGL and three.js for a potential project with Matter of Stuff, where I was an intern for my Diploma in Professional Studies (DPS).
One of my little experiments involved creating a ‘voxel builder’, a simple grid space where you can place blocks in 3D.
I took the board state, extended it to a three dimensional array and wrote a function to map to the 3D grid space. I extracted the looping function and created a simple adaptation to run outside of React-Redux (which this project does not use). Lastly, I modified the rule set to return an alive cell if the number of neighbours was as follows: 5 <= num <= 9.
The result is this, a fully 3d rendition of the Game of Life. The rule set could use tweaking given that the cells tend to bubble outwards and oscillate around the edges. And of course you do not benefit from Redux’s immutable patterns.
Never the less I was pleased with the result which went on to influence a range of projects which took place thanks to Matter of Stuff.