Skip to content

Instantly share code, notes, and snippets.

@spitty
Created December 18, 2016 21:10
Show Gist options
  • Select an option

  • Save spitty/0b333116ec071230dad179f9a551d7c5 to your computer and use it in GitHub Desktop.

Select an option

Save spitty/0b333116ec071230dad179f9a551d7c5 to your computer and use it in GitHub Desktop.
/*
This Arduino sketch shows QR-code with word 'AMAZING' and a heart
in the right side of GLCD-display.
Used parts:
- Library openGLCD https://bitbucket.org/bperrybap/openglcd/wiki/Home
- example life.pde "John Conway's Game of Life for openGLCD"
Author: Maxim Logunov [email protected]
Date: 2016-12-17
*/
#include <openGLCD.h>
/********* user configurable defines ************/
#define CELL_SIZE 4 // the height and width of each cell
#define DELAY 50 // number of milliseconds delay between the display of each generation
/******* end of user configurable defines ********/
#define ROWS (DISPLAY_HEIGHT / CELL_SIZE)
#define COLUMNS (DISPLAY_WIDTH / 2 / CELL_SIZE)
// Modification:
// ^ use only half of display
//
#define BYTES_PER_COLUMN ((COLUMNS + 7)/ 8) //this is the number of bytes needed to hold a column
#define MAX_ITERATIONS 500 // iterations are terminated if this number is exceeded
#define STABLE_GENERATIONS 4 // must be at least 2, this is the number of back generations checked for stability
#define MIN_OBJECTS 2 // minmum number of objects to be seeded by the random generater
#define MAX_OBJECTS 4 // maximum number of objects for random seed generator
#define MARGIN 4 // minimum distance from screen edge for randomly seeded objects
#define BITVAL(_pos) (1<< (7-(_pos % 8))) // macro to get a bitfield value
#define ALIVE true
#define DEAD false
/* ======== Life Objects ========
* these objects can be displayed on the Arduino life screen
* to add an object, create and initialise a structure following the examples here
* add a reference to your structure to the object table, name your object by adding to the enum list
*/
// structure template for life objects
typedef struct {
byte height ;
byte width ;
byte bitfield[];
} object_t, *object_ptr_t;
// definitions and initialisations for all defined objects here
struct {
byte height ;
byte width ;
byte bitfield[2];
} static table = {2,4, { 0b10010000 , // * *
0b11110000 } }; // ****
struct {
byte height ;
byte width ;
byte bitfield[3];
} static glider = {3,3, { 0b00100000 , // *
0b10100000 , // * *
0b01100000 }}; // **
#ifdef NOTUSED
struct {
byte height ;
byte width ;
byte bitfield[3];
} static glider2 = {3,3, { 0b10100000 , // * *
0b11000000 , // **
0b11110000 }}; // ****
#endif
struct {
byte height ;
byte width ;
byte bitfield[4];
} static loaf = {4,4, { 0b01100000 , // **
0b10010000 , // * *
0b01010000 , // * *
0b00100000 }}; // *
struct {
byte height ;
byte width ;
byte bitfield[3];
} static ship = {3,3, { 0b11000000 , // **
0b10100000 , // * *
0b01100000 }}; // **
struct {
byte height ;
byte width ;
byte bitfield[4];
} static behive = {4,3, { 0b01000000 , // *
0b10100000 , // * *
0b10100000 , // * *
0b01000000}}; // *
struct {
byte height ;
byte width ;
byte bitfield[1];
} static blinker = {1,3, { 0b11100000 }}; // ***
struct {
byte height ;
byte width ;
byte bitfield[2];
} static block = {2,2, { 0b11000000 , // **
0b11000000}}; // **
// place all the objects in the object table
object_ptr_t objectTable[] = {
(object_ptr_t)&table,
(object_ptr_t)&glider,
(object_ptr_t)&loaf,
(object_ptr_t)&ship,
(object_ptr_t)&behive,
(object_ptr_t)&blinker,
(object_ptr_t)&block
};
#define OBJECT_COUNT (sizeof( objectTable/ sizeof(object_ptr_t)))
// enumerate all the objects, add any new objects to the end of this enum (note case difference )
// the life program references all objects using the names in this list
enum {Table,Glider,Loaf,Ship,Behive,Blinker,Block};
byte generations[STABLE_GENERATIONS][ROWS][BYTES_PER_COLUMN]; // array used to calculate, draw and check if iterations are changing
// This part descripbes QR-code
#define RECTSIZE 21
#define PXSIZE 2
const int bits[RECTSIZE][RECTSIZE] = {{0,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,0,0},
{0,1,1,1,1,1,0,1,1,0,1,1,0,1,0,1,1,1,1,1,0},
{0,1,0,0,0,1,0,1,1,0,1,1,1,1,0,1,0,0,0,1,0},
{0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0},
{0,1,0,0,0,1,0,1,1,1,1,0,1,1,0,1,0,0,0,1,0},
{0,1,1,1,1,1,0,1,1,0,1,0,0,1,0,1,1,1,1,1,0},
{0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,1,1,0,1,1,0},
{1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,1,0,1},
{0,1,1,1,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,1},
{1,0,1,1,1,0,1,1,0,0,0,0,0,1,1,1,1,1,0,1,1},
{0,0,0,1,0,0,0,1,1,0,0,0,1,1,1,1,0,1,0,0,1},
{1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,0,1,0,0,1,1},
{0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1},
{0,1,1,1,1,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,0},
{0,1,0,0,0,1,0,1,0,1,1,1,1,0,1,0,1,0,0,1,0},
{0,1,0,0,0,1,0,1,1,1,0,0,1,1,0,0,1,0,1,0,1},
{0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0,1,0},
{0,1,1,1,1,1,0,1,1,0,0,1,1,0,0,0,1,1,1,0,0},
{0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,1,0,0,0,1,0}};
void setup(){
GLCD.Init(); // initialise the library, non inverted writes pixels onto a clear screen
GLCD.SelectFont(System5x7);
GLCD.DrawString("Life", gTextfmt_center, gTextfmt_center);
delay(1000);
// ^ delay decreased
seed(3);
}
void loop (void){
GLCD.ClearScreen();
// Draw QR-code
for (int i = 0; i < RECTSIZE; i++) {
for (int j = 0; j < RECTSIZE; j++) {
uint8_t color = (bits[i][j] == 0 ? PIXEL_ON : PIXEL_OFF);
GLCD.FillRect(75 + j * PXSIZE, 11 + i * PXSIZE, 2, 2, color);
}
}
unsigned int i = generate();
GLCD.CursorTo(0,0);
GLCD.print(i);
GLCD.print(" iterations");
delay(2000);
seed(1); // use random seed
}
unsigned int generate(){
unsigned int iteration = 0;
byte thisGeneration,nextGeneration;
thisGeneration = nextGeneration = 0;
//display the initial array
for(int row = 0; row < ROWS; row++)
{
for(int column = 0; column < COLUMNS; column++)
{
if(isAlive(thisGeneration,row,column))
GLCD.DrawRoundRect(column * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE, CELL_SIZE/2, BLACK) ;
else
GLCD.DrawRoundRect(column * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE, CELL_SIZE/2, WHITE) ;
}
}
delay(1500); // show the seeded condition a little longer
do{
thisGeneration = iteration % STABLE_GENERATIONS;
nextGeneration = (thisGeneration+1) % STABLE_GENERATIONS;
delay(DELAY);
memset(generations[nextGeneration],0,sizeof(generations[0])); // clear the array for the next generation
//see who lives and who dies
for(int row = 0; row < ROWS; row++){
for(int column = 0; column < COLUMNS; column++){
boolean cell = isAlive(thisGeneration,row,column);
byte n = getNeighborCount(thisGeneration,row,column);
if(cell == DEAD){
if(n==3) {
setLife(nextGeneration,row,column,ALIVE); // birth
GLCD.DrawRoundRect(column * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE, CELL_SIZE/2, BLACK) ;
}
}
else{
if(n==2 || n==3) {
setLife(nextGeneration,row,column,ALIVE); //survival
// No need to draw cell as it is already there.
} else {
setLife(nextGeneration,row,column,DEAD); //death
GLCD.DrawRoundRect(column * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE, CELL_SIZE/2, WHITE) ;
}
}
}
}
}
while(isStable(thisGeneration) == false && ++iteration < MAX_ITERATIONS ) ;
return iteration;
}
void seed(int seedValue){
// a hard coded seed is used if seedvalue is zero, otherwise a random seed is used
memset(generations,0,sizeof(generations)); // clear all generation arrays
if(seedValue == 0){
// load default objects, arguments are offsets from the center of the screen
loadObject(Table,2,-9); // put a table object 2 down from the center and 9 columns left or center
loadObject(Glider, -5,-9); // put a glider 5 rows above the center and 9 columns left of center
}
else{
// load some random objects and place at random positions
byte nbrObjects = random(MIN_OBJECTS,MAX_OBJECTS+1); // make a random number of objects
for(byte i=0; i < nbrObjects;i++){
byte obj = random(7); // OBJECT_COUNT);
int column = random(MARGIN - (COLUMNS/2), (COLUMNS/2) - MARGIN);
int row = random(MARGIN - (ROWS/2) , (ROWS/2) - MARGIN);
loadObject(obj,row,column);
}
}
}
void loadObject(void * object, char y, char x){
// object_ptr is the object to be loaded
// y and x are offsets from the center of the display
object_ptr_t object_ptr = (object_ptr_t)object;
byte row = (ROWS/2) + y;
byte column = (COLUMNS/2) + x;
for(byte ys = 0; ys < object_ptr->height; ys++ ){
for(byte xs=0; xs < object_ptr->width; xs++ ){
if( (row +ys < ROWS) && (column +xs < COLUMNS)){
// this code only works on widths up to 8 bits
boolean value = object_ptr->bitfield[ys] & BITVAL(xs) ;
setLife(0, row+ys, column+xs, value) ; // objects always loaded into the first array
}
}
}
}
void loadObject(int object, char y, char x){
loadObject(objectTable[object], y, x );
}
boolean isStable(byte thisGeneration){
// returns true if any two captured generations are the same
for(byte i=0; i < STABLE_GENERATIONS; i++)
if(i != thisGeneration)
if(memcmp(generations[thisGeneration], generations[i], sizeof(generations[0])) == 0)
return true;
return false;
}
boolean isAlive( byte generation, byte row, byte column){
byte b = generations[generation][row][column /8];
return b & BITVAL(column) ;
}
void setLife( byte generation, byte row, byte column, boolean state){
byte elem = column /8;
byte b = generations[generation][row][elem];
if(state != DEAD){
b = b | BITVAL(column);
}
else{
b &= ~(BITVAL(column)) ;
}
generations[generation][row][elem] = b;
}
byte getNeighborCount( byte generation, byte row, byte column){
byte count = 0;
for(byte d = 0;d < 8; d++)
if(isNeighborAlive(generation,row,column,d) != 0)
count++;
return count;
}
boolean isNeighborAlive( byte generation , byte row, byte column, byte dir){
byte nrow=row;
byte ncol=column;
if(dir == 7 || dir == 0 || dir == 1)
nrow--;
if(dir == 3 || dir == 4 || dir == 5)
nrow++;
if(dir == 5 || dir == 6 || dir == 7)
ncol--;
if(dir == 1 || dir == 2 || dir == 3)
ncol++;
if(ncol==255) ncol = COLUMNS - 1;
if(ncol==COLUMNS) ncol = 0;
if(nrow==255) nrow = ROWS - 1;
if(nrow==ROWS) nrow = 0;
return isAlive(generation,nrow,ncol);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment