Tiny Implementation of Conway’s Life…

March 22, 2015 | Arduino, Embedded, My Projects, My Stories | By: Mark VandeWettering

I had a few minutes, so I thought I’d try making a graphics demo that runs on the tiny little 0.96″ OLED display I mentioned last week. One of the first programs I ever wrote was an implementation of Conway’s Game of Life, having learned about it from Martin Gardner’s Mathematical Games column in Scientific American. I remember that on my old Atari 400, it took a couple of seconds at least per generation, at a resolution of just 40×20 or so. Because of the limited memory (and small size of the display) I decided a 96×64 resolution. There is just enough memory to handle two full sized bit buffers. With a bit more work, I could make it work at the full resolution of the OLED display, but you need to do some of the updates in place, which makes it a bit harder.

//
// a tiny implementation of Conway's Life 
// written by Mark VandeWettering (brainwagon@gmail.com)
// http://brainwagon.org
//

#include "U8glib.h"

#define XSIZE           (128-32)
#define XSIZE_UINT8     (XSIZE/8)
#define YSIZE           (64)

uint8_t current[YSIZE][XSIZE_UINT8] ;
uint8_t next[YSIZE][XSIZE_UINT8] ;

#define MODX(x)         (((x)+XSIZE)%XSIZE)
#define MODY(y)         (((y)+YSIZE)%YSIZE)

#define ISSET(a, x, y) \
        (a[MODY(y)][MODX(x)/8] & (0x1<<(MODX(x)&7)))

#define SET(a, x, y) \
        a[MODY(y)][MODX(x)/8] |= (0x1<<(MODX(x)&7))

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); 

void
init()
{
    int i, j ;
    for (j=0; j<YSIZE; j++)
        for (i=0; i<XSIZE_UINT8; i++)
            current[j][i] = random(256) ;
}

void 
setup()
{
    randomSeed(analogRead(0));
    init() ;
}

void
draw()
{
    u8g.setDefaultBackgroundColor() ;
    u8g.drawBox(0, 0, 128, 64) ;
    u8g.setDefaultForegroundColor() ;
    u8g.drawXBM(16, 0, XSIZE, YSIZE, (const uint8_t *) next) ;
}

void
loop()
{
    int i, j ;

    memset((void *) next, 0, sizeof(next)) ;
    for (j=0; j<YSIZE; j++) {
        for (i=0; i<XSIZE; i++) {
            int sum = 0 ;
            if (ISSET(current, i-1, j-1)) sum ++ ;
            if (ISSET(current, i  , j-1)) sum ++ ;
            if (ISSET(current, i+1, j-1)) sum ++ ;
            if (ISSET(current, i-1, j  )) sum ++ ;
            if (ISSET(current, i+1, j  )) sum ++ ;
            if (ISSET(current, i-1, j+1)) sum ++ ;
            if (ISSET(current, i  , j+1)) sum ++ ;
            if (ISSET(current, i+1, j+1)) sum ++ ;

            if (ISSET(current, i, j)) {
                if (sum == 2 || sum == 3)
                    SET(next, i, j) ;
            } else {
                if (sum == 3)
                    SET(next, i, j) ;
            }
        }
    }
    
    memcpy((void *) current, (void *) next, sizeof(current)) ;

    u8g.firstPage() ;
    do {
        draw() ;
    } while (u8g.nextPage()) ;
}

It works! It initializes the display to random values, and then runs Life. If the display gets boring, just hit the result button….


Comments

Comment from Dak
Time 3/24/2015 at 2:39 pm

Funny parallels: one of the first programs I wrote was Life. In, ah, Fortran. Had a loop detector, too.