As a project for me to work on I decided to try coding in C Conway’s Game Of Life on the SuperCon Badge. What a hoot and adventure. Every time I thought I was about finished I would think of adding something else and end up breaking something different.
Well, it wasn’t that bad. I really hadn’t programmed in a variant of C like this in a while. ANDs and ORs were not cooperating so I separated them out into their own IF statements. The code began to look ugly, but it worked.
I thought I was finally done and I decided to add a restart button. I ended up writing a little demo program to make sure I had the coding of the buttons correct. All of the examples for checking on button presses included this little bit of code that could cause a delay. Whomever came up with the default code examples for the badge included the “inefficient” delay. Hey, it got the job done.
Once I had the buttons example running I decided to try to get rid of the “inefficient” delay. To get the program running at about the desired speed I had the little section in the MAIN loop running once every 50,000 times. This is a fast little chip on the badge.
I haven’t taken out the “inefficient” delay loop out of the Game Of Life program, yet, but I am ready to document the story so far. After I got those pictures loaded into the computer and looked at the screen shot of the badge I see a change I’d like to make. I think I need to add a line above the bottom two lines that should say, “While running press:”. This way the person will know that B to exit and LEFT to reset will only happen once the program is running.
There are lots of web pages describing Conway’s Game of Life. Some just give the premise and others offer code snippets. As I understand it, for every space on the screen, if there is already a dot in place in that space then you would look at the eight adjoining, or touching, spaces around the space in question. Add up all of the dots around the space in question. If there are two or three dots the dot in that original space will continue to be a dot. If there are zero or one dots in all the surrounding spaces then the original dot will cease to be a dot and will become an empty space due to starvation. Like wise, if there are four or more dots in total around the original space with a dot that original dot will also cease to exist due to overcrowding. The original spaces that you would check that are in a corner or on the edge of the playing field are at a disadvantage due to less number of spaces surrounding them. You check every space within the playing field before you refresh the screen. I used a second array to hold the results and then sent that to the display.
A second thing to consider is if the space you are checking starts out empty it can become populated with a dot if there are three, and only three, spaces around that original space with a dot in them.
(I just thought that I could have the screen wrap around from bottom to top and left to right. Programming for another day.)
The ‘game’ runs until the screen is empty or certain patterns end up on the screen. A 2×2 block of dots will just sit there, for example. A vertical straight line of three will switch to a horizontal straight line of three and back again.
At one point I thought I was done with the game. It was really boring because it always had the same result. I decided to put in a way to give a random seed into the mix. That shook things up a bit.
At another point when I was checking how other programs were running on the badge I noticed Badgetris turned off those annoying LEDs. They are quite bright and bounce off of my glasses when I am looking at the screen. I included that line from the Badgetris code into my program.
Should I be including code snippets as I go along? Perhaps…
I am going to have to go through the code and add comments so you will know what I am doing.
There was one part that particularly did not work as I was coding. I could not figure out why it would not work. I thought I should be able to use
if (playArea[29][18] == 1 && (holder == 2 || holder ==3)) { newPlayArea[29][18] = 1; } else { newPlayArea[29][18] = 0; } // now check holder status if (playArea[29][18] == 0) { if (holder == 3) { newPlayArea[29][18] = 1; } else { newPlayArea[29][18] = 0; } }
The top part with the AND and OR never went into that area. The screen cleared quickly and the game was over shortly. Splitting the AND and Ors up really helped. But, I think it shouldn’t.
if (playArea[29][18] == 1) { if (holder == 2) { newPlayArea[29][18] = 1; } else if (holder == 3) { newPlayArea[29][18] = 1; } else { newPlayArea[29][18] = 0; } } // now check holder status if (playArea[29][18] == 0) { if (holder == 3) { newPlayArea[29][18] = 1; } else { newPlayArea[29][18] = 0; } }
In actuality I think I had the OR before the AND.
I also had noticed the error in “if(playArea[29][19] = 0;}}”. The error is the 19 should be should be 18.
That just might work with all those extra parantheses. And the OR part is second instead of being checked first as in my original attempt. I’ll go try it and come back with results.
I tried many variations of having three comparisons inside the IF statement. Perhaps I am doing it wrong.
None of the checking three booleans inline worked so I am breaking them all out. It is not that it matters, but smaller code will run a bit quicker.
While I was testing the code I still find that I don’t like the difficult readability of the default text on the screen. I also thought putting a background on the play area would be fun. This could become a never ending project. I don’t want that. 😀
–++–
I have tossed in a background picture. Now it is harder to see the game. I’ll have to create a different character in a tile map and use that as the character. The default blue character with a white outline is blurry looking anyway. I am looking at it with eyes that have over six decades of use.
I changed the tileset so that I can change the color of the font. That was pretty easy to do not that I know that the graphic files should only have 16 colors. In GIMP it is easy to set the colors to indexed and 16 colors. Then you can play with the index so you have the color you want.
I guess I better change the pictures so they match what the screen looks like now.
—+++—
Well, riding in the car back from San Diego while my wife was driving I decided to work on a version of GOL that uses wrapping on the screen. There is now a version that wraps between the top and bottom as well as side to side. The results are different. It was easy to add the code.
I wonder if I can add the code as an option on the main screen. Bet I can, if I want to do that. You can do anything if you put your mind to it.
I still need to post pictures of the game running with a background and green hash characters.
—+++—
I have uploaded files to GitHub for both versions the Game Of Life. There is the version that does not wrap around the screen edges and one that does.
The first link is the one that wraps around the screen. It creates an .elf file that is GOL4. The .elf file is also available so you don’t have to compile the program before copying it to your Hackaday Supercon Badge. There is also a .elf file for GOL2, which is the one that does not wrap around the edges.
Quite a while back, October 2019, I was trying to see what I could make the Adafruit PyGamer badge do. I kept adding things to the code until I caused it to crash. It would crash in about 10 seconds sometimes. Others it would take a few hours. I was putting it through the paces.
While I was at Hackaday Supercon I showed the badge to some acquaintances. Later that day they said Scott Shawcroft, the maintainer of CircuitPython, wanted to check out the code. Scott stopped by the tables where we were soldering and coding. We were introduced and Scott asked to check out the PyGamer. He watched it crash in about five minutes and asked to check out the code on my laptop.
Back then, when the program would crash on the PyGamer, I would have to reinstall CircuitPython on to the device. Scott thought there might be a leak overwriting into CircuitPython. I zipped the files on the PyGamer and emailed them to Scott as we talked.
The attendees of Supercon received a new device from Adafruit while at the con. It was the EdgeBadge. I put my program onto the new device and it ran with no changes. It also crashed in a few minutes. After about an hour messing with the code on my new toy, the EdgeBadge died. I was wondering if the code was too much for the device.
Here is a copy of the code that would cause the PyGamer to crash with the older version of CircuitPython. I am trying to find a suitable plugin for listing long sections of code. Something to display long sets of code in a scrollable box.
I messed with the repetitive code trying to put it into loops, or functions but didn’t have the skills, or commands to do it. I revisited this code a few times over the next few months. I decided to crunch the code down and try the code on a new version of CircuitPython. The code still crashed, but it was not as deadly as it had been. I would just press the restart button and it would start running again. Yay!!
I managed to keep crashing the PyGamer. I decided to try to use lists (arrays) to handle the loops of the ‘green rain’. That worked well, and dropped the lines of repetitive code by a lot.
Since that worked out well I decided to add another animation on the screen. Even with all this happening on the device it was still crashing. I went back onto the Adafruit forums to see if someone was also having the same kind of trouble, or post my code to have it looked at. There was a newer version of CircuitPython and even NeoPixel.mpy. Now the code is running for hours. This morning I plugged it in and it was stopped in the middle of running after a little bit of time. That must have been a fluke. I plugged it into the wall usb and hung it up on a hook around 8 this morning. It was still running at 5 this evening.
# Write your code here :-)
# pygamer_dancing_fid_rev_1.py
# ** Cleaning up the code.
#
# I am now using Lists (arrays)
# for a few of the variables groups.
# **
# Press Button A to speed up the LEDs cycling.
# Press Button B to slow down the LEDs cycling.
# Press Start to change the color of the LEDs.
# Press Select to change the color of the name, fid.
#
# text_area3 through 5 now handles the name fid.
# Each letter is handled separately.
# You can change it in there.
#
import board
import neopixel
import digitalio
import displayio
import gamepadshift
import array
import math
import random
import audioio
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font
from adafruit_display_shapes.rect import Rect
display = board.DISPLAY
FREQUENCY = 2093 # 440 Hz middle 'A'
SAMPLERATE = 2 * FREQUENCY # 8000 samples/second, recommended!
# Generate one period of sine wav.
length = SAMPLERATE // FREQUENCY
sine_wave = array.array("H", [0] * length)
for irange in range(length):
sine_wave[irange] = int(math.sin(math.pi * 2 * irange / 18) * (2 ** 15) + 2 ** 15)
# enable the speaker
speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
speaker_enable.direction = digitalio.Direction.OUTPUT
speaker_enable.value = True
audio = audioio.AudioOut(board.A0)
sine_wave_sample = audioio.RawSample(sine_wave)
led = neopixel.NeoPixel(board.NEOPIXEL, 5)
i = 0
led_speed = 200
red = (5, 0, 0)
blue = (0, 0, 5)
yellow = (5, 5, 0)
green = (0, 5, 0)
white = (5, 5, 5)
purple = (5, 0, 5)
pushed = 0
the_color = [red, blue, yellow, green, white, purple]
fid_color = [0xFF0000, 0x0000FF, 0xFFFF00, 0x00FF00, 0xFFFFFF, 0xFF00FF, 0x000000]
myX = 0
myY = 0
ledCheck = 0
ledOn = 0
soundTimer = 0
ledDirection = 1
fid_sine = [0, 1, 3, 4, 4, 5, 4, 4, 3, 1, 0, -1, -3, -4, -4, -5, -4, -4, -3, -1]
fid_count = 0
f_sine_count = 10
i_sine_count = 5
d_sine_count = 0
font2 = bitmap_font.load_font("SourceSerifPro_25.bdf")
font4 = bitmap_font.load_font("SourceSerifPro_52.bdf")
text_area = label.Label(font2, text="Hello", color=0xFFFFFF)
text_area.x = 48
text_area.y = 10
text_area2 = label.Label(font2, text="I am", color=0xFFFF00)
text_area2.x = 53
text_area2.y = 30
text_area3 = label.Label(font4, text="f", color=0xFF0000)
text_area3.x = 45
text_area3.y = 80
text_area4 = label.Label(font4, text="i", color=0xFF0000)
text_area4.x = 70
text_area4.y = 80
text_area5= label.Label(font4, text="d", color=0xFF0000)
text_area5.x = 85
text_area5.y = 80
j = 0
k = 0
my_color = the_color[j]
speedCheck = 10
pad = gamepadshift.GamePadShift(digitalio.DigitalInOut(board.BUTTON_CLOCK),
digitalio.DigitalInOut(board.BUTTON_OUT),
digitalio.DigitalInOut(board.BUTTON_LATCH))
rect = [0] * 40
slow = [0] * 40
bitmap = displayio.Bitmap(display.width, display.height, 2)
palette = displayio.Palette(2)
palette[0] = 0x000000 # This is the background of the display
palette[1] = 0x8A2BE2 # This is the foreground, Purple
# TileGrid for the bitmap
bitmap_grid = displayio.TileGrid(bitmap, pixel_shader=palette)
rect_back = Rect(0, 0, 160, 128, fill=0x000000)
for irange in range(40):
rect[irange] = Rect(irange * 4, random.randint(0, 120), 3, 3, fill=0x00FF00)
for irange in range(40):
slow[irange] = (random.randint(0, 10) * 20) + speedCheck
speed = [0] * 40
# Create a Group to hold the TileGrid
group = displayio.Group(max_size=50)
# Add the TileGrid to the Group
group.append(rect_back)
group.append(text_area)
group.append(text_area2)
group.append(text_area3)
group.append(text_area4)
group.append(text_area5)
for irange in range(40):
group.append(rect[irange])
# Add the Group to the Display
display.show(group)
while True:
if ledCheck > led_speed:
if ledOn == 0:
led[i] = (my_color)
ledOn = 1
else:
led[i] = (0, 0, 0)
ledOn = 0
i += ledDirection
if i < 1 or i > 3:
ledDirection *= -1
ledCheck = 0
ledCheck += 1
if pushed == 1:
soundTimer += 1
if soundTimer == 300:
audio.stop()
pressed = pad.get_pressed()
if pressed == 0:
pushed = 0
audio.stop()
if pushed == 0:
pressed = pad.get_pressed()
if pressed == 0x01:
led_speed += .05
if pressed == 0x02:
led_speed -= .05
if led_speed > 500:
led_speed = 500
if led_speed < 10:
led_speed = 10
if pressed == 0x04:
j += 1
if j > len(the_color) - 1:
j = 0
my_color = the_color[j]
pushed = 1
soundTimer = 0
audio.play(sine_wave_sample, loop=True)
if pressed == 0x08:
pushed = 1
k += 1
if k > len(fid_color) - 1:
k = 0
text_area3.color = fid_color[k]
text_area4.color = fid_color[k]
text_area5.color = fid_color[k]
for irange in range(40):
speed[irange] += 1
for irange in range(40):
if speed[irange] > slow[irange]:
speed[irange] = 0
slow[irange] = (random.randint(0, 10) * 20) + speedCheck
rect[irange].y += 4
if rect[irange].y > 126:
rect[irange].y = 0
fid_count += 1
if fid_count > 15:
fid_count = 0
f_sine_count += 1
if f_sine_count == len(fid_sine):
f_sine_count = 0
text_area3.y = 80 + fid_sine[f_sine_count] * 3
i_sine_count += 1
if i_sine_count == len(fid_sine):
i_sine_count = 0
text_area4.y = 80 + fid_sine[i_sine_count] * 3
d_sine_count += 1
if d_sine_count == len(fid_sine):
d_sine_count = 0
text_area5.y = 80 + fid_sine[d_sine_count] * 3
I have cleaned it up a little more and took out unused includes. I also added some comments inside the code. It went from 592 lines of code down to 209. I can probably crunch it down some more and add some stuff.
Perhaps the ‘green rain’ could be changed to sprites to make them fall faster.
BTW, if you know of a better way for me to list the code, especially long code, please let me know. SyntaxHighlighter can make the code into a clickable line to open, but I would rather have a set size box that is scrollable.