Klausj
Published © GPL3+

Game of Life on a small TFT display

In 1970, John Conway developed the rules for what he called a game for zero players, and in theory you need a board of infinity size.

AdvancedFull instructions provided1 hour132
Game of Life on a small TFT display

Things used in this project

Story

Read more

Code

Library for using TFT-1.8" (ST7735) also for UNO R4

Arduino
Replacement for library <TFT.h>. Extract and move to your library folder.
No preview (download only).

Game of Life on a small TFT for UNO R3 or R4

Arduino
Select configuration using jumpers at A0 - A5
/*
  Game of Life
  
  for ARDUINO UNO R3 and R4
  -------------------------

  resolution: 53 x 42 cells

  r-Pentomino
  four Gliders
  Spaceship
  gliderGun
  selected by jumper on A0, A1, A2, A3, A4, A5
*/

#ifdef ARDUINO_UNOR4_MINIMA

// my library
#include <TFT18_R4.h>
#define cs 10
#define rst 8
#define dc 9

TFT18_R4 tft = TFT18_R4();  //cs, dc, rst);

#else
// UNO R3

// the standard library:
#include <TFT.h>
#define cs  10
#define rst  8
#define dc   9

TFT tft = TFT(cs, dc, rst);

#endif

word ST77XX_BLACK = 0;
word ST77XX_GRAY  = 0x4208;
word ST77XX_RED   = 0xF800;
word ST77XX_WHITE = 0xFFFF;
byte w, h, m, n, sq;
const byte s = 3;
const byte W = 7 * 8;

// space for x = 7 * 8 = 56, y = 42
byte the_Board[7][42];
byte anyChange[7][42];
const byte select0 = A0;
const byte select1 = A1;
const byte select2 = A2;
const byte select3 = A3;
const byte select4 = A4;
const byte select5 = A5;
const int DT = 200;

const int r_Pentomino[] = { 0b10, 0b111, 0b1,
                            -1
                          };
const int glider_NW[] = { 0b10, 0b11, 0b101,
                          -1
                        };
const int glider_SW[] = { 0b10, 0b110, 0b101,
                          -1
                        };
const int glider_NE[] = { 0b101, 0b11, 0b10,
                          -1
                        };
const int glider_SE[] = { 0b101, 0b110, 0b10,
                          -1
                        };
const int fourtyTWO[] = { 0b111, 0b100, 0b11111, 0, 0,
                          0b11101, 0b10101, 0b10111,
                          -1
                        };
const int LWSS[] = { 0b1010, 1, 1, 0b1001, 0b111,
                     -1
                   };
const int glider_Gun[] = { 0b000110000,
                           0b000110000,
                           0, 0, 0, 0, 0, 0, 0, 0,
                           0b001110000,
                           0b010001000,
                           0b100000100,
                           0b100000100,
                           0b000100000,
                           0b010001000,
                           0b001110000,
                           0b000100000,
                           0, 0,
                           0b000011100,
                           0b000011100,
                           0b000100010,
                           0b000000000,
                           0b001100011,
                           0, 0, 0, 0, 0, 0, 0, 0, 0,
                           0b000001100,
                           0b000001100,
                           -1
                         };

void setup(void) {
  Serial.begin(9600);
  Serial.println(F(__FILE__));

#ifdef ARDUINO_UNOR4_MINIMA
  tft.begin();
  tft.setRotation(1);
  w = 160;
  h = 128;
  sq = s - 2;
#else
  tft.initR(INITR_BLACKTAB);
  tft.setRotation(3);
  w = tft.width();
  h = tft.height();
  sq = s - 1;
#endif

  tft.fillScreen(ST77XX_GRAY);


  m = w / s;
  n = h / s;
  for (byte i = 0; i < m; i++)
    for (byte j = 0; j < n; j++)
      setBit(i, j, 0);
  pinMode(select0, INPUT_PULLUP);
  pinMode(select1, OUTPUT);
  pinMode(select2, INPUT_PULLUP);
  pinMode(select3, OUTPUT);
  pinMode(select4, INPUT_PULLUP);
  pinMode(select5, INPUT_PULLUP);
  if (digitalRead(select0) == 0)
    setObject(26, 21, r_Pentomino);
  else if (digitalRead(select2) == 0) {
    setObject(43, 37, glider_NW);
    setObject(42, 2, glider_SW);
    setObject(8, 36, glider_NE);
    setObject(9, 2, glider_SE);
  }
  else if (digitalRead(select4) == 0) {
    setObject(2, 14, LWSS);
    setObject(1, 20, LWSS);
    setObject(1, 26, LWSS);
  }
  else if (digitalRead(select5) == 0)
    setObject(10, 10, fourtyTWO);
  else setObject(2, 2, glider_Gun);
  setChanged(255); // force drawing:
  // show start configuration and wait:
  showBoard();
  delay(1000);
}

void loop() {
  long t = millis() + DT;
  byte line0[m], line1[m], line2[m];
  board2line(line0, 0);
  board2line(line1, 1);
  // set all elements to unchanged:
  setChanged(0);
  // ---------------------------------------
  for (byte y = 1; y < n; y++) {
    board2line(line2, y + 1);
    if (y < n - 1)
      for (byte x = 1; x < m - 1; x++) {
        // calculate line1[]:
        // --- add the 8 neighbours --- :
        byte c = -line1[x]; // <-- this element
        // this one plus eight neighbours:
        for (byte k = x - 1; k <= x + 1; k++) {
          c = c + line0[k];
          c = c + line1[k];
          c = c + line2[k];
        }
        // --- Auswertung --- :
        switch (c) {
          case 2: break; // remains
          case 3:
            setBit(x, y, 1); // newborn or remaining
            if (line1[x] == 0) markAsChanged(x, y); // newborn
            break;
          default:
            setBit(x, y, 0); // dies
            if (line1[x]) markAsChanged(x, y);

        }
      }
    // copy line 2 to the_Board, line1 to line0, line2 to line1
    for (byte j = 0; j < m; j++) {
      line0[j] = line1[j];
      line1[j] = line2[j];
    }
  }
  // ------------------------------------------
  showBoard();
  if (!changesDetected()) {
    delay(1000);
    // if nothing happens just restart:
#ifdef ARDUINO_UNOR4_MINIMA
    //
#else
    asm("jmp 0");
#endif
  }

  //while (millis() < t);
}

void setChanged(byte v) {
  for (byte x = 0; x < 7; x++)
    for (byte y = 0; y < 42; y++)
      anyChange[x][y] = v;
}

boolean changesDetected() {
  int b = 0;
  for (byte x = 0; x < 7; x++)
    for (byte y = 0; y < 42; y++)
      b = b + anyChange[x][y];
  if (b > 0) return 1;
  return 0;
}

void setObject(byte x, byte y, const int feld[]) {
  int i = 0;
  int c = feld[0];
  while (c != -1) {
    for (byte j = 0; c > 0; j++) {
      setBit(x + i, y + j, c & 1);
      c = c / 2;
    }
    c = feld[++i];
  }
}

//-------------------------------------
// time-consuming procedures:
void setBit(byte x, byte y, byte z) {
  byte xIdx = x / 8;
  byte mask = 1 << (x & 7);
  if (z) the_Board[xIdx][y] = the_Board[xIdx][y] | mask;
  else the_Board[xIdx][y] = the_Board[xIdx][y] & ~mask;
}

byte getBit(byte x, byte y) {
  byte xIdx = x / 8;
  byte mask = 1 << (x & 7);
  byte b = the_Board[xIdx][y] & mask;
  if (b)return 1;
  return 0;
}

void markAsChanged(byte x, byte y) {
  byte xIdx = x / 8;
  byte mask = 1 << (x & 7);
  anyChange[xIdx][y] = anyChange[xIdx][y] | mask;
}

boolean getChange(byte x, byte y) {
  byte xIdx = x / 8;
  byte mask = 1 << (x & 7);
  byte b = anyChange[xIdx][y] & mask;
  if (b) return 1;
  return 0;
}
//------------------------------------

void board2line(byte line[W], byte y) {
  for (byte i = 0; i < m; i++)
    line[i] = getBit(i, y);
}

void showBoard() {
  for (byte i = 0; i < m; i++)
    for (byte j = 0; j < n; j++)
      if (getChange(i, j))
        placeRect(i, j, getBit(i, j));
}

void placeRect(byte x, byte y, byte b) {
  word c = b ? ST77XX_RED : ST77XX_WHITE;
  tft.fillRect(s * x, s * y, sq, sq, c);
}

Game of Life only for UNO R4

Arduino
same as previous
/*
  Game of Life, the best!!!
  due to memory requirements

  this version is only for UNO-R4
  -------------------------------

  r-Pentomino
  four Gliders
  Spaceship
  gliderGun
  selected by jumper on A0, A1, A2, A3, A4, A5

  Adafruit library cannot be used,
  it only shows an empty white screen
  the standard library does not work for R4
  (that lib unnecessarily includes <avr/io.h>)
  so I had to develop my own library TFT18_R4.
  difference to gameOfLife_TFT_R4only:
  array anyChange to avoid unnecessary draws
  draws 2x2px
  times:
  R4only  31831 ms
  R4only_a  7457 ms
  R$only_b  11079 ms
*/

#include <TFT18_R4.h>
#define cs 10
#define rst 8
#define dc 9
TFT18_R4 tft = TFT18_R4();  //cs, dc, rst);



word ST7735_GRAY = 0x4208;
const byte s = 3;
const byte W = 53;
const byte H = 42;

const byte sq = s - 1;
boolean theBoard[W][H];
boolean anyChange[W][H]; // <-----
const byte select0 = A0;
const byte select1 = A1;
const byte select2 = A2;
const byte select3 = A3;
const byte select4 = A4;
const byte select5 = A5;
const byte DT = 200;
int iter;
long t0;

const int r_Pentomino[] = { 0b10, 0b111, 0b1,
                            -1
                          };
const int glider_NW[] = { 0b10, 0b11, 0b101,
                          -1
                        };
const int glider_SW[] = { 0b10, 0b110, 0b101,
                          -1
                        };
const int glider_NE[] = { 0b101, 0b11, 0b10,
                          -1
                        };
const int glider_SE[] = { 0b101, 0b110, 0b10,
                          -1
                        };
const int fourtyTWO[] = { 0b111, 0b100, 0b11111, 0, 0,
                          0b11101, 0b10101, 0b10111,
                          -1
                        };
const int LWSS[] = { 0b1010, 1, 1, 0b1001, 0b111,
                     -1
                   };
const int glider_Gun[] = { 0b000110000,
                           0b000110000,
                           0, 0, 0, 0, 0, 0, 0, 0,
                           0b001110000,
                           0b010001000,
                           0b100000100,
                           0b100000100,
                           0b000100000,
                           0b010001000,
                           0b001110000,
                           0b000100000,
                           0, 0,
                           0b000011100,
                           0b000011100,
                           0b000100010,
                           0b000000000,
                           0b001100011,
                           0, 0, 0, 0, 0, 0, 0, 0, 0,
                           0b000001100,
                           0b000001100,
                           -1
                         };

void setup(void) {
  long t = millis() + 2000;
  Serial.begin(9600);
  while (!Serial & millis() < t)
    ;
  Serial.println(__FILE__);
  Serial.println(__TIME__);
  tft.begin();
  Serial.println("using my library <TFT18_R4.h>");
  tft.setRotation(3);
  tft.fillScreen(ST7735_GRAY);
  pinMode(select0, INPUT_PULLUP);
  pinMode(select1, OUTPUT);
  pinMode(select2, INPUT_PULLUP);
  pinMode(select3, OUTPUT);
  pinMode(select4, INPUT_PULLUP);
  pinMode(select5, INPUT_PULLUP);
  for (byte x = 0; x < W; x++)
    for (byte y = 0; y < H; y++) {
      theBoard[x][y] = false;
      anyChange[x][y] = true;
    }
  if (digitalRead(select0) == 0)
    setObject(26, 21, r_Pentomino);
  else if (digitalRead(select2) == 0) {
    setObject(43, 37, glider_NW);
    setObject(42, 2, glider_SW);
    setObject(8, 36, glider_NE);
    setObject(9, 2, glider_SE);
  }
  else if (digitalRead(select4) == 0) {
    setObject(2, 14, LWSS);
    setObject(1, 20, LWSS);
    setObject(1, 26, LWSS);
  }
  else if (digitalRead(select5) == 0)
    setObject(10, 10, fourtyTWO);
  else setObject(2, 2, glider_Gun);
  // show start configuration and wait:
  showBoard();
  delay(2000);
  pinMode(LED_RX, OUTPUT);
  t0 = millis();
}

