cgol

Conway's Game of Life
git clone git://git.bitsmanent.org/cgol
Log | Files | Refs | README | LICENSE

commit d97692300ed8f7fa1b0dec505a9de62c9666fbf1
Author: Claudio Alessi <smoppy@gmail.com>
Date:   Wed, 21 Dec 2022 18:56:15 +0100

initial commit (it's working)

Diffstat:
ALICENSE | 21+++++++++++++++++++++
AMakefile | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarg.h | 41+++++++++++++++++++++++++++++++++++++++++
Acgol.1 | 0
Acgol.c | 267+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.mk | 16++++++++++++++++
Apatterns/blinker.cgol | 5+++++
Apatterns/block.cgol | 4++++
Apatterns/glider.cgol | 5+++++
Apatterns/gosper.cgol | 11+++++++++++
Apatterns/pulsar.cgol | 17+++++++++++++++++
11 files changed, 444 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,21 @@ +MIT/X Consortium License + +© 2022-2023 Claudio Alessi <smoppy at gmail dot com> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,57 @@ +# cgol +# See LICENSE file for copyright and license details. + +include config.mk + +APPNAME=cgol +SRC = ${APPNAME}.c +OBJ = ${SRC:.c=.o} + +all: options ${APPNAME} + +options: + @echo ${APPNAME} build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: config.mk + +${APPNAME}: ${OBJ} + @echo CC -o $@ + @${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + @echo cleaning + @rm -f ${APPNAME} ${OBJ} ${APPNAME}-${VERSION}.tar.gz + +dist: clean + @echo creating dist tarball + @mkdir -p ${APPNAME}-${VERSION} + @cp -R LICENSE Makefile README config.mk \ + ${APPNAME}.1 ${SRC} ${APPNAME}-${VERSION} + @tar -cf ${APPNAME}-${VERSION}.tar ${APPNAME}-${VERSION} + @gzip ${APPNAME}-${VERSION}.tar + @rm -rf ${APPNAME}-${VERSION} + +install: all + @echo installing executable file to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f ${APPNAME} ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/${APPNAME} + @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @sed "s/VERSION/${VERSION}/g" < ${APPNAME}.1 > ${DESTDIR}${MANPREFIX}/man1/${APPNAME}.1 + @chmod 644 ${DESTDIR}${MANPREFIX}/man1/${APPNAME}.1 + +uninstall: + @echo removing executable file from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/${APPNAME} + @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/${APPNAME}.1 + +.PHONY: all options clean dist install uninstall diff --git a/arg.h b/arg.h @@ -0,0 +1,41 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef __ARG_H__ +#define __ARG_H__ + +extern char *argv0; + +#define USED(x) ((void)(x)) + +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char _argc;\ + char **_argv;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (argv[0]++, _argv = argv; argv[0][0];\ + argv[0]++) {\ + if (_argv != argv)\ + break;\ + _argc = argv[0][0];\ + switch (_argc) + +#define ARGEND }\ + USED(_argc);\ + }\ + USED(argv);\ + USED(argc); + +#define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\ + (argc--, argv++, argv[0])) + +#endif + diff --git a/cgol.1 b/cgol.1 diff --git a/cgol.c b/cgol.c @@ -0,0 +1,267 @@ +/* Conway's Game of Life */ + +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <time.h> +#include <unistd.h> + +#include "arg.h" +char *argv0; + +/* macros */ +#define LENGTH(X) (sizeof X / sizeof X[0]) + +/* VT100 escape sequences */ +#define CLEARRIGHT "\33[0K" +#define CURPOS "\33[%d;%dH" +#define CURSON "\33[?25h" +#define CURSOFF "\33[?25l" + +/* function declarations */ +void *ecalloc(size_t nmemb, size_t size); +void die(const char *fmt, ...); +void draw(void); +int msleep(int ms); +int mvprintf(int x, int y, char *fmt, ...); +int neighbors(int pos); +int gridfile(char *file); +void gridrand(int w, int h); +void resize(int x, int y); +void setup(void); +void sigwinch(int unused); +void tick(void); +void usage(void); + +/* variables */ +int gw, gh, gs; /* grid, width, height, size */ +int *grid, *diff; +int rows, cols; +int generation; + +/* function implementations */ +void * +ecalloc(size_t nmemb, size_t size) { + void *p; + + if(!(p = calloc(nmemb, size))) + die("Cannot allocate memory."); + return p; +} + +void +die(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(0); +} + +void +draw(void) { + int r, c; + + printf(CURSOFF); + mvprintf(1, 1, "Conway's Game of Life ⋅ #%d ⋅ %0dx%0d" CLEARRIGHT, generation, rows, cols); + for(r = 0; r < rows - 1 && r < gh; ++r) + for(c = 0; c < cols && c < gw; ++c) + mvprintf(1+c, 2+r, "%s", grid[r * gw + c] ? "\033[07m \033[m" : " "); + printf(CURPOS CURSON, rows, cols); +} + +int +gridfile(char *file) { + FILE *fp; + char *line, *p; + size_t len = 0, nc; + int r, c; + + fp = fopen(file, "r"); + if(!fp) + return -1; + r = 0; + while((nc = getline(&line, &len, fp)) != -1) { + p = line; + c = 0; + while(*p && *p != '\n') { + grid[r * gw + c] = (*p == '1') ? 1 : 0; + ++c; + p += 2; + } + ++r; + } + free(line); + fclose(fp); + return 0; +} + +void +gridrand(int w, int h) { + int r, c; + + for(r = 0; r < h && r < gh; ++r) + for(c = 0; c < w && c < gw; ++c) + grid[r * gw + c] = random() % 4 ? 0 : 1; +} + +int +msleep(int ms) { + struct timespec req = {0}, rem; + int r = ms / 1000; + + if(r >= 1) { + req.tv_sec = r; + ms -= r * 1000; + } + if(ms) + req.tv_nsec = ms * 1000000; + + while((r = nanosleep(&req, &rem)) == -1 && errno == EINTR) + req = rem; + return r; +} + +int +mvprintf(int x, int y, char *fmt, ...) { + va_list ap; + int len; + + printf(CURPOS, y, x); + va_start(ap, fmt); + len = vfprintf(stdout, fmt, ap); + va_end(ap); + return len; +} + +int +neighbors(int pos) { + int r = pos / gw; + int c = pos % gw; + int sides[] = { + r - 1 >= 0 && c - 1 >= 0 ? (r - 1) * gw + (c - 1) : -1, /* top-left */ + r - 1 >= 0 ? (r - 1) * gw + c : -1, /* top */ + r - 1 >= 0 && c + 1 < gw ? (r - 1) * gw + (c + 1) : -1, /* top-right */ + c + 1 < gw ? r * gw + (c + 1) : -1, /* right */ + r + 1 < gh && c + 1 < gw ? (r + 1) * gw + (c + 1) : -1, /* bottom-right */ + r + 1 < gh ? (r + 1) * gw + c : -1, /* bottom */ + r + 1 < gh && c - 1 >= 0 ? (r + 1) * gw + (c - 1) : -1, /* bottom-left */ + c - 1 >= 0 ? r * gw + (c - 1) : -1, /* left */ + }; + int n = 0, i; + + for(i = 0; i < LENGTH(sides); ++i) + n += sides[i] >= 0 ? grid[sides[i]] : 0; + return n; +} + +void +resize(int wsrow, int wscol) { + rows = wsrow; + cols = wscol; +} + +void +setup(void) { + struct sigaction sa; + struct winsize ws; + + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigwinch; + sigaction(SIGWINCH, &sa, NULL); + ioctl(0, TIOCGWINSZ, &ws); + resize(ws.ws_row, ws.ws_col); + setbuf(stdout, NULL); +} + +void +sigwinch(int unused) { + struct winsize ws; + + ioctl(0, TIOCGWINSZ, &ws); + resize(ws.ws_row, ws.ws_col); + draw(); +} + +void +tick(void) { + int i, n; + + for(i = 0; i < gs; ++i) { + n = neighbors(i); + if(grid[i] == 1 && (n < 2 || n > 3)) + diff[i] = 0; + else if(grid[i] == 0 && n == 3) + diff[i] = 1; + else + diff[i] = grid[i]; + } + for(i = 0; i < gs; ++i) + if(grid[i] != diff[i]) + grid[i] = diff[i]; + ++generation; +} + +void +usage(void) { + die("Usage: %s [-v] [-dgn <arg>] [file]", argv0); +} + +int +main(int argc, char *argv[]) { + int delay = 250, gen = 0, ticks = 0; + char *file = NULL; + + ARGBEGIN { + case 'd': delay = atoi(EARGF(usage())); break; + case 'g': gen = atoi(EARGF(usage())); break; + case 'n': ticks = atoi(EARGF(usage())); break; + case 'v': die("cgol-"VERSION); + default: usage(); + } ARGEND; + + if(argc) file = argv[0]; + setup(); + + if(!gw) gw = 256; + if(!gh) gh = 256; + gs = gw * gw; + grid = ecalloc(gs, sizeof(int)); + diff = ecalloc(gs, sizeof(int)); + + if(file) { + if(gridfile(file)) + die("%s: %s:", argv0, file); + } + else { + srandom(time(NULL)); + gridrand(cols, rows); + } + + while(gen-- > 0) + tick(); + while(1) { + draw(); + tick(); + if(!--ticks) + break; + msleep(delay); + } + /* NOTREACHED */ + free(grid); + free(diff); + return 0; +} diff --git a/config.mk b/config.mk @@ -0,0 +1,16 @@ +# cgol +VERSION = 0.1 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -DVERSION=\"${VERSION}\" +CFLAGS = -std=c99 -g -pedantic -Wall -O0 ${CPPFLAGS} +#CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${CPPFLAGS} + +# compiler and linker +CC = cc diff --git a/patterns/blinker.cgol b/patterns/blinker.cgol @@ -0,0 +1,5 @@ +0 0 0 0 0 +0 0 0 0 0 +0 1 1 1 0 +0 0 0 0 0 +0 0 0 0 0 diff --git a/patterns/block.cgol b/patterns/block.cgol @@ -0,0 +1,4 @@ +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 0 0 0 diff --git a/patterns/glider.cgol b/patterns/glider.cgol @@ -0,0 +1,5 @@ +0 0 0 0 0 +0 0 1 0 0 +0 0 0 1 0 +0 1 1 1 0 +0 0 0 0 0 diff --git a/patterns/gosper.cgol b/patterns/gosper.cgol @@ -0,0 +1,11 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 +0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/patterns/pulsar.cgol b/patterns/pulsar.cgol @@ -0,0 +1,17 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 +0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 +0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 +0 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 0 +0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 +0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 +0 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0