Created
April 13, 2022 19:46
-
-
Save jonphilpott/e30dbd48e2bdf35c1f859df82a513bc3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// GAME OF LIFE KOSMO MODULE | |
// instead of walking the field and turning them into clock pulses, generate clock pulses based on the number of dead/alive cells | |
// | |
// split area into 4 zones, any time there is change in that area, create a clock pulse. | |
// pot on analog input to change the refresh rate | |
// double buffered | |
#define GOL_W (96) | |
#define GOL_H (32) | |
#define N_ALIVE_CELLS (20) | |
#define GOL_BYTE_SIZE ( ((GOL_W) * (GOL_H)) / 8 ) | |
#define MAX_GENS 128 | |
#define PULSE_CYCLES 1 | |
#define GOL_CYCLES 4 | |
#define GOL_SPEED_INPUT A0 | |
#define CLK_SPEED_INPUT A1 | |
#define CLK_OUTPUT1 6 | |
#define CLK_OUTPUT2 7 | |
#define CLK_OUTPUT3 8 | |
#define CLK_OUTPUT4 9 | |
#define RATE_POT_PIN A0 | |
byte GAME_LIFE1[(GOL_BYTE_SIZE)]; | |
byte GAME_LIFE2[(GOL_BYTE_SIZE)]; | |
#include <avr/wdt.h> | |
#include <Adafruit_GFX.h> | |
#include <Adafruit_SSD1306.h> | |
#define SCREEN_WIDTH 128 // OLED display width, in pixels | |
#define SCREEN_HEIGHT 64 // OLED display height, in pixels | |
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) | |
// The pins for I2C are defined by the Wire-library. | |
// On an arduino UNO: A4(SDA), A5(SCL) | |
// On an arduino MEGA 2560: 20(SDA), 21(SCL) | |
// On an arduino LEONARDO: 2(SDA), 3(SCL), ... | |
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) | |
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 | |
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); | |
/************************************************************************** | |
* ACTIVITY BOX CODE | |
*/ | |
#define N_ACTIVITY_BOXES (4) | |
struct activity_box { | |
unsigned int x, y, w, h; | |
}; | |
struct activity_box activity_boxes[N_ACTIVITY_BOXES] = { | |
{2, 2, 12, 12}, | |
{20, 18, 12, 12}, | |
{40, 2, 12, 12}, | |
{60, 2, 12, 12} | |
}; | |
unsigned int activity_box_status[N_ACTIVITY_BOXES] = {0, 0, 0, 0}; | |
int check_box(struct activity_box *box) | |
{ | |
for (int y = box->y ; y < (box->y + box->h) ; y++) { | |
for (int x = box->x ; x < (box->x + box->w); x++) { | |
if (gol_get(x, y) == 0 && gol_get2(x, y)) { | |
return 1; | |
} | |
} | |
} | |
return 0; | |
} | |
void check_activity_boxes() | |
{ | |
for (int i = 0 ; i < N_ACTIVITY_BOXES ; i++) { | |
struct activity_box *box = &activity_boxes[i]; | |
activity_box_status[i] = check_box(box); | |
} | |
} | |
void draw_activity_boxes() | |
{ | |
for (int i = 0 ; i < N_ACTIVITY_BOXES ; i++) { | |
struct activity_box *box = &activity_boxes[i]; | |
display.drawRect(box->x, box->y, box->w, box->h, SSD1306_WHITE); | |
} | |
} | |
void shuffle_activity_boxes() | |
{ | |
for (int i = 0; i < N_ACTIVITY_BOXES ; i++) { | |
struct activity_box *box = &activity_boxes[i]; | |
box->h = 4+random(GOL_H-6); | |
box->w = 4+random(16); | |
box->y = (random(GOL_H - box->h)); | |
} | |
} | |
void draw_activity_box_statuses() | |
{ | |
for (int i = 0 ; i < N_ACTIVITY_BOXES ; i++) { | |
struct activity_box *box = &activity_boxes[i]; | |
unsigned sx = 100; | |
unsigned sy = 2 + (i * 16); | |
if (activity_box_status[i]) { | |
display.fillRect(sx, sy, 8, 14, SSD1306_WHITE); | |
} | |
} | |
} | |
void clear_box_statuses() | |
{ | |
for (int i = 0 ; i < N_ACTIVITY_BOXES ; i++) { | |
activity_box_status[i] = 0; | |
} | |
} | |
void output_pulses() | |
{ | |
digitalWrite(CLK_OUTPUT1, activity_box_status[0] ? HIGH : LOW); | |
digitalWrite(CLK_OUTPUT2, activity_box_status[1] ? HIGH : LOW); | |
digitalWrite(CLK_OUTPUT3, activity_box_status[2] ? HIGH : LOW); | |
digitalWrite(CLK_OUTPUT4, activity_box_status[3] ? HIGH : LOW); | |
} | |
void clear_pulses() | |
{ | |
digitalWrite(CLK_OUTPUT1, LOW); | |
digitalWrite(CLK_OUTPUT2, LOW); | |
digitalWrite(CLK_OUTPUT3, LOW); | |
digitalWrite(CLK_OUTPUT4, LOW); | |
} | |
/************************************************************************** | |
* GAME OF LIFE CODE | |
*/ | |
int gol_get(signed int x, signed int y) | |
{ | |
if (x < 0 || y < 0 || x >= GOL_W || y >= GOL_H) { | |
return 0; | |
} | |
int posn = (y * GOL_W) + x; | |
int byte_pos = posn / 8; | |
int bit_pos = posn % 8; | |
byte bit_mask = (1 << bit_pos); | |
return ((GAME_LIFE1[byte_pos] & bit_mask) ? 1 : 0); | |
} | |
int gol_get2(signed int x, signed int y) | |
{ | |
if (x < 0 || y < 0 || x >= GOL_W || y >= GOL_H) { | |
return 0; | |
} | |
int posn = (y * GOL_W) + x; | |
int byte_pos = posn / 8; | |
int bit_pos = posn % 8; | |
byte bit_mask = (1 << bit_pos); | |
return ((GAME_LIFE2[byte_pos] & bit_mask) ? 1 : 0); | |
} | |
void gol_set(signed int x, signed int y, int n) | |
{ | |
if (x < 0 || y < 0 || x > GOL_W || y > GOL_H) { | |
return; | |
} | |
int posn = (y * GOL_W) + x; | |
int byte_pos = posn / 8; | |
int bit_pos = posn % 8; | |
byte bit_mask = (1 << bit_pos); | |
if (n) { | |
GAME_LIFE2[byte_pos] |= bit_mask; | |
} | |
else { | |
bit_mask = ~bit_mask; | |
GAME_LIFE2[byte_pos] &= bit_mask; | |
} | |
} | |
void gol_buffer_swap() | |
{ | |
memcpy(GAME_LIFE1, GAME_LIFE2, sizeof(GAME_LIFE1)); | |
} | |
int neighbours(int x, int y) | |
{ | |
int cnt = gol_get(x - 1, y - 1) + gol_get(x, y - 1) + gol_get(x + 1, y - 1) + | |
gol_get(x - 1, y) + 0 + gol_get(x + 1, y) + | |
gol_get(x - 1, y + 1) + gol_get(x, y + 1) + gol_get(x + 1, y + 1); | |
return cnt; | |
} | |
unsigned int gol_run() | |
{ | |
unsigned char x = 0; | |
unsigned char y = 0; | |
int cnt = 0; | |
int cur = 0; | |
int nxt = 0; | |
memset(GAME_LIFE2, 0, sizeof(GAME_LIFE2)); | |
unsigned int alive = 0; | |
for (y = 0; y < GOL_H; y++) { | |
for (x = 0 ; x < GOL_W ; x++) { | |
cnt = neighbours(x, y); | |
cur = gol_get(x, y); | |
nxt = ((cnt | cur) == 3); | |
gol_set(x, y, nxt); | |
} | |
} | |
check_activity_boxes(); | |
gol_buffer_swap(); | |
return alive; | |
} | |
/************************************************************************** | |
* GAME OF LIFE CREATURES | |
*/ | |
// create creatures in buffer2, make sure you swap... | |
void place_glider(int x, int y) | |
{ | |
gol_set(x + 1, y, 1); | |
gol_set(x + 2, y + 1, 1); | |
gol_set(x + 3, y, 1); gol_set(x + 3, y + 1, 1); gol_set(x + 3, y + 2, 1); | |
} | |
void place_spaceship(int x, int y) | |
{ | |
gol_set(x + 1, y, 1); gol_set(x + 4, y, 1); | |
gol_set(x, y + 1, 1); | |
gol_set(x, y + 2, 1); gol_set(x + 4, y + 2, 1); | |
gol_set(x, y + 3, 1); gol_set(x + 1, y + 3, 1); gol_set(x + 2, y + 3, 1); gol_set(x + 3, y + 3, 1); | |
} | |
void place_clock(int x, int y) | |
{ | |
gol_set(x + 2, y, 1); | |
gol_set(x, y + 1, 1); gol_set(x + 2, y + 1, 1); | |
gol_set(x + 1, y + 2, 1); gol_set(x + 3, y + 2, 1); | |
gol_set(x + 1, y + 3, 1); | |
} | |
void place_deca(int x, int y) | |
{ | |
gol_set(x + 2, y, 1); gol_set(x + 7, y, 1); | |
y++; | |
gol_set(x + 0, y, 1); gol_set(x + 1, y, 1); gol_set(x + 3, y, 1); gol_set(x + 4, y, 1); gol_set(x + 5, y, 1); gol_set(x + 6, y, 1); gol_set(x + 8, y, 1); gol_set(x + 9, y, 1); | |
y++; | |
gol_set(x + 2, y, 1); gol_set(x + 7, y, 1); | |
y++; | |
} | |
void place_25p(int x, int y) | |
{ | |
gol_set(x + 10, y, 1); | |
y++; | |
gol_set(x + 8, y, 1); gol_set(x + 9, y, 1); gol_set(x + 10, y, 1); gol_set(x + 12, y, 1); gol_set(x + 13, y, 1); gol_set(x + 14, y, 1); | |
y++; | |
gol_set(x + 7, y, 1); gol_set(x + 8, y, 1); gol_set(x + 15, y, 1); | |
y++; | |
gol_set(x + 2, y, 1); gol_set(x + 6, y, 1); gol_set(x + 9, y, 1); gol_set(x + 13, y, 1); gol_set(x + 14, y, 1); | |
y++; | |
gol_set(x + 1, y, 1); gol_set(x + 2, y, 1); gol_set(x + 3, y, 1); gol_set(x + 4, y, 1); | |
y++; | |
gol_set(x + 0, y, 1); gol_set(x + 4, y, 1); | |
y++; | |
gol_set(x + 1, y, 1); gol_set(x + 3, y, 1); gol_set(x + 6, y, 1); | |
y++; | |
gol_set(x + 5, y, 1); | |
y++; | |
} | |
void place_gg(int x, int y) | |
{ | |
gol_set(x + 24, y, 1); | |
y++; | |
gol_set(x + 22, y, 1); gol_set(x + 24, y, 1); | |
y++; | |
gol_set(x + 12, y, 1); gol_set(x + 13, y, 1); gol_set(x + 20, y, 1); gol_set(x + 21, y, 1); gol_set(x + 34, y, 1); gol_set(x + 35, y, 1); | |
y++; | |
gol_set(x + 11, y, 1); gol_set(x + 15, y, 1); gol_set(x + 20, y, 1); gol_set(x + 21, y, 1); gol_set(x + 34, y, 1); gol_set(x + 35, y, 1); | |
y++; | |
gol_set(x + 0, y, 1); gol_set(x + 1, y, 1); gol_set(x + 10, y, 1); gol_set(x + 16, y, 1); gol_set(x + 20, y, 1); gol_set(x + 21, y, 1); | |
y++; | |
gol_set(x + 0, y, 1); gol_set(x + 1, y, 1); gol_set(x + 10, y, 1); gol_set(x + 14, y, 1); gol_set(x + 16, y, 1); gol_set(x + 17, y, 1); gol_set(x + 22, y, 1); gol_set(x + 24, y, 1); | |
y++; | |
gol_set(x + 10, y, 1); gol_set(x + 16, y, 1); gol_set(x + 24, y, 1); | |
y++; | |
gol_set(x + 11, y, 1); gol_set(x + 15, y, 1); | |
y++; | |
gol_set(x + 12, y, 1); gol_set(x + 13, y, 1); | |
y++; | |
} | |
void place_loafer(int x, int y) | |
{ | |
gol_set(x + 1, y, 1); gol_set(x + 2, y, 1); gol_set(x + 5, y, 1); gol_set(x + 7, y, 1); gol_set(x + 8, y, 1); | |
y++; | |
gol_set(x + 0, y, 1); gol_set(x + 3, y, 1); gol_set(x + 6, y, 1); gol_set(x + 7, y, 1); | |
y++; | |
gol_set(x + 1, y, 1); gol_set(x + 3, y, 1); | |
y++; | |
gol_set(x + 2, y, 1); | |
y++; | |
gol_set(x + 8, y, 1); | |
y++; | |
gol_set(x + 6, y, 1); gol_set(x + 7, y, 1); gol_set(x + 8, y, 1); | |
y++; | |
gol_set(x + 5, y, 1); | |
y++; | |
gol_set(x + 6, y, 1); | |
y++; | |
gol_set(x + 7, y, 1); gol_set(x + 8, y, 1); | |
y++; | |
} | |
void place_cluster(int x, int y) | |
{ | |
for (int i = 0 ; i < 16 ; i++) { | |
gol_set(x+random(8), y+random(8), 1); | |
} | |
} | |
void gol_reset() | |
{ | |
memset(GAME_LIFE2, 0, sizeof(GAME_LIFE2)); | |
int n_creatures = 2 + random(10); | |
for (int i = 0; i < n_creatures ; i++) { | |
int sx = i * (GOL_W / n_creatures); | |
int sy = random(GOL_H / 3) * 4; | |
switch (random(12)) { | |
case 0: | |
case 1: | |
case 2: | |
case 3: place_glider(sx, sy); break; | |
case 4: place_spaceship(sx, sy); break; | |
case 5: place_clock(sx, sy); break; | |
case 6: place_deca(sx, sy); break; | |
case 7: place_25p(sx, sy); break; | |
case 8: place_gg(sx, sy); break; | |
case 9: place_loafer(sx, sy); break; | |
default: place_cluster(sx, sy); break; | |
} | |
} | |
gol_buffer_swap(); | |
} | |
/************************************************************************** | |
* ARDUINO SETUP AND MAIN LOOPS | |
*/ | |
void setup() { | |
randomSeed(analogRead(0)); | |
Serial.begin(9600); | |
pinMode(CLK_OUTPUT1, OUTPUT); | |
pinMode(CLK_OUTPUT2, OUTPUT); | |
pinMode(CLK_OUTPUT3, OUTPUT); | |
pinMode(CLK_OUTPUT4, OUTPUT); | |
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally | |
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { | |
Serial.println(F("SSD1306 allocation failed")); | |
for (;;); // Don't proceed, loop forever | |
} | |
memset(GAME_LIFE1, 0x0, sizeof(GAME_LIFE1)); | |
memset(GAME_LIFE2, 0x0, sizeof(GAME_LIFE2)); | |
// Show initial display buffer contents on the screen -- | |
// the library initializes this with an Adafruit splash screen. | |
display.clearDisplay(); | |
display.display(); | |
gol_reset(); | |
// enable watchdog | |
wdt_enable(WDTO_500MS); | |
} | |
int byte_pointer = 0; | |
int bit_pointer = 0; | |
int generations = MAX_GENS; | |
unsigned int gol_counter = GOL_CYCLES; | |
unsigned int gol_incr = 255; | |
unsigned int pulse_counter = 0; | |
void loop() { | |
wdt_reset(); | |
// render screen output | |
display.clearDisplay(); | |
display.drawBitmap(0, 0, GAME_LIFE1, GOL_W, GOL_H, SSD1306_WHITE); | |
unsigned current_y = bit_pointer / GOL_W; | |
unsigned current_x = bit_pointer % GOL_W; | |
display.drawPixel(current_x, current_y, SSD1306_WHITE); | |
draw_activity_boxes(); | |
if (pulse_counter == 0) { | |
clear_pulses(); | |
} | |
else { | |
draw_activity_box_statuses(); | |
} | |
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); | |
display.setTextSize(3); | |
display.setCursor(0, 32); | |
display.println(F("life")); | |
display.drawRect(96, 0, 3, SCREEN_HEIGHT, SSD1306_WHITE); | |
display.drawLine(97, 0, 97, (int) map(generations, 0, MAX_GENS, 0, SCREEN_HEIGHT), SSD1306_WHITE); | |
display.display(); | |
// run GOL if we're going to overflow | |
if (gol_counter == 0) { | |
gol_run(); | |
generations--; | |
pulse_counter = PULSE_CYCLES; | |
output_pulses(); | |
gol_counter = GOL_CYCLES; | |
} | |
if (generations == 0) { | |
gol_reset(); | |
generations = MAX_GENS; | |
shuffle_activity_boxes(); | |
} | |
int potValue = analogRead(RATE_POT_PIN); | |
int dly = map(potValue, 0, 1023, 0, 1000); | |
delay(dly); | |
pulse_counter--; | |
gol_counter--; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment