# Project Chronicles: Game of Life

# Conway’s Game of Life

Conway’s Game of Life is a cellular automaton. Although it is called a “game”, it is a zero-player game. In this automaton, cells reproduce, evolve, and destroy themselves. They either form ordered tribes and “civilizations”, or silently vanish in history. I drew a dynamic, visual game of life through simple HTML, CSS and JS. We play the role of “the highest”, either indifferently observing the evolution of life, or altering the life and death of a cell with a single click to control the rise and fall of civilizations. For more information about this project, please go to Introduction , and to Project itself.

# Project Epiphanies

Through the development of the project, there were some hard challenges from the project that I learnt a lot, which I also think are useful for future reference. The hits from this project are:

- Analysing
**CSS clamp**for different situations - Take a drawn
**pattern**on a**JS canvas**, duplicate it and**rotate**it to a different angle.

## CSS clamp

### clamp syntax

I’m sure you’re quite familiar with the clamp syntax, but in case you need to, check the documentation if necessary.

#### clamp variables

CSS’s clamp function accepts the following variables:

1 | clamp(MIN, VAL, MAX) |

and then returns a value that depends on these three variables. In between, if `VAL`

corresponds to a value that is not less than `MIN`

and not greater than `MAX`

, then clamp will return `VAL`

.

In this description, you can see that `MIN`

and `MAX`

play the roles of maximum and minimum values. If `VAL`

is less than `MIN`

then `MIN`

is used. Similarly, if `VAL`

is greater than `MAX`

then `MAX`

is used.

This function can be implemented actually by earlier functions with `MAX()`

, `min()`

, what’s more, a clamp is exactly equivalent to:

1 | max(MIN, min(VAL, MAX)) |

Clamp doesn’t only combine them, but optimizes the counter-intuitive problem that the names `max()`

and `min()`

bring. For example, when we want to set a minimum unit of font size, `min()`

is probably the first thing that comes to mind. But this will only further **reduce** the font size. What we need is `max()`

to choose the smallest font size when there is a smaller one. Clamp solves this counter-intuitive problem nicely.

#### clamp example

1 | font-size: clamp(5rem, 10vw, 20rem) |

### clamp analysis

Readers may have a doubt while reading, with `VAL`

and `MIN`

(with `MIN`

as an example but `MAX`

also applies) as fixed CSS values, how can they be `VAL`

larger than `MIN`

from time to time and `VAL`

smaller than `MIN`

in other times?

The answer is simple: when the value depends on the size of other elements and is dynamically adjusted, then the values are not fixed any more.

For example, in the last clamp code example, `10vw`

is **200px** when the screen size is 2000px wide. Suppose at this point `1rem`

is equivalent to 16px, then `MIN`

is equal to **80px**. It is easy to see that at this point clamp will return **200px** (no 320px greater than 20rem).

However, if the user is reading on the phone screen, then the screen size may be reduced to 400px width. At this point, `10vw`

is equal to **40px**, while `5rem`

is still equal to **80px** (in the real case, the cell phone’s rem will be smaller). Then, according to the rules of clamp, at this time `VAL < MIN`

, so it will return the **80px** of `MIN`

, complying with the minimum font size.

So it can be seen that CLAMP can conveniently limit the size to achieve a dynamic limiting effect. However, it can not only balance between too large and too small but also extend the values more extreme beyond the proportion.

#### Two combinations of clamp

Up to now, I don’t know if you have noticed, but the variables of the clamp example I gave are of this form:

1 | clamp(fixed, scale, fixed) |

What if we take this form:

1 | clamp(scale, fixed, scale) |

What happens if you use this clamp? For this reason, I will call the two different forms of clamps by their effects as *stable clamps* and *dynamic clamps* respectively.

#### Stable clamp

Stable clamp is how we normally use clamp, for example, to stabilize font size, image size, etc.

1 | clamp(fixed, scale, fixed) |

Due to the fixed value limit, this value does not usually change drastically depending on the size of the parent element/screen. For example, it will not become too small for a small phone screen, or too large for a large computer screen. **This is why stable clamps are very suitable for important elements that need to be stable,** such as fonts, images.

##### Dynamic clamp

Dynamic clamp is required for fewer scenarios, examples include margin, padding and border.

1 | clamp(fixed, scale, fixed) |

In the case of preferably fixed values, it means that the size of this element will be maintained even under different screens. In this sense, this clamp applies to elements that need to be fixed in size, such as images sometimes. But in most cases, we can take full advantage of the scale size limited by both ends, and the preferred fixed value simply acts as a trigger line to split the proportion needed for larger screens and the proportion needed for smaller screens.

The advent of stable clamps has solved the problem of making the size of the two ends converge to a balanced middle value. So the meaning of dynamic clamps will be the opposite, that is, increasing the difference between the two ends. This means that on small screens the element will become smaller than the anchored scale size, and on large screens, it will be larger than the anchored scale size. **So variation clamp is suitable for less important, secondary elements, elements that leave space for more important elements when the screen is small; and elements that can fill in the gaps when the screen is large,** such as margin, padding and border.

### Short summary

CSS clamp is a quite useful function, which can be dynamically adjusted to different needs. If you want to study the most correct values of clamp, you need a mix of scale and fixed variables to achieve the best performance. For specific values, please refer to the search article on the web for the keywords related to the perfect variables to fit a clamp.

## JS canvas pattern flipping

For the presentation of the Game of Life selection, I used HTML canvas and JS to render dynamic visualization. So I drew the cells using the built-in drawing function from canvas context. So how should each life be represented? In the end, I decided to use a lap of leaves to represent the cells after a few minutes of thinking. Resulting figure:

How to implement it exactly? The function `drawCell()`

that draws a whole cell looks like this:

1 | // Draw whole cell |

We first need to draw one leaf, which is a base unit. Then we can call this leaf function and rotate it to different angles.

### Draw a leaf

In the `drawCell()`

function, `drawLeaf()`

is called several times to form a cell pattern with multiple leaves clustered around it. And `drawLeaf()`

looks like this.

1 | // Draw single leaf |

The result looks like this.

This example, if run, will only work for one leaf, and later we will see how to copy and rotate it. For now, let’s briefly see how to construct one leaf.

#### Point, Line, Surface

This method that I use is rather clumsy. It might be better to use the drawn picture directly and then have the canvas draw it for you.

The steps in between are not too difficult in order to draw this leaf. First, assuming that we are drawing the leaf pointing upwards, the first step is to figure out each of the **key points** used to outline it. What is a key point? It refers to the points of change when drawing the contour lines. For example, if we want to draw the sides of a square, then its four corners are the key points because the line will change at this point. Similarly, this applies to arcs, where the intersection of two different arcs is a key point. In this example, `p1`

to `p2`

depicts the upper left arc, while `p2`

to `p3`

outlines another arc with different circle centers `c1`

and `c2`

(p.s. they are the same now, but the previous pattern version had two different circle centers, and it’s more intuitive to split it into two parts, so I kept both circle centers).

Regarding the coordinate system of the key points, I used the center point of the respective X and Y axes as the origin (0, 0) coordinate in each cell. The top is +y, the bottom is -y, the left is -x, and the right is +x. This is useful when copying and rotating other leaves according to the center point, after all, the axis/point of rotation is the center point of X and Y axes. Of course, you can also use the upper left corner as the origin of the coordinate system. Then, find the relative coordinates of each key point with their respective centroids, notice that it is a rather lengthy step full of trials.

After using this method, you can then connect the points into a set of **line** segments. For example, `drawCurve()`

is a custom function of the Cell class, but inside it is actually a `ctx.arc()`

, a built-in function for drawing curves. There is also, for example, `lineTo()`

function for drawing lines, which draws a basic straight line.

The last thing is to fill the area connected by the lines into one **face** of the complete leaf. The filling can be done using `ctx.fill()`

. In the end, I have also added some lines to pretend leaf veins, as an embellishment.

### Draw multiple leaves

It’s not hard to copy and paste to draw multiple leaves, you just need to call `drawLeaf()`

a few more times. The question is how to rotate them regularly?

For this kind of method that requires angle and periodicity, the first thing that usually comes to my mind are the trigonometric functions. If sin and cos are used properly to add, subtract, times 0, times 1, then is it possible to achieve this effect? The answer is yes.

At first, I thought this approach: assume that each point has x and y offsets, called dx and dy, which can be positive or negative or none. This offset d is the difference with the origin, for example, the dy of `p1`

is -1/2. Ignore `size`

for now because its value is constant. Then you can use cos and sin to periodically increase and decrease d. For example, the `p1`

-dx of the upper leaf is +dy on the right-pointing leaf, and this mental picture should not be hard to imagine, so you readers can try it.

#### Naive attempt

The dx and dy of each point change for each angle, even to the point where dx is attached to y and dy is attached to x. This process is periodic, which ensures that the coordinates of the leaves are cyclic. Of course, when dx and dy are no longer necessarily the offsets that represent x and y at different angles, then we give them a different name. We call the x offset of the original leaf **d1** instead of dx and the y offset **d2** instead of dy.

This periodicity and offset may sound roundabout; a tabular representation would be:

90º leaf | xd1 | xd2 | yd1 | yd2 |
---|---|---|---|---|

Scale | 1 | 0 | 0 | 1 |

This should not be difficult to understand. In the most primitive 90° leaf, how much d1 and how much d2 does x have? Because we have already defined **the x offset of the primitive leaf as d1 and the y offset is d2**, so x has full d1 and no d2; while y has full d2 and no d1.

Later, I took this for granted that the rest of the angle should follow the same rules.

Any leaf | xd1 | xd2 | yd1 | yd2 |
---|---|---|---|---|

Degrees | sin | cos | cos | sin |

This gives us the following code:

1 | // Attempt for multiple leaves |

**BUT**, once you try to draw with this function, there will be an error, the leaves simply do not look like leaves and coordinates look disordered. To be honest, my heart broke when the leaves didn’t show up correctly even though I spend so much time analysing them. I obviously couldn’t give up at this point, so I continued trying. Swapping sin and cos, setting the initial angle to 0º, and so on, but I couldn’t make it right. At the end of the day, I almost wanted to give up and just use a square to represent the cell. As a final trial, I tried to write out the right, complete table of angles, and to my surprise, I did find some patterns which I was previously wrong.

#### The final pattern

When I wrote out the values corresponding to each angle, it looked like this:

Degrees | xd1 | xd2 | yd1 | yd2 |
---|---|---|---|---|

0 | 0 | -1 | 1 | 0 |

90 | 1 | 0 | 0 | 1 |

180 | 0 | 1 | -1 | 0 |

270 | -1 | 0 | 0 | -1 |

After comparison, I realised that the corresponding trigonometric functions are not:

Any leaf | xd1 | xd2 | yd1 | yd2 |
---|---|---|---|---|

Degrees | sin | cos | cos | sin |

but:

Any leaf | xd1 | xd2 | yd1 | yd2 |
---|---|---|---|---|

Degrees | sin | -cos | cos | sin |

Because cos and -cos of 90º are both equal to 0, and I totally ignored -sin and -cos at that part. This caused the code to be drawn incorrectly. So I immediately checked with other angles, and found that d1 and d2 of each angle are perfectly right now!

I immediately used it in the code as follows:

1 | // Abstracted for all degrees |

Successful in getting the desired leaf cells!

### Short summary

In this JavaScript canvas article, we learned how to use trigonometric functions to rotate and copy a specific pattern. If the angular requirements and coordinate system starts at the center, you can just use these trigonometric functions directly in the table that I came up with at the end. But more importantly, is that this can be applied to different scenes and distinct angles. This article presents an idea, an example that can help and then be optimized by readers.

# Ending

Overall, I had to face various challenges when developing this project, and needed to persevere relentlessly. I proudly say that I enjoyed the process. That is why I gathered these learnt points, summed them up, and written them down to archive them for future reference.

**Title:**Project Chronicles: Game of Life**Author:**Gnefil Voltexy**Created at :**2022-07-17 20:50:03-
**Updated at :**2024-07-27 17:51:42 -
**Link:**https://blog.gnefil.com/2022-07-17/Project-Chronicles-Game-of-Life/ -
**License:**This work is licensed under CC BY-SA 4.0.