void loop() {
  digitalWrite(LED_RX, ++iter & 1);
  long t = millis() + DT;
  byte previous[W][H];
  memcpy(previous, theBoard, (int)W * H);
  for (byte y = 1; y < H - 1; y++) {
    for (byte x = 1; x < W - 1; x++) {
      anyChange[x][y] = false;
      // --- add the 8 neighbours --- :
      byte c = previous[x - 1][y - 1] + previous[x - 1][y] + previous[x - 1][y + 1] + previous[x][y - 1] + previous[x][y + 1] + previous[x + 1][y - 1] + previous[x + 1][y] + previous[x + 1][y + 1];
      // --- apply Conway's rules: --- :
      switch (c) {
        case 2:
          break;  // nothing to be done
        // newborn or remaining
        case 3:
          theBoard[x][y] = true;
          anyChange[x][y] = true;
          break;
        // dies
        default:
          if (theBoard[x][y]) anyChange[x][y] = true;
          theBoard[x][y] = false;
      }
    }
  }
  // ------------------------------------------
  showBoard();
  // if you want a delay:
  //while (millis() < t)
  ;
  /*
    if (iter > 100) {
    Serial.println(millis() - t0);
    while (true);
    }
  */
}

void setObject(byte x, byte y, const int feld[]) {
  int i = 0;
  int c = feld[0];
  while (c != -1) {
    for (byte j = 0; c > 0; j++) {
      // setBit(x + i, y + j, c & 1);
      theBoard[x + i][y + j] = c & 1;
      c = c / 2;
    }
    c = feld[++i];
  }
}

void showBoard() {
  for (byte i = 0; i < W; i++)
    for (byte j = 0; j < H; j++)
      if (anyChange[i][j])
        placeRect(i, j, theBoard[i][j]);
}

void placeRect(byte x, byte y, byte b) {
  word c = b ? ST7735_RED : ST7735_WHITE;
  tft.fillRect(s * x, s * y, sq, sq, c);
}

Game of Life for good old ARDUINO DUE

Arduino
same as previous
/*
  Game of Life
  due to memory requirements

  this version is only for ARDUINO-DUE
  ------------------------------------

  maximum resolution 160 x 128 pixels

  r-Pentomino
  four Gliders
  Spaceship
  gliderGun
  selected by jumper on A0, A1, A2, A3, A4, A5
*/

// for ARDUINO UNO R3 and ARDUINO DUE R3
// you can use the standard TFT library.
// The ARDUINO DUE was introduced in 2012.
#include <TFT.h>
#define cs  10
#define rst  8
#define dc   9
TFT tft = TFT(cs, dc, rst);

word ST7735_GRAY = 0x4208;
const byte W = 160;
const byte H = 128;

boolean theBoard[W][H];
boolean anyChange[W][H];
const byte select0 = A0;
const byte select1 = A1;
const byte select2 = A2;
const byte select3 = A3;
const byte select4 = A4;
const byte select5 = A5;
const byte DT = 200;
int iter;

const int r_Pentomino[] = { 0b10, 0b111, 0b1,
                            -1
                          };
const int glider_NW[] = { 0b10, 0b11, 0b101,
                          -1
                        };
const int glider_SW[] = { 0b10, 0b110, 0b101,
                          -1
                        };
const int glider_NE[] = { 0b101, 0b11, 0b10,
                          -1
                        };
const int glider_SE[] = { 0b101, 0b110, 0b10,
                          -1
                        };
const int fourtyTWO[] = { 0b111, 0b100, 0b11111, 0, 0,
                          0b11101, 0b10101, 0b10111,
                          -1
                        };
const int LWSS[] = { 0b1010, 1, 1, 0b1001, 0b111,
                     -1
                   };
const int glider_Gun[] = { 0b000110000,
                           0b000110000,
                           0, 0, 0, 0, 0, 0, 0, 0,
                           0b001110000,
                           0b010001000,
                           0b100000100,
                           0b100000100,
                           0b000100000,
                           0b010001000,
                           0b001110000,
                           0b000100000,
                           0, 0,
                           0b000011100,
                           0b000011100,
                           0b000100010,
                           0b000000000,
                           0b001100011,
                           0, 0, 0, 0, 0, 0, 0, 0, 0,
                           0b000001100,
                           0b000001100,
                           -1
                         };

void setup(void) {
  Serial.begin(9600);
  Serial.println(__FILE__);
  Serial.println(__TIME__);
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ST7735_GRAY);
  pinMode(select0, INPUT_PULLUP);
  pinMode(select1, OUTPUT);
  pinMode(select2, INPUT_PULLUP);
  pinMode(select3, OUTPUT);
  pinMode(select4, INPUT_PULLUP);
  pinMode(select5, INPUT_PULLUP);
  for (byte x = 0; x < W; x++)
    for (byte y = 0; y < H; y++) {
      theBoard[x][y] = false;
      anyChange[x][y] = true;
    }
  if (digitalRead(select0) == 0)
    setObject(80, 64, r_Pentomino);
  else if (digitalRead(select2) == 0) {
    setObject(144, 124, glider_NW);
    setObject(143, 2, glider_SW);
    setObject(29, 123, glider_NE);
    setObject(28, 2, glider_SE);
  }
  else if (digitalRead(select4) == 0) {
    setObject(2, 57, LWSS);
    setObject(1, 64, LWSS);
    setObject(1, 72, LWSS);
  }
  else if (digitalRead(select5) == 0)
    setObject(80, 30, fourtyTWO);
  else setObject(2, 2, glider_Gun);
  // show start configuration and wait:
  showBoard();
  delay(2000);
}

void loop() {
  long t = millis() + DT;
  byte previous[W][H];
  memcpy(previous, theBoard, (int)W * H);
  for (byte y = 1; y < H - 1; y++) {
    for (byte x = 1; x < W - 1; x++) {
      anyChange[x][y] = false;
      // --- add the 8 neighbours --- :
      byte c = previous[x - 1][y - 1] + previous[x - 1][y] + previous[x - 1][y + 1] + previous[x][y - 1] + previous[x][y + 1] + previous[x + 1][y - 1] + previous[x + 1][y] + previous[x + 1][y + 1];
      // --- apply Conway's rules: --- :
      switch (c) {
        case 2:
          break;  // nothing to be done
        // newborn or remaining
        case 3:
          theBoard[x][y] = true;
          anyChange[x][y] = true;
          break;
        // dies
        default:
          if (theBoard[x][y]) anyChange[x][y] = true;
          theBoard[x][y] = false;
      }
    }
  }
  // ------------------------------------------
  showBoard();
  // if you want a delay:
  //while (millis() < t)
  ;
}

void setObject(byte x, byte y, const int feld[]) {
  int i = 0;
  int c = feld[0];
  while (c != -1) {
    for (byte j = 0; c > 0; j++) {
      theBoard[x + i][y + j] = c & 1;
      c = c / 2;
    }
    c = feld[++i];
  }
}

void showBoard() {
  for (byte i = 0; i < W; i++)
    for (byte j = 0; j < H; j++)
      if (anyChange[i][j]) {
        byte b = theBoard[i][j];
        word c = b ? ST7735_RED : ST7735_WHITE;
        tft.drawPixel(i, j, c);
      }
}

Credits

Klausj
89 projects • 8 followers

Comments