John Conway's Game of Life - Playdate C API
Controls
Up/Down | Increase/Decrease cell size |
A | Reset |
B | Pause |
Crank | Increase/Decrease probability of a cell being alive on init |
Source Code - C
#include <stdbool.h>
#include "pd_api.h"
static int CELL_SIZE = 8;
static int CELL_COLS; // set at runtime
static int CELL_ROWS;
static PlaydateAPI* pd = NULL;
static bool** fg = NULL;
static bool** bg = NULL;
static bool isPaused = false;
static int newCellProbability = 50;
static bool isAlive(int x, int y) {
return fg[(x + CELL_COLS) % CELL_COLS][(y + CELL_ROWS) % CELL_ROWS];
}
// any live cell with fewer than 2 live neighbors dies, as if caused by underpopulation.
// any live cell with more than 3 live neighbors dies, as if by overcrowding.
// any live cell with two or 3 live neighbors lives on to the next generation.
// any dead cell with exactly 3 live neighbors becomes a live cell.
static bool nextState(int x, int y) {
int neighbors = 0;
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if (dx == 0 && dy == 0) continue;
if (isAlive(x + dx, y + dy)) neighbors++;
}
}
if (isAlive(x, y)) {
return neighbors == 2 || neighbors == 3;
} else {
return neighbors == 3;
}
return false;
}
static void step(void) {
for (int y = 0; y < CELL_ROWS; y++) {
for (int x = 0; x < CELL_COLS; x++) {
bg[x][y] = nextState(x, y);
}
}
bool** temp = fg;
fg = bg;
bg = temp;
}
static void randomizeGrid(void) {
for (int y = 0; y < CELL_ROWS; y++) {
for (int x = 0; x < CELL_COLS; x++) {
fg[x][y] = (rand() % 100 < newCellProbability);
}
}
}
static void initGrid(void) {
CELL_COLS = LCD_COLUMNS / CELL_SIZE;
CELL_ROWS = LCD_ROWS / CELL_SIZE;
fg = (bool**)malloc(sizeof(bool*) * CELL_COLS);
bg = (bool**)malloc(sizeof(bool*) * CELL_COLS);
for (int i = 0; i < CELL_COLS; i++) {
fg[i] = (bool*)malloc(sizeof(bool) * CELL_ROWS);
bg[i] = (bool*)malloc(sizeof(bool) * CELL_ROWS);
}
randomizeGrid();
}
static void freeGrid(void) {
for (int i = 0; i < CELL_COLS; i++) {
free(bg[i]);
free(fg[i]);
}
free(bg);
free(fg);
bg = NULL;
fg = NULL;
}
static void resizeCell(int newCellSize) {
freeGrid();
CELL_SIZE = newCellSize;
initGrid();
}
static void drawGrid(void) {
pd->graphics->clear(kColorWhite);
for (int y = 0; y < CELL_ROWS; y++) {
for (int x = 0; x < CELL_COLS; x++) {
if (fg[x][y]) {
int px = x * CELL_SIZE;
int py = y * CELL_SIZE;
pd->graphics->fillRect(px, py, CELL_SIZE, CELL_SIZE, kColorBlack);
}
}
}
}
static int update(void* ud) {
PDButtons current, pushed, released;
pd->system->getButtonState(¤t, &pushed, &released);
if (pushed & kButtonA) {
randomizeGrid();
}
if (pushed & kButtonB) {
isPaused = !isPaused;
}
if (pushed & kButtonUp) {
resizeCell(CELL_SIZE + 1);
} else if (pushed & kButtonDown && CELL_SIZE > 1) {
resizeCell(CELL_SIZE - 1);
}
float crankChange = pd->system->getCrankChange();
if (crankChange > 0) { // clockwise
newCellProbability += 1;
if (newCellProbability > 100) newCellProbability = 100;
randomizeGrid();
} else if (crankChange < 0) { // counter-clockwise
newCellProbability -= 1;
if (newCellProbability < 1) newCellProbability = 1;
randomizeGrid();
}
if (!isPaused) {
step();
}
drawGrid();
// pd->system->drawFPS(0, 0);
return 1;
}
int eventHandler(PlaydateAPI* playdate, PDSystemEvent event, uint32_t arg) {
if (event == kEventInit) {
pd = playdate;
initGrid();
pd->display->setRefreshRate(8);
pd->system->setUpdateCallback(update, NULL);
}
return 0;
}
Build & Run
make clean && make && make run
~/Developer/PlaydateSDK/bin/Playdate\ Simulator.app/Contents/MacOS/Playdate\ Simulator build/GameOfLife.pdx
Download
Source Code
The source code can be found on GitHub.