Skip to content

Instantly share code, notes, and snippets.

@lecram
Last active December 16, 2016 23:34
Show Gist options
  • Save lecram/72725f29eadf356da3074d75c324c29e to your computer and use it in GitHub Desktop.
Save lecram/72725f29eadf356da3074d75c324c29e to your computer and use it in GitHub Desktop.
Game of Life in terminal using braille.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <locale.h>
#include <time.h>
#include <poll.h>
#include <signal.h>
#define CW 80
#define CH 24
#define PW (CW * 2)
#define PH (CH * 4)
typedef struct Surf {
int pw, ph;
size_t stride;
uint8_t data[];
} Surf;
typedef struct Screen {
int cw, ch;
Surf *cur, *old;
} Screen;
#define PGET(S, X, Y) \
((S)->data[(S)->stride * (Y) + (X) / 8] & (1 << (7 - ((X) & 7))))
#define PSET(S, X, Y) \
((S)->data[(S)->stride * (Y) + (X) / 8] |= (1 << (7 - ((X) & 7))))
#define PRST(S, X, Y) \
((S)->data[(S)->stride * (Y) + (X) / 8] &= ~(1 << (7 - ((X) & 7))))
static volatile sig_atomic_t running;
Surf *
new_surf(int pw, int ph)
{
Surf *surf;
size_t stride;
stride = (pw >> 3) + !!(pw & 7);
surf = calloc(1, sizeof(*surf) + stride * ph * sizeof(surf->data[0]));
if (surf) {
surf->pw = pw;
surf->ph = ph;
surf->stride = stride;
}
return surf;
}
Screen *
new_screen(int cw, int ch)
{
Screen *screen;
int pw, ph;
pw = cw * 2;
ph = ch * 4;
screen = malloc(sizeof(*screen));
if (screen) {
screen->cw = cw;
screen->ch = ch;
screen->cur = new_surf(pw, ph);
screen->old = new_surf(pw, ph);
memset(screen->old->data, 0xFF, screen->old->stride * screen->old->ph);
}
return screen;
}
void
flip(Screen *screen)
{
int cx, cy;
int px, py;
uint8_t oldc, newc;
uint8_t mask[8] = {0x01, 0x08, 0x02, 0x10, 0x04, 0x20, 0x40, 0x80};
uint8_t mb[4] = {0xE2, 0x00, 0x00, 0x00};
int i;
Surf *tmp;
for (cy = 0; cy < screen->ch; cy++) {
for (cx = 0; cx < screen->cw; cx++) {
oldc = newc = 0x00;
i = 0;
for (py = cy * 4; py < (cy+1) * 4; py++) {
for (px = cx * 2; px < (cx+1) * 2; px++) {
if (PGET(screen->old, px, py))
oldc += mask[i];
if (PGET(screen->cur, px, py))
newc += mask[i];
i++;
}
}
if (newc != oldc) {
mb[1] = 0xA0 | (newc >> 6);
mb[2] = (newc & 0xBF) | 0x80;
printf("\x1B[%d;%dH%s", cy + 1, cx + 1, mb);
}
}
}
fflush(stdout);
tmp = screen->old;
screen->old = screen->cur;
screen->cur = tmp;
}
void
free_screen(Screen *screen)
{
free(screen->cur);
free(screen->old);
free(screen);
}
void
random_surf(Surf *surf)
{
unsigned i, n;
uint8_t c;
for (i = 0; i < surf->stride * surf->ph; i++) {
c = 0xFF;
for (n = 0; n < 3; n++)
c &= rand() % 0x100;
surf->data[i] = c;
}
}
void
update(Screen *screen)
{
int x, y;
int c, n;
for (y = 0; y < PH; y++) {
for (x = 0; x < PW; x++) {
c = PGET(screen->old, x, y);
n = !!PGET(screen->old, (PW+x-1) % PW, (PH+y-1) % PH)
+ !!PGET(screen->old, (PW+x ) % PW, (PH+y-1) % PH)
+ !!PGET(screen->old, (PW+x+1) % PW, (PH+y-1) % PH)
+ !!PGET(screen->old, (PW+x-1) % PW, (PH+y ) % PH)
+ !!PGET(screen->old, (PW+x+1) % PW, (PH+y ) % PH)
+ !!PGET(screen->old, (PW+x-1) % PW, (PH+y+1) % PH)
+ !!PGET(screen->old, (PW+x ) % PW, (PH+y+1) % PH)
+ !!PGET(screen->old, (PW+x+1) % PW, (PH+y+1) % PH);
if (c) {
if (n < 2 || n > 3)
PRST(screen->cur, x, y);
else
PSET(screen->cur, x, y);
} else {
if (n == 3)
PSET(screen->cur, x, y);
else
PRST(screen->cur, x, y);
}
}
}
}
void
handle_int(int signum)
{
(void) signum;
running = 0;
}
int
main()
{
Screen *scr;
struct sigaction sa;
srand(time(NULL));
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handle_int;
sigaction(SIGINT, &sa, NULL);
printf("\x1B[?25l");
setlocale(LC_ALL, "");
scr = new_screen(CW, CH);
flip(scr);
random_surf(scr->cur);
running = 1;
while (running) {
flip(scr);
update(scr);
poll(NULL, 0, 10);
}
printf("\x1B[?25h\x1B[%d;1H\n", CH);
free_screen(scr);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment