Conway's Game of Life - with friends!
Tags: react, node, c, showdev
Published: 2019-08-30 17:16:16

This is the story of how I created something cool and amusing, using computers, electronics, code, creativity and curiosity. The end result being https://www.multilife.jmercha.dev/ 

{% github jmercha/multilife %}

## Origins

I was talking to a few friends and colleagues recently about [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life). It can be basically explained as follows. 

There is a grid of cells with 2 states: alive and dead. On each iteration of the game there are a set of rules which are evaluated on each cell on the grid:

1. Live cells with < 3 live neighbors die
2. Live cells with > 1 live neighbors live on to the next iteration
3. Live cells with > 3 neighbors die
4. Dead cells with exactly 3 neighbors become alive

Back in the 2000's, I had created a Game of Life Java Applet that ran in the browser - Applets being long since deprecated and the original source code unfortunately lost to time. I had a lot of fun writing it and showing it off to people.

I started to think to myself, could I do it again in 2019 using my favorite web technologies? Could I performantly render a dynamic grid of cells in JSX? How would the game state be represented and updated? 

I ended up going down multiple rabbit holes and tangents and ended up learning a lot!

## Experimentation

In the initial iterations of my experiment, I attempted to render the the grid as a sequence of JSX elements. `<span>` elements with '⬜' (white box) emoji to represent living cells and '⬛' (black box) to represent dead cells. As those familiar with React may know, this wasn't a great idea: the DOM is excruciatingly slow to update, even with React's [reconciliation](https://reactjs.org/docs/reconciliation.html), it was still updating hundreds of DOM elements on each tick, resulting in an unresponsive experience. e.g. a cell click event would take almost 100ms to update the entire grid.

So, how could I performantly render the game of life grid then? The answer is, with a [\<canvas\>](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API). I used React's [useEffect](https://reactjs.org/docs/hooks-effect.html) hook to paint the game state on each state update to the canvas element.

## Multiplayer

I started to think about where to store and handle the game state and decided to manage the game state in Redux so I could clearly define and handle game events using actions and reducers. To put it as simply as possible, Redux is a "state container" that allows you to reduce the events (a.k.a actions) raised by your application into a single, predictable state. 

While I was implementing the reducers and actions I thought: wouldn't it be really easy to centralize the game state and broadcast to multiple "players"? I then moved all of the game processing logic: the game update interval, rule evaluation and player events into a Node.js server, hooked up some web socket actions and thus "multilife" was created.

Although there are existing frameworks for using WebSockets with Redux, e.g. [redux-websocket](https://github.com/giantmachines/redux-websocket), I decided to write my own, as there are only 4 simple actions required:

* `WS_OPEN` - connection open
* `WS_SEND` - send a message to the server
* `WS_RECEIVE`- receive a message from the server
* `WS_CLOSE` - connection closed

I also needed more control over the format I sent and received messages in, using binary instead of JSON, as I describe in the Optimization section.

## Colors

Now this is interesting, I thought! I could now broadcast the game state to multiple browsers, in real time! But... How could I make it _more_ interesting? I decided to give each cell a color because it looks pretty! Every player is assigned a random color when they connect. The cells also mix colors when they reproduce, creating some interesting patterns.

## Optimization

I found that serializing the entire game state and events in plaintext JSON was computationally very expensive and used a lot of bandwidth. I was talking to a colleague and they suggested to create a binary protocol, so I did! I also considered [Protocol Buffers](https://developers.google.com/protocol-buffers/), but I preferred to serialize the data myself.

I knew the binary protocol would be especially tricky to implement, so I used a [Test-driven-development](https://en.wikipedia.org/wiki/Test-driven_development) approach: I wrote initially failing `serialize` and `deserialize` tests for the binary protocol, each asserting that it could successfully serialize and deserialize a protocol model and then wrote the code for each method until they all passed. Unit tests are invaluable when working with complex logic.

I used the [color-namer](https://github.com/colorjs/color-namer) module to name each color in the game state. However, it was inefficient at first - every time it looked up a color it iterates through the entire list of color names to compare color distance, an `O(n)` (or linear time) operation and it did not cache the results of each color lookup. To improve the performance, I forked the repository and implemented [Memoization](https://en.wikipedia.org/wiki/Memoization) by caching the results in a [WeakMap](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap). I used a WeakMap so that the Garbage Collector would intermittently clear the cache, instead of filling up the cache forever (there are `2^24 - 1`, or 16,777,215 possible colors that could be looked up ... ). I also implemented support for the [Delta-E](http://zschuessler.github.io/DeltaE/learn/) color distance function for more accurate naming of colors. I submitted both these changes as a pull request to the module maintainer and they were eventually accepted and released.

{% github https://github.com/colorjs/color-namer/pull/9 %}


## Deployment and Release

It was now time to show the world what I had created! But how? Well, to host a website, I needed a server. I created a Droplet on [DigitalOcean](https://digitalocean.com) to host multilife. I also purchased a domain: multilife.live. (edit: I did not renew the domain, and it has since expired and is parked by someone else now!)

I set up [Nginx](https://nginx.com) to host the site, and [pm2](http://pm2.keymetrics.io/) to run the app, as well as [LetsEncrypt](https://letsencrypt.com) to provide SSL.

I also set up CI/CD using [CircleCI](https://circleci.com) so that I did not have to manually deploy to production whenever I merged new code into master. CircleCI also runs my tests before deploying.

After many attempts to get CI/CD working (many, many "fixing CI" commits), multilife was released and I shared it with my friends. We had a lot of fun clicking around and watching the patterns form. The site also uses responsive design, so everyone had their phones out touching on their screens!

## MultiLife RGB

{% github jmercha/multilife-rgb %}

I then wanted to take things a step further: what if I could render the game state to a RGB LED panel? Wouldn't that be cool? So I shopped around and purchased a [32x32 RGB LED matrix panel](https://core-electronics.com.au/32x32-rgb-led-matrix-panel-4mm-pitch.html) and a [Raspberry Pi Zero W](https://core-electronics.com.au/raspberry-pi-zero-w-wireless.html)

When I was wiring up the RGB LED panel, I accidentally connected a wrong pin and broke all of the electronic components in the panel - I wired 5 volts into the ground pin. Oops! I had to wait another week for a new one to arrive before I could get started. 

I learned a valuable lesson here: broken software is easy to fix, but you cannot easily fix broken transistors and capacitors.

![](https://i.imgur.com/AS1TCfw.jpg)


I wrote a program in C - [multilife-rgb](https://github.com/jmercha/multilife-rgb) to listen to game events from the server over UDP using my binary protocol and render the cells using the [rpi-rgb-led-matrix](https://github.com/hzeller/rpi-rgb-led-matrix) library. I chose UDP over TCP as it made more sense for the case I was using it for - TCP is a stream-based protocol and UDP is datagram, or message-based. I also didn't mind if messages occasionally weren't received or were in the incorrect order.

I found it was very convenient to read the multilife binary protocol in C, I simply assigned a pointer to the protocol model structs to the received message buffers.

```c
message.data = buffer + 1;
```

Although it did require using the [packed attribute](https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html) on the `Cell` struct to align the data correctly.

The end result is visually appealing, especially in the dark. I love being able to tap cells in from my phone or desktop browser and seeing it instantly appear on the matrix. 

## Conclusion

Perhaps in the future, if "mutlilife" somehow goes viral (I doubt it), I could scale it out by using [Redis](https://redis.io/) and the [Hashlife](https://en.wikipedia.org/wiki/Hashlife) algorithm, as well as supporting zooming and panning in the frontend and protocol.

Learning by building things is fun, even if you are building something practically useless. I hope this might inspire others to learn by building the superfluous!