Without a lot more explanation, I did a bit more work on my implementation of Conway’s life, reducing the overhead so I can implement the full 128×64 bit resolution of the OLED display. I also hardcoded an initial pattern: the classic Gosper glider gun. It runs at about 1.625 frames/second, so a new glider is emitted every 18 seconds or so. Eventually, they wrap around and destroy the glider gun by colliding with it. I can think of a few more optimizations that are easy, but I doubt they are very important.
Upgraded my #arduino sketch to support the full 128×64 res, and coded in a Gosper glider gun that will shoot itself. pic.twitter.com/WJbdZTAbcu
— Mark VandeWettering (@brainwagon) March 23, 2015
[sourcecode lang=”cpp”]
#include "U8glib.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);
#define XSIZE (128)
#define XMASK (127)
#define XSIZE_UINT8 (XSIZE/8)
#define YSIZE (64)
#define YMASK (63)
// We use the extra lines as buffer space…
uint8_t c[YSIZE][XSIZE_UINT8] ;
#define MODX(x) (((x)+XSIZE)&XMASK)
#define MODY(y) (((y)+YSIZE)&YMASK)
#define ISSET(a, x) \
(a[MODX(x)>>3] & (0x1<<(MODX(x)&7)))
#define SET(a, x) \
a[MODX(x)>>3] |= (0x1<<(MODX(x)&7))
#define SETXY(a, x, y) \
a[y][x>>3] |= (0x1<<((x)&7))
#define CLR(a, x) \
a[MODX(x)>>3] &= ~(0x1<<(MODX(x)&7))
void
initrandom()
{
int i, j ;
for (j=0; j<YSIZE; j++)
for (i=0; i<XSIZE_UINT8; i++)
c[j][i] = random(256) ;
}
void
initgun()
{
memset(c, 0, sizeof(c)) ;
SETXY(c, 70, 28); SETXY(c, 68, 29); SETXY(c, 70, 29); SETXY(c, 58, 30);
SETXY(c, 59, 30); SETXY(c, 66, 30); SETXY(c, 67, 30); SETXY(c, 80, 30);
SETXY(c, 81, 30); SETXY(c, 57, 31); SETXY(c, 61, 31); SETXY(c, 66, 31);
SETXY(c, 67, 31); SETXY(c, 80, 31); SETXY(c, 81, 31); SETXY(c, 46, 32);
SETXY(c, 47, 32); SETXY(c, 56, 32); SETXY(c, 62, 32); SETXY(c, 66, 32);
SETXY(c, 67, 32); SETXY(c, 46, 33); SETXY(c, 47, 33); SETXY(c, 56, 33);
SETXY(c, 60, 33); SETXY(c, 62, 33); SETXY(c, 63, 33); SETXY(c, 68, 33);
SETXY(c, 70, 33); SETXY(c, 56, 34); SETXY(c, 62, 34); SETXY(c, 70, 34);
SETXY(c, 57, 35); SETXY(c, 61, 35); SETXY(c, 58, 36); SETXY(c, 59, 36);
}
void
setup()
{
#ifdef TIME_ME
Serial.begin(9600) ;
#endif
randomSeed(analogRead(A0));
initgun() ;
}
void
draw()
{
u8g.setDefaultBackgroundColor() ;
u8g.drawBox(0, 0, 128, 64) ;
u8g.setDefaultForegroundColor() ;
u8g.drawXBM(0, 0, XSIZE, YSIZE, (const uint8_t *) c) ;
}
void
line(const uint8_t *a, const uint8_t *b, const uint8_t *c,
uint8_t *out)
{
int i ;
for (i=0; i<XSIZE; i++) {
int sum = 0 ;
if (ISSET(a, i-1)) sum ++ ;
if (ISSET(a, i )) sum ++ ;
if (ISSET(a, i+1)) sum ++ ;
if (ISSET(b, i-1)) sum ++ ;
if (ISSET(b, i+1)) sum ++ ;
if (ISSET(c, i-1)) sum ++ ;
if (ISSET(c, i )) sum ++ ;
if (ISSET(c, i+1)) sum ++ ;
if (ISSET(b, i)) {
if (sum == 2 || sum == 3)
SET(out, i) ;
else
CLR(out, i) ;
} else {
if (sum == 3)
SET(out, i) ;
else
CLR(out, i) ;
}
}
}
void
loop()
{
int j, op ;
uint8_t obuf[2][XSIZE_UINT8] ;
uint8_t otmp[XSIZE_UINT8] ;
#ifdef TIME_ME
unsigned long time_start, time_stop ;
time_start = millis() ;
#endif
line(c[YSIZE-1], c[0], c[1], obuf[0]) ;
line(c[0], c[1], c[2], obuf[1]) ;
op = 0 ;
// We need this for the wrap around.
memcpy(otmp, c[0], XSIZE_UINT8) ;
for (j=2; j<YSIZE-1; j++) {
memcpy(c[j-2], obuf[op], XSIZE_UINT8) ;
line(c[MODY(j-1)], c[j], c[MODY(j+1)], obuf[op]) ;
op = 1 – op ;
}
// We still have three lines we need to copy back…
// And one more buffer to compute with line()
memcpy(c[YSIZE-3], obuf[op], XSIZE_UINT8) ;
line(c[YSIZE-2], c[YSIZE-1], otmp, obuf[op]) ;
op = 1 – op ;
memcpy(c[YSIZE-2], obuf[op], XSIZE_UINT8) ;
op = 1 – op ;
memcpy(c[YSIZE-1], obuf[op], XSIZE_UINT8) ;
// compute one generation..
u8g.firstPage() ;
do {
draw() ;
} while (u8g.nextPage()) ;
#ifdef TIME_ME
time_stop = millis() ;
Serial.print(time_stop – time_start) ;
Serial.println() ;
#endif
}
[/sourcecode]