mirror of
https://github.com/sigmasternchen/gameoflife
synced 2025-03-15 08:08:57 +00:00
I seem to only use git for projects I already got into a functioning state.
This commit is contained in:
parent
6e13be87b5
commit
e5ffe2516d
15 changed files with 923 additions and 0 deletions
23
Makefile
Normal file
23
Makefile
Normal file
|
@ -0,0 +1,23 @@
|
|||
LFLAGS =
|
||||
CFLAGS =
|
||||
|
||||
all: gif
|
||||
|
||||
gif: LFLAGS += -lgif
|
||||
gif: CFLAGS += -DGIF_SUPPORT
|
||||
gif: gameoflife
|
||||
|
||||
gameoflife: src/main.o src/field.o src/misc.o src/cli.o src/gif.o
|
||||
gcc $(LFLAGS) -o $@ $^
|
||||
|
||||
src/main.o: src/field.h src/misc.h src/cli.h
|
||||
src/field.o: src/field.h src/misc.h
|
||||
src/misc.o: src/misc.h
|
||||
src/cli.o: src/field.h
|
||||
|
||||
%.o: %.c
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
@rm src/*.o
|
||||
@rm gameoflife
|
5
demos/diehard.gol
Normal file
5
demos/diehard.gol
Normal file
|
@ -0,0 +1,5 @@
|
|||
#50,30;
|
||||
|
||||
27,6;
|
||||
21,7;22,7;
|
||||
22,8;26,8;27,8;28,8;
|
23
demos/glider-gun.gol
Normal file
23
demos/glider-gun.gol
Normal file
|
@ -0,0 +1,23 @@
|
|||
#200,200;
|
||||
|
||||
1,6;2,6;
|
||||
1,7;2,7;
|
||||
|
||||
13,4;14,4;
|
||||
12,5;16,5;
|
||||
11,6;17,6;
|
||||
11,7;15,7;17,7;18,7;
|
||||
11,8;17,8;
|
||||
12,9;16,9;
|
||||
13,10;14,10;
|
||||
|
||||
25,2;
|
||||
23,3;25,3;
|
||||
21,4;22,4;
|
||||
21,5;22,5;
|
||||
21,6;22,6;
|
||||
23,7;25,7;
|
||||
25,8;
|
||||
|
||||
35,4;36,4;
|
||||
35,5;36,4;
|
5
demos/glider.gol
Normal file
5
demos/glider.gol
Normal file
|
@ -0,0 +1,5 @@
|
|||
#30,30;
|
||||
|
||||
1,0;
|
||||
2,1;
|
||||
0,2;1,2;2,2;
|
6
demos/lwss.gol
Normal file
6
demos/lwss.gol
Normal file
|
@ -0,0 +1,6 @@
|
|||
#70,7;
|
||||
|
||||
0,1;4,1;
|
||||
5,2;
|
||||
1,3;5,3;
|
||||
2,4;3,4;4,4;5,4;
|
14
demos/pentadecathlon.gol
Normal file
14
demos/pentadecathlon.gol
Normal file
|
@ -0,0 +1,14 @@
|
|||
#11,18;
|
||||
|
||||
4,3;5,3;6,3;
|
||||
5,4;
|
||||
5,5;
|
||||
4,6;5,6;6,6;
|
||||
|
||||
4,8;5,8;6,8;
|
||||
4,9;5,9;6,9;
|
||||
|
||||
4,11;5,11;6,11;
|
||||
5,12;
|
||||
5,13;
|
||||
4,14;5,14;6,14;
|
43
src/cli.c
Normal file
43
src/cli.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "cli.h"
|
||||
#include "field.h"
|
||||
|
||||
cli_settings_t cli_settings;
|
||||
|
||||
void cli_init(cli_settings_t _settings) {
|
||||
cli_settings = _settings;
|
||||
}
|
||||
|
||||
int height;
|
||||
void cli_output(field_t field) {
|
||||
height = field.height;
|
||||
if (cli_settings.border)
|
||||
for (int x = 0; x < field.width + 2; x++) {
|
||||
printf("#");
|
||||
}
|
||||
printf("\n");
|
||||
for (int y = 0; y < field.height; y++) {
|
||||
if (cli_settings.border)
|
||||
printf("#");
|
||||
for (int x = 0; x < field.width; x++) {
|
||||
cell_t cell = field.cells[x][y];
|
||||
if (cell == UNDEFINED)
|
||||
cell = DEAD;
|
||||
printf("%c", cli_settings.charset[cell]);
|
||||
}
|
||||
if (cli_settings.border)
|
||||
printf("#");
|
||||
printf("\n");
|
||||
}
|
||||
if (cli_settings.border)
|
||||
for (int x = 0; x < field.width + 2; x++) {
|
||||
printf("#");
|
||||
}
|
||||
printf("\n\033[%iA", height + (cli_settings.border ? 2 : 0));
|
||||
}
|
||||
|
||||
void cli_end() {
|
||||
printf("\33[%iB", height + (cli_settings.border ? 2 : 0));
|
||||
}
|
17
src/cli.h
Normal file
17
src/cli.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef CLI_H
|
||||
#define CLI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "field.h"
|
||||
|
||||
typedef struct {
|
||||
bool border;
|
||||
const char* charset;
|
||||
} cli_settings_t;
|
||||
|
||||
void cli_init(cli_settings_t);
|
||||
void cli_output(field_t);
|
||||
void cli_end();
|
||||
|
||||
#endif
|
164
src/field.c
Normal file
164
src/field.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "field.h"
|
||||
|
||||
settings_t settings;
|
||||
|
||||
int generationCount;
|
||||
cell_t*** generations = NULL;
|
||||
|
||||
void initField(settings_t _settings) {
|
||||
settings = _settings;
|
||||
|
||||
generationCount = settings.periodThreshold;
|
||||
if (generationCount < 1)
|
||||
generationCount = 2;
|
||||
generations = malloc(generationCount * sizeof(cell_t**));
|
||||
if (generations == NULL)
|
||||
error(EXIT_PANIC, "field_setup", NULL);
|
||||
|
||||
for (int g = 0; g < generationCount; g++) {
|
||||
generations[g] = malloc(settings.dimensions.width * sizeof(cell_t*));
|
||||
if (generations[g] == NULL)
|
||||
error(EXIT_PANIC, "field_setup", NULL);
|
||||
|
||||
for (int x = 0; x < settings.dimensions.width; x++) {
|
||||
generations[g][x] = malloc(settings.dimensions.height * sizeof(cell_t));
|
||||
if (generations[g][x] == NULL)
|
||||
error(EXIT_PANIC, "field_setup", NULL);
|
||||
for (int y = 0; y < settings.dimensions.height; y++) {
|
||||
generations[g][x][y] = g == 0 ? DEAD : UNDEFINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline cell_t normalize(cell_t cell) {
|
||||
if (cell == NEW)
|
||||
return ALIVE;
|
||||
if (cell == DIED)
|
||||
return DEAD;
|
||||
if (cell == UNDEFINED)
|
||||
return DEAD;
|
||||
return cell;
|
||||
}
|
||||
|
||||
static inline cell_t normalizeForPeriodCheck(cell_t cell) {
|
||||
if (cell == UNDEFINED)
|
||||
return cell;
|
||||
return normalize(cell);
|
||||
}
|
||||
|
||||
static inline int getBoundarySafeState(int x, int y, cell_t** field) {
|
||||
if (x < 0)
|
||||
return 0;
|
||||
if (y < 0)
|
||||
return 0;
|
||||
if (x >= settings.dimensions.width)
|
||||
return 0;
|
||||
if (y >= settings.dimensions.height)
|
||||
return 0;
|
||||
return normalize(field[x][y]) == ALIVE ? 1 : 0;
|
||||
}
|
||||
static inline int countNeighbors(int x, int y, cell_t** field) {
|
||||
int count = 0;
|
||||
|
||||
count += getBoundarySafeState(x + 1, y - 1, field);
|
||||
count += getBoundarySafeState(x + 1, y, field);
|
||||
count += getBoundarySafeState(x + 1, y + 1, field);
|
||||
count += getBoundarySafeState(x, y + 1, field);
|
||||
count += getBoundarySafeState(x - 1, y + 1, field);
|
||||
count += getBoundarySafeState(x - 1, y, field);
|
||||
count += getBoundarySafeState(x - 1, y - 1, field);
|
||||
count += getBoundarySafeState(x, y - 1, field);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline cell_t nextState(int count, cell_t current) {
|
||||
current = normalize(current);
|
||||
|
||||
if (current == ALIVE) {
|
||||
switch (settings.rules.neighbors[count]) {
|
||||
case DIE:
|
||||
return DIED;
|
||||
case CREATE:
|
||||
return ALIVE;
|
||||
case KEEP:
|
||||
return ALIVE;
|
||||
case TOGGLE:
|
||||
return DIED;
|
||||
default:
|
||||
error(EXIT_PANIC, "field_nextState", "unknown case");
|
||||
}
|
||||
} else {
|
||||
switch (settings.rules.neighbors[count]) {
|
||||
case DIE:
|
||||
return DEAD;
|
||||
case CREATE:
|
||||
return NEW;
|
||||
case KEEP:
|
||||
return DEAD;
|
||||
case TOGGLE:
|
||||
return NEW;
|
||||
default:
|
||||
error(EXIT_PANIC, "field_nextState", "unknown case");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int getPeriod() {
|
||||
if (settings.periodThreshold < 0)
|
||||
return -1;
|
||||
|
||||
for (int g = 1; g < generationCount; g++) {
|
||||
bool diff = false;
|
||||
for (int x = 0; x < settings.dimensions.width; x++) {
|
||||
for (int y = 0; y < settings.dimensions.height; y++) {
|
||||
//printf("x: %i, y: %i, g: %i, %i, %i\n", x, y, g, normalizeForPeriodCheck(generations[0][x][y]), normalizeForPeriodCheck(generations[g][x][y]));
|
||||
if (normalizeForPeriodCheck(generations[0][x][y]) != normalizeForPeriodCheck(generations[g][x][y])) {
|
||||
diff = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (diff)
|
||||
break;
|
||||
}
|
||||
//printf("diff: %i\n", diff);
|
||||
if (!diff)
|
||||
return g;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int nextGeneration() {
|
||||
for (int x = 0; x < settings.dimensions.width; x++)
|
||||
free(generations[generationCount - 1][x]);
|
||||
for (int g = generationCount - 2; g >= 0; g--) {
|
||||
memcpy(generations[g + 1], generations[g], settings.dimensions.width * sizeof(cell_t*));
|
||||
}
|
||||
for (int x = 0; x < settings.dimensions.width; x++) {
|
||||
generations[0][x] = malloc(settings.dimensions.height * sizeof(cell_t));
|
||||
if (generations[0][x] == NULL)
|
||||
error(EXIT_PANIC, "field_nextGeneration", NULL);
|
||||
|
||||
for (int y = 0; y < settings.dimensions.height; y++) {
|
||||
generations[0][x][y] = nextState(countNeighbors(x, y, generations[1]), generations[1][x][y]);
|
||||
}
|
||||
}
|
||||
|
||||
return getPeriod();
|
||||
}
|
||||
|
||||
field_t getField() {
|
||||
return (field_t) {
|
||||
.height = settings.dimensions.height,
|
||||
.width = settings.dimensions.width,
|
||||
.cells = generations[0]
|
||||
};
|
||||
}
|
43
src/field.h
Normal file
43
src/field.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef FIELD_H
|
||||
#define FIELD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
DEAD = 0, DIED = 1, NEW = 2, ALIVE = 3, UNDEFINED = -1
|
||||
} cell_t;
|
||||
|
||||
typedef enum {
|
||||
DIE = 0, CREATE = 1, KEEP = 2, TOGGLE = 3
|
||||
} behaviour_t;
|
||||
|
||||
typedef struct {
|
||||
behaviour_t neighbors[9];
|
||||
} rules_t;
|
||||
|
||||
typedef struct {
|
||||
int height;
|
||||
int width;
|
||||
} dimensions_t;
|
||||
|
||||
typedef struct {
|
||||
rules_t rules;
|
||||
dimensions_t dimensions;
|
||||
int periodThreshold;
|
||||
} settings_t;
|
||||
|
||||
typedef struct {
|
||||
int height;
|
||||
int width;
|
||||
cell_t** cells;
|
||||
} field_t;
|
||||
|
||||
#define DEFAULT_RULES ((rules_t) { .neighbors = {DIE, DIE, KEEP, CREATE, DIE, DIE, DIE, DIE, DIE}})
|
||||
#define DEFAULT_PERIOD_THRESHOLD (30)
|
||||
|
||||
void initField(settings_t);
|
||||
int nextGeneration();
|
||||
field_t getField();
|
||||
cell_t normalize(cell_t);
|
||||
|
||||
#endif
|
134
src/gif.c
Normal file
134
src/gif.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
#ifdef GIF_SUPPORT
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gif_lib.h>
|
||||
|
||||
#include "gif.h"
|
||||
#include "field.h"
|
||||
#include "misc.h"
|
||||
|
||||
gif_settings_t gif_settings;
|
||||
|
||||
GifFileType* image;
|
||||
|
||||
void putGraficsControlBlock() {
|
||||
int delay = gif_settings.sleep;
|
||||
|
||||
static unsigned char
|
||||
ExtStr[4] = { 0x04, 0x00, 0x00, 0xff };
|
||||
|
||||
|
||||
ExtStr[0] = (false) ? 0x06 : 0x04;
|
||||
ExtStr[1] = delay % 256;
|
||||
ExtStr[2] = delay / 256;
|
||||
|
||||
EGifPutExtension(image, GRAPHICS_EXT_FUNC_CODE, 4, ExtStr);
|
||||
}
|
||||
|
||||
void enableLoop() {
|
||||
char nsle[12] = "NETSCAPE2.0";
|
||||
char subblock[3];
|
||||
if (EGifPutExtensionLeader(image, APPLICATION_EXT_FUNC_CODE) != GIF_OK) {
|
||||
error(EXIT_PANIC, "gif_init", "put extension leader");
|
||||
}
|
||||
if (EGifPutExtensionBlock(image, 11, nsle) != GIF_OK) {
|
||||
error(EXIT_PANIC, "gif_init", "put extension");
|
||||
}
|
||||
subblock[0] = 1;
|
||||
subblock[2] = 0; // loop count lo
|
||||
subblock[1] = 0; // loop count hi
|
||||
if (EGifPutExtensionBlock(image, 3, subblock) != GIF_OK) {
|
||||
error(EXIT_PANIC, "gif_init", "put extension");
|
||||
}
|
||||
if (EGifPutExtensionTrailer(image) != GIF_OK) {
|
||||
error(EXIT_PANIC, "gif_init", "put extension trailer");
|
||||
}
|
||||
}
|
||||
|
||||
void gif_init(gif_settings_t _settings) {
|
||||
gif_settings = _settings;
|
||||
|
||||
int errorCode = 0;
|
||||
image = EGifOpenFileHandle(gif_settings.fh, &errorCode);
|
||||
|
||||
if (errorCode != E_GIF_SUCCEEDED) {
|
||||
error(EXIT_PANIC, "gif_init", "open file handler");
|
||||
}
|
||||
|
||||
GifColorType colors[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
colors[i].Red = gif_settings.colors[i][0];
|
||||
colors[i].Green = gif_settings.colors[i][1];
|
||||
colors[i].Blue = gif_settings.colors[i][2];
|
||||
}
|
||||
|
||||
ColorMapObject* colorMap = GifMakeMapObject(4, &(colors[0]));
|
||||
|
||||
EGifSetGifVersion(image, true);
|
||||
|
||||
if (EGifPutScreenDesc(image, gif_settings.dimensions.width, gif_settings.dimensions.height, 4, 0, colorMap) != GIF_OK) {
|
||||
error(EXIT_PANIC, "gif_init", "put screen desc");
|
||||
}
|
||||
|
||||
enableLoop();
|
||||
}
|
||||
|
||||
void gif_output(field_t field) {
|
||||
putGraficsControlBlock();
|
||||
|
||||
if (EGifPutImageDesc(image, 0, 0, field.width, field.height, false, NULL) != GIF_OK) {
|
||||
error(EXIT_PANIC, "git_output", "put image desc");
|
||||
}
|
||||
|
||||
for (int y = 0; y < field.height; y++) {
|
||||
for(int x = 0; x < field.width; x++) {
|
||||
cell_t cell = field.cells[x][y];
|
||||
if (cell == UNDEFINED)
|
||||
cell = DEAD;
|
||||
if (EGifPutPixel(image, (GifPixelType) cell) != GIF_OK) {
|
||||
error(EXIT_PANIC, "gif_output", "put pixel");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gif_end() {
|
||||
int errorCode;
|
||||
EGifCloseFile(image, &errorCode);
|
||||
|
||||
if (errorCode != E_GIF_SUCCEEDED) {
|
||||
fprintf(stderr, "%d\n", errorCode);
|
||||
error(EXIT_PANIC, "gif_end", "close file");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char hex2byte(const char* string, int offset) {
|
||||
char tmp[3];
|
||||
memcpy(&(tmp[0]), string + offset, 2);
|
||||
tmp[3] = '\0';
|
||||
|
||||
char* endptr;
|
||||
unsigned char result = strtol(tmp, &endptr, 16);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void hex2color(unsigned char* color, const char* string) {
|
||||
if (strlen(string) == 7) {
|
||||
color[0] = hex2byte(string, 1);
|
||||
color[1] = hex2byte(string, 3);
|
||||
color[2] = hex2byte(string, 5);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
void gif_setColor(gif_settings_t* settings, const char* dead, const char* died, const char* new, const char* alive) {
|
||||
hex2color(&(settings->colors[DEAD][0]), dead);
|
||||
hex2color(&(settings->colors[DIED][0]), died);
|
||||
hex2color(&(settings->colors[NEW][0]), new);
|
||||
hex2color(&(settings->colors[ALIVE][0]), alive);
|
||||
}
|
||||
#endif
|
19
src/gif.h
Normal file
19
src/gif.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef GIF_H
|
||||
#define GIF_H
|
||||
|
||||
#include "field.h"
|
||||
|
||||
typedef struct {
|
||||
int fh;
|
||||
dimensions_t dimensions;
|
||||
unsigned char colors[4][3];
|
||||
int sleep;
|
||||
} gif_settings_t;
|
||||
|
||||
void gif_init(gif_settings_t _settings);
|
||||
void gif_output(field_t field);
|
||||
void gif_setColor(gif_settings_t* settings, const char* dead, const char* died, const char* new, const char* alive);
|
||||
void hex2color(unsigned char* color, const char* string);
|
||||
void gif_end();
|
||||
|
||||
#endif
|
386
src/main.c
Normal file
386
src/main.c
Normal file
|
@ -0,0 +1,386 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "field.h"
|
||||
#include "cli.h"
|
||||
#include "gif.h"
|
||||
|
||||
char behaviourMap[4] = {
|
||||
'd', 'c', 'k', 't'
|
||||
};
|
||||
|
||||
void optionError(const char* string) {
|
||||
fprintf(stderr, "Error: %s\n", string);
|
||||
fprintf(stderr, "User -y for help.\n");
|
||||
exit(EXIT_USER_ERROR);
|
||||
}
|
||||
|
||||
void rules2string(char* string, rules_t rules) {
|
||||
for (int i = 0; i < 9; i++) {
|
||||
string[i] = behaviourMap[rules.neighbors[i]];
|
||||
}
|
||||
string[10] = '\0';
|
||||
}
|
||||
|
||||
rules_t string2rules(const char* string) {
|
||||
if (strlen(string) != 9) {
|
||||
optionError("Rules string malformed");
|
||||
}
|
||||
|
||||
rules_t rules;
|
||||
|
||||
for(int i = 0; i < strlen(string); i++) {
|
||||
bool okay = false;
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (string[i] == behaviourMap[j]) {
|
||||
rules.neighbors[i] = j;
|
||||
okay = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!okay) {
|
||||
optionError("Rules string malformed");
|
||||
}
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
void help() {
|
||||
printf("SYNOPSIS: %s [-y] [-c [-lbn] [-s TIME] [-t CHARSET]]\n"
|
||||
"%*s%s[-p LIMIT] [-r RULES] [-h HEIGHT -w WIDTH] FILE\n\n", getProgName(), strlen(getProgName()), "",
|
||||
#ifdef GIF_SUPPORT
|
||||
"[-g [-s TIME] [{-o COLOR}]] "
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
);
|
||||
printf("OPTIONS:\n");
|
||||
printf(" -y show help\n");
|
||||
printf(" -c use CLI output (default)\n");
|
||||
printf(" -g use GIF output\n");
|
||||
printf(" -p LIMIT the search limit for periodes (default: %i)\n", DEFAULT_PERIOD_THRESHOLD);
|
||||
char tmp[11];
|
||||
rules2string(tmp, DEFAULT_RULES);
|
||||
printf(" -r RULES set custom rules (default: %s)\n", tmp);
|
||||
printf(" -h HEIGHT override the height of the output\n");
|
||||
printf(" -w WIDTH override the width of the output\n");
|
||||
printf(" FILE the input file (- for stdin)\n");
|
||||
printf("\n");
|
||||
|
||||
printf("CLI OPTIONS:\n");
|
||||
printf(" -l loop the output even if periodes occure\n");
|
||||
printf(" -b draw border (defaul)\n");
|
||||
printf(" -n don't draw border\n");
|
||||
printf(" -s TIME sleep in ms (default: 200)\n");
|
||||
printf(" -t CHARSET charset for drawing (default: \" **\")\n");
|
||||
printf("\n");
|
||||
|
||||
#ifdef GIF_SUPPORT
|
||||
printf("GIF OPTIONS:\n");
|
||||
printf(" -s TIME sleep in 1/10 s (default: 20)\n");
|
||||
printf(" -o COLOR colors to use\n");
|
||||
printf(" COLOR := DEAD|DIED|NEW|ALIVE:HEX_COLOR\n");
|
||||
printf(" HEX_COLOR := #[a-f0-9]{6}\n");
|
||||
printf(" (defaults: DEAD/DIED = #000000; NEW/ALIVE = #ffffff)\n");
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
printf("INPUT FORMAT:\n");
|
||||
printf("[#WIDTH,HEIGHT;]{X,Y;}\n\n");
|
||||
printf(" WDITH the width of the output\n");
|
||||
printf(" HEIGHT the width of the output\n");
|
||||
printf(" X the x coordinate of a cell\n");
|
||||
printf(" Y the y coordinate of a cell\n\n");
|
||||
printf("Example:\n");
|
||||
printf("#3,3;1,0;1,2;1,3;\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int readCoordinates(FILE* file, int* x, int* y) {
|
||||
int c;
|
||||
char tmp[10];
|
||||
char* endptr;
|
||||
int position = 0;
|
||||
*x = -1;
|
||||
*y = -1;
|
||||
bool has = false;
|
||||
while((c = getc(file)) != EOF) {
|
||||
switch(c) {
|
||||
case ',':
|
||||
tmp[position] = '\0';
|
||||
position = 0;
|
||||
*x = strtol(tmp, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case ';':
|
||||
tmp[position] = '\0';
|
||||
position = 0;
|
||||
*y = strtol(tmp, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
return -1;
|
||||
}
|
||||
if ((*x < 0) || (*y < 0))
|
||||
return -1;
|
||||
return 0;
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\t':
|
||||
break;
|
||||
default:
|
||||
has = true;
|
||||
if (position > 9) {
|
||||
return -1;
|
||||
}
|
||||
tmp[position++] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void setSize(FILE* file, int* width, int* height) {
|
||||
int c = getc(file);
|
||||
if (c != '#') {
|
||||
ungetc(c, file);
|
||||
return;
|
||||
}
|
||||
|
||||
c = readCoordinates(file, width, height);
|
||||
|
||||
if (c != 0) {
|
||||
error(EXIT_USER_ERROR, "setSize", "malformed file format");
|
||||
}
|
||||
}
|
||||
|
||||
void setField(FILE* file, field_t field) {
|
||||
int r, x, y;
|
||||
|
||||
while((r = readCoordinates(file, &x, &y)) == 0) {
|
||||
if ((x < 0) || (y < 0) || (x >= field.width) || (y >= field.height)) {
|
||||
error(EXIT_USER_ERROR, "setField", "coordinates out of bounds");
|
||||
}
|
||||
field.cells[x][y] = ALIVE;
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
error(EXIT_USER_ERROR, "setField", "malformed input format");
|
||||
}
|
||||
};
|
||||
|
||||
void parseColor(gif_settings_t* settings, char* optarg) {
|
||||
char* color = NULL;
|
||||
int celltype = -1;
|
||||
for (int i = 0; i < strlen(optarg); i++) {
|
||||
if (optarg[i] == ':') {
|
||||
color = optarg + i + 1;
|
||||
optarg[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (color == NULL) {
|
||||
optionError("color format malformed");
|
||||
}
|
||||
|
||||
if (strcmp(optarg, "DEAD") == 0) {
|
||||
celltype = DEAD;
|
||||
} else if (strcmp(optarg, "DIED") == 0) {
|
||||
celltype = DIED;
|
||||
} else if (strcmp(optarg, "NEW") == 0) {
|
||||
celltype = NEW;
|
||||
} else if (strcmp(optarg, "ALIVE") == 0) {
|
||||
celltype = ALIVE;
|
||||
} else {
|
||||
optionError("unknown cell type");
|
||||
}
|
||||
|
||||
hex2color(&(settings->colors[celltype][0]), color);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
initMisc(argv[0]);
|
||||
|
||||
bool gif = false;
|
||||
bool loop = false;
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
|
||||
const char* filename;
|
||||
FILE* file;
|
||||
|
||||
long sleep = -1;
|
||||
|
||||
settings_t settings;
|
||||
settings.dimensions.width = -1;
|
||||
settings.dimensions.height = -1;
|
||||
settings.rules = DEFAULT_RULES;
|
||||
settings.periodThreshold = DEFAULT_PERIOD_THRESHOLD;
|
||||
|
||||
cli_settings_t cli_settings;
|
||||
cli_settings.border = true;
|
||||
cli_settings.charset = " **";
|
||||
|
||||
gif_settings_t gif_settings;
|
||||
const char* gif_filename = "-";
|
||||
gif_setColor(&gif_settings, "#000000", "#000000", "#ffffff", "#ffffff");
|
||||
|
||||
int opt;
|
||||
char* endptr;
|
||||
|
||||
while((opt = getopt(argc, argv, "ycgp:r:h:w:lbns:t:o:")) != -1) {
|
||||
switch(opt) {
|
||||
case 'y':
|
||||
help();
|
||||
break;
|
||||
case 'c':
|
||||
gif = false;
|
||||
break;
|
||||
case 'g':
|
||||
gif = true;
|
||||
break;
|
||||
case 'p':
|
||||
settings.periodThreshold = strtol(optarg, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
optionError("Argument has to be a number.");
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
settings.rules = string2rules(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
settings.dimensions.height = strtol(optarg, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
optionError("Argument has to be a number.");
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
settings.dimensions.width = strtol(optarg, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
optionError("Argument has to be a number.");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
loop = true;
|
||||
break;
|
||||
case 'b':
|
||||
cli_settings.border = true;
|
||||
break;
|
||||
case 'n':
|
||||
cli_settings.border = false;
|
||||
break;
|
||||
case 's':
|
||||
sleep = strtol(optarg, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
optionError("Argument has to be a number.");
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (strlen(optarg) != 4) {
|
||||
optionError("Argument needs to be length 4");
|
||||
}
|
||||
cli_settings.charset = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
parseColor(&gif_settings, optarg);
|
||||
break;
|
||||
default:
|
||||
optionError("unknown option");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
optionError("No file given.");
|
||||
}
|
||||
|
||||
filename = argv[optind];
|
||||
|
||||
if (strcmp("-", filename) == 0) {
|
||||
file = stdin;
|
||||
} else {
|
||||
file = fopen(filename, "r");
|
||||
if (file == NULL)
|
||||
error(EXIT_USER_ERROR, "main", NULL);
|
||||
}
|
||||
|
||||
setSize(file, &width, &height);
|
||||
|
||||
if ((width < 0) || (height < 0)) {
|
||||
optionError("Width or height missing");
|
||||
}
|
||||
|
||||
settings.dimensions.width = width;
|
||||
settings.dimensions.height = height;
|
||||
|
||||
initField(settings);
|
||||
|
||||
setField(file, getField());
|
||||
|
||||
if (gif) {
|
||||
#ifndef GIF_SUPPORT
|
||||
error(EXIT_COMPILE_TIME_ERROR, "main", "no GIF support");
|
||||
#else
|
||||
|
||||
if (sleep < 0)
|
||||
sleep = 20;
|
||||
|
||||
if (strcmp(gif_filename, "-") == 0) {
|
||||
freopen(NULL, "wb", stdout);
|
||||
|
||||
gif_settings.fh = fileno(stdout);
|
||||
} else {
|
||||
gif_settings.fh = open(gif_filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
|
||||
if (gif_settings.fh < 0) {
|
||||
error(EXIT_USER_ERROR, gif_filename, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
gif_settings.dimensions = settings.dimensions;
|
||||
gif_settings.sleep = sleep;
|
||||
|
||||
gif_init(gif_settings);
|
||||
|
||||
gif_output(getField());
|
||||
|
||||
int period;
|
||||
while(((period = nextGeneration()) < 0)) {
|
||||
gif_output(getField());
|
||||
}
|
||||
|
||||
gif_end();
|
||||
|
||||
#endif
|
||||
} else {
|
||||
if (sleep < 0)
|
||||
sleep = 200;
|
||||
|
||||
cli_init(cli_settings);
|
||||
|
||||
cli_output(getField());
|
||||
|
||||
int period;
|
||||
while(((period = nextGeneration()) < 0) || loop) {
|
||||
usleep(sleep * 1000);
|
||||
cli_output(getField());
|
||||
}
|
||||
|
||||
cli_output(getField());
|
||||
|
||||
cli_end();
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
28
src/misc.c
Normal file
28
src/misc.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
const char* progName;
|
||||
|
||||
void initMisc(const char* _progName) {
|
||||
progName = _progName;
|
||||
}
|
||||
|
||||
const char* getProgName() {
|
||||
return progName;
|
||||
}
|
||||
|
||||
void error(int exitcode, const char* module, const char* error) {
|
||||
(void) fprintf(stderr, "%s: %s", progName, module);
|
||||
if (error != NULL) {
|
||||
(void) fprintf(stderr, ": %s", error);
|
||||
}
|
||||
if (errno != 0) {
|
||||
(void) fprintf(stderr, ": %s", strerror(errno));
|
||||
}
|
||||
(void) fprintf(stderr, "\n");
|
||||
exit(exitcode);
|
||||
}
|
13
src/misc.h
Normal file
13
src/misc.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
|
||||
#define EXIT_PANIC 42
|
||||
#define EXIT_SUCCESS 0
|
||||
#define EXIT_USER_ERROR 1
|
||||
#define EXIT_COMPILE_TIME_ERROR 5
|
||||
|
||||
void initMisc(const char*);
|
||||
const char* getProgName();
|
||||
void error(int, const char*, const char*);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue