I’ve seen many implementations of this Algorithm or “Game” in almost any language or platform out there. I have not seen yet, someone implementing this on Salesforce LWC. I’m pretty sure someone might have tried that before me, But I thought to myself.. “Self..maybe you should write one and see what you can learn from this…”
In addition, I’ve recently heard that John Horton Conway, the inventor of this amazing algorithm, has recently passed away from COVID-19, so I thought this can be a nice tribute for him as well.
In the 1970's, he explored or even helped create the field of Cellular automaton. In short, this is where scientists were literally observing a grid of cells in any finite number of dimensions.
- For each cell, There is a set of cells called its neighbourhood — Those are the cells surrounding a certain cell.
- Time starts when the Grid is set to a certain initial state – Zero or One on each cell.
- Then, we can observe the evolution of the grid, certain cells and/or their neighbourhoods.
The “Rules of life” will determine the next generation state for each cell – Alive or Dead / Zero or One.
This concept resembles and even illustrates the rise and fall of any living organisms or society based on population.
Evolution is determined by its initial state…
Quite deep. But the Game of Life logic can have infinite implementation and implications.
It runs on this basic, but quite a clever algorithm or if you wish, a “Set of Rules”:
- Any live cell with fewer than two live neighbours dies, as if by underpopulation.
- Any live cell with two or three live neighbours lives on to the next generation.
- Any live cell with more than three live neighbours dies, as if by overpopulation.
- Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
Basically when implementing those rules in an infinite loop over a 2 dimensional array – Some shapes literally get a “Life” of their own..
Many different types of patterns were discovered that can occur in the Game of Life. They even gave them names :
- still life : do not change between generations.
- oscillators : which returns to their initial state after a few generations.
- spaceships : translate themselves across the grid.
Theoretically, the Game of Life has the power of a universal Turing machine – anything that can be computed algorithmically can be computed within the Game of Life.
I was quite intrigued by this concept...
But enough background stories, everything you need or want to learn more about, a quick google search will lead your path to many more genius discoveries.
Let’s get cracking and try to build this algorithm in LWC and see this thing in action.
Find here the full playground code for this project.
- Code Playground – Full Working Example
But if you wish to see the breakdown step by step — keep on reading.
Step 1 : Create the Universe
Our first step here, would be to render a 2 dimensional data array that holds the initial state of our “universe”.
- We can set the number of the
rowsas parameters for our grid dimensions.
- We will set the
widthof the cell dynamically based on component screen size – just because, I like responsive things :)
Our initial state will be a random number of Zero or One for each Cell in the grid.
- Zero represents a “Dead” cell. One will be our “Alive” cell.
- We can also, set colour inputs for each state.
- I’ll skip the html parts in this post that shows the color and button inputs — but feel free to look in the actual example code for those.
We will use html5 canvas to draw our grid. I will be able to draw pretty easily on it and animate it. Plus… I wanted to play with canvas for quite some time so this can be a good use case.
Canvas Element on the HTML
A bit of CSS just to set the initial size of our canvas container — the rest will be handled in our JS file.
Our Initial JS File — Create a Random World
- When our component has rendered —
renderedCallbackwill be fired. This is where we will have access to our DOM elements. We need it, in order to get our canvas context and draw on it.
- We only want to call it once after render.
- We also need it, so we can set the width and height dynamically based on the DOM container size.
Cool trick for debugging — If we wish to evaluate our grid values generations :
- Un-comment the
console.table(universe)log statement and check in your browser console — we can actually display our grid quickly this way and check our values.
Step by step methods inside our JS code - Render the grid on the canvas.
- Getting the canvas
contextas parameter allows us to re-use and reset our context when we need.
clientHeight— will provide us the component current dimensions and will allow us to set the cell width and height dynamically.
- Iteration over the
columnsand X axis and
rowsas Y axis
- Setting each
- Setting the colour based on the given state value. (Alive/Dead colours)
- I’ve separated those methods so we can re-use them for the different functionality later on and tried to keep it readable.
Let’s test where we are on the UI… It should give us an initial dynamic scaled state canvas based on those
grid holds a 2 dimensional array with random values of 0 or 1 as the cells state that are also painted with our set of colours.
Step 2 : Creating the World’s next Generations
- This is where we first need to identify the Neighbourhoods for each cell, then count all living cells.
- Secondly, implement the Rules of life and Generate the Next Generation of the
So, based on the current state of each cell and how many neighbours are alive, we can define the new state of each cell and assign it into the Next Generation grid.
- The below
nextGenerationmethod will be broken down into steps.
- It will allow us to both
countNeighboursand set the new state for each cell based on the
countNeighbours method will sum up the values in each neighbourhood.
Respectively it will also implement the Edges and Corners rules which we will discuss a bit further below.
This is a good point to stop for a second and understand, how can we identify the neighbourhood of a given cell ?
When checking for neighbours we basically need to run on the surrounding “neighbourhood” of the cells as an array by itself.
We are iterating over
columns and then again over
rows— 2 nested iterations.
In the above case, We can shift one column to the left and one to right, same goes for top to bottom on the rows.
This will give us the neighbours of the middle cell (Red).
We can build another iteration here to cover this with this setting :
- xStart : indexStart, // -1
- xEnd : indexEnd, // 2
- yStart :indexStart, // -1
- yEnd : indexEnd // 2
columns represent our X axis and
rows represent our Y axis. By running between -1 up to 1 in each iteration, we are covering the cells to the :
- Top Left, Left, Bottom Left => Column 0, Rows 0 up 2.
- Top, Bottom => Column 1, Only Row 0 and 2
- Top Right, Right, Bottom Right => Column 2, Rows 0 up 2.
- We need to skip the middle cell : 1 : 1 => that’s the one we are evaluating.
I must admit that when I started developing this thing, I didn’t fully realise, what kind of a complex “mind ** procedure” it is, when you keep iterating over those
rows and trying to understand how to calculate those in your brain.
Did you get this headache already ? Which cell are we iterating now ? go left then right ah ?? It helps to visualise this as much as possible.
Handling the Grid Edges and Corners
When using for example a
3X3 grid (like above) we can see a different size of a neighbourhood when it comes to any cell, BUT the middle Red cell.
- For the algorithm to work — we basically need 8 cells as a neighbourhood.
- So we can either just ignore any other case and only handle the cells that has 8 neighbours.
- This will leave the edges and corners in their initial state.
But, I’ve seen a few ways of handling the edges and corners, which can also give us dynamic edges.
The problem here is that :
- Corners — will have only 3 neighbours.
- Edges — will have only 5 neighbours.
In those cases, we can extend the array with Dead Zero cells , Or maybe simpler to say, Only count the available cells for each case.
- This seems to be more interesting and will also make the entire grid dynamic.
So I’ve built this
getNeighbourhoodCells method to handle the iteration for each neighbourhood relative to its position on the grid.
- It seems a bit long… but hey you kept on reading this far :)
- For each corner or edge — it will run relatively on the available cells
- Otherwise it will give us the 8 neighbours scenario as default.
Personally, Not a big fan of the many
if/else if statements but I hope it makes it easy to read.
Once we have the total number of neighbours and we know the current cell state, we can finally run the Rules of life on this cell and calculate the next generation grid.
Step 3 : Lets Animate the Universe and see the future generations !
Now, this step is optional. We can just leave it with a next button that shows us the next Generation each click.
But, we have a few options to perform this kind of an infinite loop or animate the canvas.
- SetInterval — Will run in an infinite loop and allow you to control the speed in a pretty straight forward way.
- window.requestAnimationFrame — This should be more efficient as it calls the next animation frame only once ready.
- But, to control the speed of the animation I had to do some math.
- This does not seem to work for me in the web components playground. But works great inside Salesforce.
- Those are the currently supported window methods in LWC.
- Above code is how I have achieved it, though as always there are better and more optimised ways to code this challenge.
- Many wise people, have actually found ways and written complete theories on how to optimised this algorithm and shorten up the code to achieve it — I just tried to see if it would work and I that I can play with my example.
So from now on it’s mainly which UI buttons we wish to have in order to interact with the grid.
I’ve added some slds base input component to get the colour and sliders inputs along with some buttons on the html template — that’s easy.
But thats it.. Challenge accepted and completed successfully.
We are ready to observe our animated Game of life.
While its probably a complete useless app to have this inside a Salesforce Record Page, it might help pass some time for curious users in our org :)
Full Working example can be found here :
lwc-conways-game-of-life-example by vyuvalv
canvas implementation inside lightning web components by Yuval Vardi
I went on to add some more functionality into this project to allow me to draw the different shapes as I please and examine their behaviour.
Feel free to fork it and extend it as you wish.
There are some pretty cool examples online for different implementation.
I’ve even heard about a Game of Life written in the Game of life.. Embedding audio and sounds and all sorts of crazy stuff.
I hope this article shows my small contribution to the community and my greatest appreciation to Conway’s Game of life. RIP John Conway.
Worth recognising and noting that he made much larger contributions to mankind other than just the “Game of life”, he in fact didn’t really like it! He thought it stole some attention from other great inventions. But for me, this is the something that just caught my eye and made me think. So thanks for that !
Hope you enjoyed reading… I enjoyed figuring this one out and sharing the story of a great man.