## Introduction: Game of Life With Nokia 5110 LCD

College dorms generally don't allow you to have pets in the room. Even if you did keep a pet, you would have to dedicate time and money to feed it and maintain it's environment. However, sometimes it's still nice to have another lifeform in the room besides your roommate...

Give digital life a try!

Today I'm going to show how I made Conway's Game of Life on an Arduino Nano, displayed on the Nokia 5110 LCD Screen. The hardware setup should also allow you to explore other cellular automata algorithms and games of life.

I like to look at Conway's game of life as a bunch of bacteria or microbes. By having a randomly generated starting map and following a simple set of rules, Conway's game of life can fascinate you with the different life forms, structures, and sequences it displays. It's like a digital petri-dish or fishtank that you can check on as a distraction.

Attached documents

I attached a few books related to Conway's Game of Life that you might find interesting.

• "the art of cellular automata.pdf" - this has a lot of examples of other algorithms of cellular automata and patterns that you might be interested in trying out
• "Turing Machine Universality of the Game of Life.pdf" - how Conway's game of life can make state machines and make a computer
• "cellular automata.pdf" - confusing math stuff I don't understand

One of the most valuable takeaways I got from the readings is that complexity comes from the interaction of simple rules over time. If you think about it, things can seem very complicated from up close, and can seem very simple from far. But the complexity/simplicity of a system doesn't matter, because it's an important part of a more beautiful and infinite system.

Other notes

I wanted to make a physical platform to try out different algorithms of cellular automata, and the most common is Conway's Game of Life, so I will be walking through the code for that.

This runs on Arduino Nano, with external SRAM for memory, and a Nokia 5110 screen.

It's pretty slow. I'm looking for a more efficient method. I'm not great at code, but this is what I have so far.

## Step 1: Parts

• 2 Buttons
• 1 Potentiometer
• 1 Arduino
• 1 Nokia 5110 screen
• 23k256 SRAM chip

this other stuff is if you want to follow my horrible setup

• cell phone battery (preferably not from a Galaxy S7)
• headers for the Arduino to plug into
• An on-off switch of some sort (I used a jumper)

## Step 2: Assembling the Circuit

Here is a schematic and a breadboard diagram of how everything is connected! I soldered this onto a perf board, so I don't have any circuit board to follow. Attached are the fritzing files too.

The connections aren't too important for functioning, as we're working with an Arduino with plenty of IO ports. Basically the SRAM is connected to the SPI, the LCD screen is connected however you configure it in the PCD8544 library, and the buttons and potentiometer are connected to some IO pin.

## Step 3: Overview of Code

This will be a brief overview of how each generation is calculated in this code. It's definitely not the most efficient way to handle memory or do calculations. Please share what you guys come up with to make this process more efficient! In this code, each life is a byte of memory. It requires external SRAM to store all the lifeforms (unless you're using an Arduino Mega). The SPI communication is slow, so the calculation speed is limited.

Attached is the code.

In case you wanted even more information, you can check out the attached pdf I have of my scribbles.

Generate initial random states -> A

```/*******************This function generates random*********************/void initialRandom() {  for (int i = 0; i < 8064; i++) {
SRAM.write_stream(i, pixoff, 1);
}
for (int i = 0; i < sat; i++) {
int randx = random(0, 84);
int randy = random(0, 48);
display.drawPixel(randx, randy, BLACK);
// write same thing to randx and randy of the nopixel matrix
SRAM.write_stream((84 * randy + randx), pixon, 1);
}
display.print(0); /*************************you can disable the generation count here*******************/
display.setCursor(0, 40);
display.display();
}```

This part of the code chooses random pixels to turn on. The density of the lifeforms depends on the potentiometer reading. The code displays these pixels and also writes them to a part of the SRAM I will call "A".

Calculate from A to B

```/*********************calculate generation from 1 to 2*******************/void calcNextGen()
{ //goes from 1 to 2
//  for (i = 4032; i<8064 ; i++) {SRAM.write_stream(i, pixoff, 1);} //clear 2
for (i = 0; i < 4032 ; i++) {
numNeighbors = 0;    //reset neighbors per pixel
x = (i) % 84;
y = (i - x) / 84;
findNeighbors(x, y);
//[AxAy+BxBy+CxCy+DxDy+ExEy+FxFy+GxGy+HxHy]
numNeighbors = countNeighbors(Ax, Ay)
+ countNeighbors(Bx, By)
+ countNeighbors(Cx, Cy)
+ countNeighbors(Dx, Dy)
+ countNeighbors(Ex, Ey)
+ countNeighbors(Fx, Fy)
+ countNeighbors(Gx, Gy)
+ countNeighbors(Hx, Hy);
//<2||>3 DIE, =3 LIVE, =2 same
if (numNeighbors == 2) {
//do nothing
}
else if (numNeighbors == 3) {
SRAM.write_stream(i + 4032, pixon, 1);
}
else {
SRAM.write_stream(i + 4032, pixoff, 1);
}
}
}```

This part of the code looks the states of pixels in "A".

For Conway's Game of Life, this part of the code counts the number of neighbors (the 8 immediately surrounding pixels) a cell has to determine it's next state (dead or alive). If there are 2 neighbors, a cell remains it's current state. If there are 3 neighbors, a cell will become alive or stay alive. Any other amount of neighbors, the cell should be dead. If you look at countNeighbors() and findNeighbors() in the attached code, you will find more details.

After determining the future state of a cell, the new state is stored in "B" of the SRAM.

Output B to A

```/*********************move from 2 to display*****************************/void dispGen()
{ //goes 2 to display
for (i = 4032; i < 8064 ; i++) {
x = (i - 4032) % 84;
y = (i - 4032 - x) / 84;
if (income[0] == 'W')
{
display.drawPixel(x, y, BLACK);
SRAM.write_stream(i - 4032, pixon, 1);
}
else
{
display.drawPixel(x, y, WHITE);
SRAM.write_stream(i - 4032, pixoff, 1);
}
}
generationCount++;
display.print(generationCount); /*************************you can disable the generation count here*******************/
display.setCursor(0, 40);
display.display();
}```

In this step, whats stored in "B" will now be displayed and stored in "A" of the SRAM. "B" is cleared for the next cycle.

1. Generate initial random states, store this is section "A" of SRAM
2. Calculate from "A" to "B" in the SRAM.
3. Output "B" to "A" in SRAM and display

Repeat steps 2 and 3 forever!

## Step 4: Other Methods of Calculation

I was looking at other code online for more efficient ways of doing Conway's GOL and I found ticklemynausea's code .

https://gist.github.com/ticklemynausea/fef80472743...

The code is pretty cool! Each lifeform is stored as a single bit. Groups of 8 lifeforms (1 byte) are calculated at a time. This takes up 8 times less memory than my code. Each byte is calculated top to bottom, left to right on the Nokia 5110 screen and lines up perfectly with the way the PCD8544 driver operates. It operates very swiftly, as you can see in the videos.

I modified the code a bit to make it a wrapping world.

However, because it operates a byte at a time, you might notice that it's not truly fullscreen. I started to notice life dying prematurely as if it hit an invisible wall, life existing where it shouldn't, and cells not interacting with one another.

If someone knows how to fix this bug, please share! It would get rid of the need for external memory and make the GOL operate much more quickly!

## Step 5: Holstein's Game of Life

Of course, there are other algorithms that you can explore and input into your Arduino. Here is an example of another cellular automata program you can run. The code is attached.

http://www.rendell-attic.org/CA/holstein/index.htm

found in page 150 of "the art of cellular automata":

Holstein is a little known cellular automata with rule B35678/S4678. It uses the same neighbourhood of 8 cells as Conway’s Game of Life however a cell is born if it has 3, 5, 6, 7 or 8 neighbours and survives if it has 4, 6, 7 or 8 neighbours. This rule is symmetrical in that a pattern of live cells in a background of dead cells behaves exactly the same as the same pattern in dead cells in a background of live cells. The result is generally quite boring as most patterns just shrink to nothing. There are a few small stable patterns and small oscillators and three large complex gliders have been found [41].

The rules go as follows:

• a cell is alive if it has 3, 5, 6, 7 or 8 neighbors
• a cell stays whatever it is if it has 4 neighbors
• else, it dies.

## Step 6: Soldering and Construction

This step is up to you to decide how you want to arrange your setup! I didn't have many materials at hand to make a beautiful enclosure, but I encourage you to find a respectable arrangement for your digital petri-dish!

You can find specific details about how I built my setup in the pictures.

## Step 7: Finished!

Congratulations! If you follow the schematic properly, you now have a nice digital tank for different cellular automata! Have fun trying out different rules and algorithms, and don't forget to share your code so others can try out different pets!

There's many ways the code could be improved. A few ways I could think of are:

• Make each lifeform bigger than 1 pixel so there's less life to calculate
• Calculate a bit at a time instead of a byte (less memory to handle)
• improve the code so that you can input the rules at the beginning instead of needing to change that block of code

Share your results! Thanks.